Wednesday, September 6, 2017

Paid Upgrades in the App Store

App developers are increasingly turning to subscriptions to build a sustainable business model. This is understandable; the economics of a one-time-purchase business model favor short-lived apps and games, but building a high-quality app requires ongoing revenue. However, many consumers are feeling "subscription fatigue". Each subscription may seem reasonable on its own, but subscriptions add up quickly, and nobody wants to be spending $100/month on app subscriptions.

For years, developers have been asking Apple to support paid upgrades in their app stores. That hasn't happened yet, but I want to make the argument that Apple doesn't really need to do anything; developers can already implement paid upgrades today in the App Store, with just a little bit of extra work. In fact, I'll argue that we can do it better now than what we'd get if Apple added an explicit "paid update" option that required users to pay before updating their app.

We generally all know what a paid update model looks like. Just to be clear, though, this is what I mean by paid updates:
  • Full access to all features when purchasing
  • No separate in-app purchases required upon initial purchase
  • Users can choose to upgrade to new versions on their own schedule
  • Users can choose to skip an upgrade
  • Developers can provide a discounted upgrade price to existing owners of the app
This mirrors the common software business model before the iPhone. If you bought Acme Office 2007, you had immediate access to everything in the app; no unlocking needed. When Acme Office 2010 came out, you could upgrade if you wanted and get all the new features, or you could skip it, and maybe buy the 2012 edition instead.

Paid upgrades aren't right for every app; in some cases a one-time purchase is the right solution, and in others a subscription may be make more sense. But having paid upgrades as an option helps everyone.

The Design

So how are we going to design this? Instead of having separate apps in the App Store, we're going to implement this all within a single app. However, various features of the app may be unavailable to some users.

When we release version 1.0 of our app, everyone who purchases the app will have full access to all of its features. We'll call that feature set A. They paid for those features, after all. Hooray!

Version 2.0 brings with it new features, feature set B. We're implementing paid upgrades here, so a non-consumable in-app purchase is available for our existing users to get access to the new set of features. However, new users who purchase the app at this point are buying the app with the 2.0 features included, so we immediately unlock all features, both A and B, for new users.

Version 3.0 adds additional features. Again, anyone who purchases the app for the first time will have all features available, A, B, and C. They bought the app as it stands right now, and those are the features they get. Users with earlier feature packs can choose to upgrade to the 3.0 feature pack via in-app purchase, which includes all features from all versions.

This is a critical point: Users do not have to purchase every update along the way. If a user bought version 1 and doesn't feel a need for the additional features in version 2, they don't have to upgrade. Later, when they update to version 3.0, they decide they want feature set C, so they buy the in-app upgrade. When they do, they get B as well, so they have A, B, and C. Users are not buying feature sets individually; they're simply choosing when to upgrade to the latest version. It is not possible for a user to have A and C, but not B. There's a linear progression to the features.

Providing a linear feature history makes things simpler for developers as well. Instead of testing 2n combinations of feature sets, there are just N configurations to test; one for each feature release.

Note that I've phrased everything in terms of additional features. Bug fixes would be released as part of the app and would generally be released to all users, whether or not they've upgraded to the current feature set. It would be silly for a developer to fix a bug but lock the fix behind an in-app purchase. So everyone gets bug fixes, and even your users that still just have feature set A still feel like they're getting support from the developer. This actually works better than requiring users to pay for an app update; developers can roll out bug fixes to users on old "feature versions" of the app without having to build separate hotfix builds for each release.

The key feature of this scheme is that users who purchase the app new immediately have access to all features. This enables users to charge a fair price up front.

How do we implement this?

The app store receipt included with every app purchase includes the original_application_version and original_purchase_date fields. These fields can be used to determine what the base feature set should be for a given user. If the original_application_version is 2.0, for example, then we know that all 2.0 features should be available for this user, but the 3.0+ features will require an in-app purchase.

The app store receipt also includes information about in-app purchases. Simply scan the receipt for the highest level of in-app purchase and you'll know what features to unlock. Everything we need is already available in the app store receipt!

We can adjust the pricing of the in-app purchase products independently of the app store price, which allows us to provide upgrade pricing to our existing users.

Will this work for my app?

This systems works well for any app that primarily adds value by adding new features. Productivity apps and professional apps are a particularly good fit. It's probably not a good fit for most games.

Any caveats I should know about?

Unfortunately, yes: Family Sharing throws things off a bit. If I purchase an app at version 1.0 and my wife downloads it for the first time (via Family Sharing) at version 2.0, what feature level should she expect? You could make a reasonable argument for both 1.0 and 2.0, and Apple hasn't documented which one we should expect. It doesn't matter, though, because the actually answer is "neither". In my testing, Family Sharing receipts had a purchase date corresponding to the most recent release (3.0) of the app, even if the family member downloaded the app years ago. I've filed Radar #33076385 regarding this issue, but I'm not aware of any workaround right now.

I know that's a heavy caveat, but it's one that Apple can (and should fix). If this kind of business model interests you, I'd encourage you to file your own bugs with Apple requesting a fix.

And if you do try this out, please let me know! You can find me @bjhomer on Twitter.

Wednesday, June 29, 2016

A U.S. analogy of the Brexit situation

Confused about the "Brexit" situation? Here's how it might go down if the United States were actually the United Kingdom.




The Tea Party hates being part of the European Union because they have to pay a bunch of money, the EU gets to make some laws that affect the US, and immigrants can come in from anywhere in the EU with very few restrictions. They've been complaining about it for years. Finally, Prime Minister Paul Ryan agrees to schedule a non-binding national vote on whether they should leave, figuring it will fail and the Tea Party will have to stop talking about it.

So Sarah Palin goes about campaigning on how terrible the EU is, and how all the money we send to the EU could go toward education instead! $350 million every week to education! Wouldn't that be awesome? And by the way, look at all these immigrants coming in and making it harder for you to get jobs. Isn't that terrible? "Take Back Control" is their slogan.

Paul Ryan recognizes that leaving would be a terrible blow to the economy, so he's encouraging everyone to Remain. The Republican party leadership runs campaigns like "Brits Don't Quit" and stuff, which are fine and all, but it's not as inspiring or scaremongery as "Take Back Control", so the Leave campaign is picking up quite a bit of steam.

This is a national vote, not just a Republican vote, so the Democrats need to be involved too. They're in favor of staying, generally… except that it turns out Nancy Pelosi (the leader of the Democrats in our little story) privately actually has never liked the EU all that much, and would kinda be okay with leaving. So she puts some token effort into supporting the Remain campaign, but doesn't put much effort into it.

Paul Ryan recognizes that they're going to need the Democrat vote on the Remain side if they're going to win. The general leadership of both parties is on the Remain side, so it seems like the one time that they might actually agree on something! But… no. Nancy Pelosi flat out refuses to be seen cooperating with Paul Ryan. Democrats are getting mixed messages. Polls are predicting a narrow win for Remain, but it's a close thing.

The week before the vote, a Democrat senator from Pennsylvania named Jo Cox is shot and killed outside a library where she was about to hold a town hall meeting. She was a supporter of the Remain movement. The shooter was known to be affiliated with far-right extremist groups, and Cox's "Remain" stance and her support for immigration is suspected as a motive for the shooting.

The day of the vote comes, and in a 52%-48% split, Leave wins. Soon, there are reports of Leave supporters harassing and assaulting foreigners across Britain, telling them to get out of the country where "they are not wanted".

Paul Ryan says that he won't be the one to lead the US out of the EU, and announces his upcoming resignation. In the meantime, he's in no hurry to formally notify the EU of any departure. Reporters ask him "what's the plan for exiting the EU?" and he says "Not my problem."

Sarah Palin didn't actually expect the Leave campaign to win; she was mostly looking to score political points. Now the rubber hits the road, and she has to admit that immigration is still going to happen and there's *definitely* not $350 million actually available to go toward education. Leave voters are *super* upset with her. The Dollar starts falling dramatically in value. Sarah Palin announces that after this long and tiring campaign, she's going to take a month off from public appearances, and generally doesn't want to admit that she has no idea how an exit from the EU would actually work.

Democrats are furious with Nancy Pelosi over her weak support of the Remain movement. They produce a 172-40 vote of no confidence in her leadership, and much of the other party leadership resigns too. Pelosi vows to stay in power since the no confidence vote is non-binding, but everyone assumes she'll be gone before long, and there's a lot of questions over who will replace her.

Texas voted heavily in favor of Remain. The governer of Texas says that if the US is going to leave the EU, maybe Texas will secede from the Union and join the EU on its own.

Rumors start spreading that many people were confused about what the vote actually meant (even though the ballot was pretty dang clear), and soon there are millions of signatures on a petition for a re-vote.


So at this point:

  • The leader of the Republicans (Paul Ryan) has stepped down.
  • His presumptive replacement (Sarah Palin) is in hiding.
  • The leader of the Democrats (Nancy Pelosi) is likely to be ousted.
  • Texas is threatening to secede.
  • Nobody wants to actually be the one to notify the EU of the US departure, as it's probably political suicide.
  • Nobody's sure whether the US is staying or leaving, but everyone's unhappy with the situation.


Got it?

Starring: 

Paul Ryan as David Cameron
Sarah Palin as Boris Johnson
Nancy Pelosi as Jeremy Corbyn
Tea Party as UKIP
"$350m to Education" as "£350m to the NHS"
Texas as Scotland



Thanks for reading. If I've messed something up, please let me know!

Tuesday, June 7, 2016

Swift Protocols constrained to a specific class

Update: As of Swift 4, this is supported natively. Just do this:

var controller: UIViewController & MyProtocol


That's it!





Objective-C developers learning Swift often wonder how to represent something like UIViewController<MyProtocol>. The simple answer is that there's currently no way to directly represent that construct in Swift as of Swift 2.2. However, it's possible to work around it fairly easily with just a tiny bit of extra code.

protocol KitchenHelper: class {
    // The normal protocol stuff here
    func bakeCookies()
    func eatCake()

    // The new stuff
    var asViewController: UIViewController { get }
}

We've added an additional constraint to the protocol; any object that implements KitchenHelper must be able to return a UIViewController as well. Presumably, any conforming view controller would want to simply return self, so lets make that easier:

extension KitchenHelper where Self: UIViewController {
    // Let's provide a default implementation for view controllers
    var asViewController: UIViewController { return self }
}

Now any view controller class that conforms to KitchenHelper will automatically implement asViewController as well!

class MyViewController: UIViewController, KitchenHelper {
    func bakeCookies() {
        // sugar, flour, eggs...
    }

    func eatCake() {
        // yum
    }

    // 'asViewController' was automatically implemented!
}

func test() {
    let helper: KitchenHelper = MyViewController()
    let frame = helper.asViewController.view.frame
    print("helper's view's frame:", frame)
}

Sunday, June 14, 2015

Thoughts on WWDC 2015

In my mind, these are biggest developer-facing announcements to come out of WWDC 2015:
  • iPad Multitasking
  • UI Testing and Code Coverage in Xcode 7
  • Swift 2
  • Swift as an Open-Source language
  • App Thinning
  • Watch OS 2.0 with on-device apps
  • CloudKit Web Services
  • Free iOS provisioning
Note that these aren't changes in the SDK itself. Developers don't have to write a lot of code to get these benefits. iPad multitasking already works if you followed the adaptivity guidance for iOS 8 from last year; support is as simple as recompiling. UI Testing works on your existing apps. On-watch WatchKit extensions use the same SDK that on-phone extensions used (albeit with additional capabilities exposed). You don't need to make any changes to your CloudKit usage in order to enable web services. All these features come essentially free for the developer.

There are, of course, some nice SDK changes as well. UIStackView and NSCollectionView are very welcome additions to their respective platforms, but even these aren't really new; they're just parity features between the platforms. The various new extension points are nice, and the new gaming frameworks are probably a big deal for game developers. But as I see it, WWDC doesn't fundamentally change the kinds of apps I can write.

The focus this year isn't on enabling new kinds of apps, but rather on improving the experience of the apps we can already build. App testing, delivery, performance, and consumption are all improved across the board, with little effort needed from developers. iOS 9 and OS X El Capitan offer significant improvements to the user experience, and Xcode 7 improves the developer experience, but the improvements are largely outside the SDK. The big news from WWDC 2015 isn't about new frameworks, classes, and methods—it's about an improved experience for users and developers.



Caveat: If you're a game developer, GameplayKit, ReplayKit, and the expanded Metal announcements may have made this a big year for you. I'm not a game developer, so they don't really affect me from a development standpoint. But I acknowledge that they definitely look cool, and are definitely part of the SDK.

Saturday, May 2, 2015

Defining Modules for Static Libraries

Preview:

I figured out how to define modules for static libraries, so you can import them into Swift or into other module-defining frameworks. This solves a common build error: "include of non-modular header inside framework module", as it allows you to turn that non-modular header into a modular one.

You just have to define a custom module.modulemap file and copy it into the right place. Read more for details, or skip to the end if you just want to know what to do.




A brief and incomplete rationale for modules

In 2013, Xcode 5 was released with support for modules, a new way of using libraries and frameworks. Xcode 5 only supported modules in system libraries, but Xcode 6 extended support to third-party libraries as well. Before the advent of modules, developers gained access to libraries via #import statements, which literally copy the requested header files into the importing file. This made the library APIs visible within that file, but required repeatedly parsing system headers again and again as multiple files imported the same headers. This is an unnecessary duplication of work.

We could greatly improve build times if the compiler could parse all the headers of a library just once. However, this is difficult in a #import-based system, because the compiler does not know what files constitute the canonical headers for any given library. Some libraries provide an "umbrella" header that imports all of its API, but the compiler can't always know what header that would be, and even these aren't always complete. (Example: UIKit.h does not import UIGestureRecognizerSubclass.h.) We needed a better way of defining the canonical interface for a given library.

Modules are that better way. A module is a binary representation of a library's interface—once built, the module can be reused throughout the entire compilation process. It can also be used to provide access to the library from other languages, such as Swift. In fact, Swift code can only make use of frameworks and libraries that have a module defined, so this becomes a critical piece of infrastructure going forward.

Module maps

So how do we define a module from a bunch of headers? How do we define the canonical API for a library? Module maps. A module map is a simple file that defines the canonical headers for a library. They're easiest to understand by example, so let's jump right in.

The following is a valid module map for an imaginary library. It is place in a file named module.modulemap in the same directory as the library headers.

module AirplaneKit {
   header "Airplane.h"
   header "AirplaneView.h"
   header "Cockpit.h"
   header "Fuselage.h"
   header "Wheels.h"
   header "Wings.h"
}

Simple enough; the module map is just a list of headers that define the representation of the library. It can get a bit tedious to write out every header, though, and it's easy to forget to update the modulemap when you add a new header, so module maps support a convenient syntax:

module AirplaneKit {
   umbrella header "AirplaneKit.h"
   export *
}

That export * line means "Also consider any file imported by one of the listed headers to be part of the module." This is generally what you'll want to do. (The umbrella part is not strictly necessary, but indicates that the header is expected to import every header file in its directory.)

In general, Xcode has fairly good support for defining modules based on your own frameworks: just set the Defines Module build setting to YES and maintain your framework's umbrella header. Xcode will build the appropriate module map file and embed it within your framework. Support for static libraries, though, is somewhat lacking.

Before we move on, one important note: In Xcode 6.3.1, you must clean your project after any change to a module.modulemap file. In my experience, the compiled version of the module is not rebuilt when the module map file changes. Also, when tweaking the module map file, make sure you're editing your original file, not the one in the build folder. Xcode sometimes likes to jump to that one instead.

Defining modules for static libraries

If you're using a static library and you want to make it available to Swift or to another module, there are 2 things you need to do to "modularize" it.

1. Add a file named exactly module.modulemap as described above.

2a. If you're using someone else's library you didn't write, you probably already put the headers somewhere. Just write a module map file for the library and put it with the headers.

2b. If you're building your own static library, make sure your library's headers and the module map are being copied to [build directory]/include/[LibraryName]/. If your static library target is using a Copy Headers build step (as is the default in Xcode 6.3.1), remove it. Instead add a Copy Files build phase, configured as follows:



That's it. Really. After doing the above, you can now use your library from Swift. You can read more about modules and the modulemap format in the Clang Documentation.

If you have any problems, please let me know on Twitter (@bjhomer). I'd like to make sure these steps work for everyone.

Saturday, October 18, 2014

Mutable collections in Swift

I recently came across this question on Twitter:


This is an interesting question. As a developer coming from Objective-C and the Cocoa frameworks, we're used to distinguishing between NSArray and NSMutableArray. It's generally bad practice to expose an NSMutableArray as a public property on an object.


The problem is that another part of the app could modify the contents of the pizzaMenu array without the knowledge of the Pizzeria object. The Pizzeria still points to the same object it always has; from its perspective, nothing has changed. This is bad.


Instead, a better practice is to only expose an immutable NSArray. This way, whenever any change happens, the setter method will be called. The Pizzeria is always aware of what's happening.

In Swift, the Array type is built into the language. Though you can still use NSArray and NSMutableArray, it's often not necessary; the native Array type is plenty capable.

There are two types of objects in Swift: value types and reference types. Reference types are like traditional Objective-C objects—multiple variables can reference the same object. This was the problem in the examples above above: both the Pizzeria object and external code referenced the same object, so we had to expose an NSArray that does not have APIs for changing its contents.

Value types are identified by their value. They also exist in Objective-C, but only for C types (structs and primitives). If two CGRect variables have the same value, it simply means that the two rects contain the same data. A change to one rect will never change the value of another rect stored in a separate variable.

While Objective-C requires that all objects be reference types, Swift objects can be reference or value types. This means that Swift structs can have methods just like classes can. Swift's built-in collections (Array and Dictionary) are value types.

Because they are value types, two variables can never reference the "same" array. Every time an array is assigned to a variable, it gets a new, separate copy1 which cannot be affected by any other part of the code.

This means there is no need for a separate mutable collection type in Swift. Mutating a value type is always safe because changing a struct only affects the values of that specific variable. That what it means to be a value type. Modifying a Swift Array never affects any other variable, anywhere.

Let's see how this affects our pizzeria example.

Note that even though pizzeria.pizzaMenu is a mutable var property, modifying the array we get back from the pizzeria has no effect on the pizzeria's copy of the data. They are separate arrays, separate values, and we can't change their copy unless we go through the setter.

In fact, this holds true even if we avoid assigning the array to an intermediate variable! In Objective-C, you simply cannot modify a value type held by another object. Many iOS developers have discovered this, to their frustration:

But in Swift, not only does this work, but it guarantees that the setter will be called! Whenever you call a mutating2 method on a value type, the end result is assigned back into the receiving variable (or expression). So calling obj.someInts.append(1) causes a new value to be constructed, which is then assigned back into the someInts property!


Because Swift arrays and dictionaries can never be shared, there is no distinction between mutating an existing collection and re-assigning a new collection. The behavior of the code is exactly the same. In either case, the owner's setter method is called whenever the array is modified.

So to answer the original question, there is no syntax to specify a variable that holds an immutable array because there is nothing that such syntax would add. Swift addresses the issues that made NSArray and NSMutableArray necessary in the first place. If you need a shared array, you can still use the Cocoa types. In every other case, Swift's solution is safer, simpler, and more concise.



1: Note that the implementation is lazy, only performing an actual copy when really necessary.
2: Methods on structs and enums in Swift must be labeled mutating if they're going to modify any data. Otherwise, they cannot be called on a value stored in a let variable.

Friday, August 8, 2014

NSScrollView and Autolayout

So, you've got an NSScrollView, and you want to add some content that is sized via auto layout constraints. Perhaps you found the "Auto Layout by Example" documentation in the Mac developer library, and discovered that though it contains a section titled Using Scroll Views with Auto Layout, the example is actually for UIScrollView, not NSScrollView. Given the significant differences between the two, that simply won't work.

Here's what you need to do. I'll demonstrate doing this in code, but the same principles apply in Interface Builder as well.



1. Create an NSScrollView. Position it however you like.

    _scrollView = [[NSScrollView alloc] initWithFrame:self.bounds];
    _scrollView.translatesAutoresizingMaskIntoConstraints = YES;
    _scrollView.autoresizingMask = NSViewHeightSizable | NSViewWidthSizable;
    _scrollView.hasVerticalScroller = YES;
    _scrollView.identifier = @"ScrollView";

    [self addSubview:_scrollView];


2. Create an NSView subclass with isFlipped == YES that will contain all your scrolling content.  (If it's not flipped, then the content will prefer to pin to the bottom of the scroll view if not tall enough to require scrolling. That's not what you want.)

    @interface FlippedView : NSView
    @end

    @implementation FlippedView

    - (BOOL)isFlipped {
        return YES;
    }

    @end

3. Create the scrolling container view. Do not use autoresizing masks. Its size doesn't matter too much, but its superview will be the scroll view's contentView, so that makes a convenient default size.

    _scrollContentContainer = [[FlippedView alloc]
                                initWithFrame:_scrollView.contentView.bounds];
    _scrollContentContainer.translatesAutoresizingMaskIntoConstraints = NO;
    _scrollContentContainer.identifier = @"Content container";

4. Set the content container to be the scroll view's document view.

    _scrollView.documentView = _scrollContentContainer;

5. Constrain the top, left, and right edges of the container view to its superview (the contentView). Use Auto Layout constraints to do this, not autoresizing masks.

    NSDictionary *views = NSDictionaryOfVariableBindings(_scrollContentContainer);
    NSArray *hConstraints = [NSLayoutConstraint
                             constraintsWithVisualFormat:@"H:|[_scrollContentContainer]|"
                             options:0 metrics:nil views:views];
    
    NSArray *vConstraints = [NSLayoutConstraint
                             constraintsWithVisualFormat:@"V:|[_scrollContentContainer]"
                             options:0 metrics:nil views:views];
    [_scrollView.contentView addConstraints:hConstraints];
    [_scrollView.contentView addConstraints:vConstraints];
    
6. Add your subviews to the content container however you like. Make sure that the constraints from these subviews will create a natural content size in at least the vertical dimension.

    NSDictionary *buttons = NSDictionaryOfVariableBindings(button, button2);
    NSArray *hConstraints2 = [NSLayoutConstraint
                              constraintsWithVisualFormat:@"V:|-[button]-(1300)-[button2]-|"
                              options: (NSLayoutFormatAlignAllLeft |
                                        NSLayoutFormatAlignAllRight)
                              metrics:nil views:buttons];
    
    NSLayoutConstraint *centerX = [NSLayoutConstraint
                                   constraintWithItem: button
                                   attribute: NSLayoutAttributeCenterX
                                   relatedBy: NSLayoutRelationEqual
                                   toItem:    _scrollContentContainer
                                   attribute: NSLayoutAttributeCenterX
                                   multiplier:1 constant:0];
    
    [_scrollContentContainer addConstraints:hConstraints2];
    [_scrollContentContainer addConstraint:centerX];


Of course, you can do this all in Interface Builder as well, and that's usually what I'd do. It's nice to know how to do it both ways, though. The same principles apply in either case:

  1. Position the NSScrollView externally however you like.
  2. Flipped container view as the scroll view's document view.
  3. Constraints from top, right, and left of container view to its superview.
  4. Constraints from the scrollable content to the container view.

And that's it!