Overview of In-App Purchase

A high-level look at IAP.  Could be wrong.  Oriented to a trial-use or freemium app.

Disambiguating

A user can acquire your app without using IAP.  That is, your app can be in the store without using IAP.

“Acquire” does not necessarily mean “pay for”.  If your app is free, a user can get it without payment, by browsing the App Store.  The free portion of your app is a product with a receipt, but it is not a product that you show in your app’s store facade, since the user already has it.

Trials

Apparently, Apple does not want “trial versions” of apps in the store.  But this just means: they don’t want duplicates (where one is crippled and the duplicate not) and  instead prefer one version with IAP.  The version in the store can still be a trial version in this sense: the app can require an IAP purchase to continue certain capabilities beyond a trial period.  Probably Apple also doesn’t want an app that won’t do anything after a trial period.  In other words, an app should continue to do something minimally useful after a trial period.

Probably they don’t even want you to advertise that an app is a trial-version: the user will see the IAP icon in the store, and know that the app may be limited (from the get-go, or after a trial period.)

Probably most users expect that certain apps will be trials or crippled.  Don’t worry about offending potential customers.

The Data Model

An app has one or more products.

A product has one or more receipts.  Each receipt is for a transaction.  (StoreKit lets you play back the transactions.)

A product has one or more content/capabilities.

Generally content/capabilities are one-to-one with receipts.

I am glossing here: it is probably only subscription type products that have many receipts for the same product.  That is, a subscription product entitles the purchaser to many contents, and each content has its own receipt.

The Facade Pattern

Your app presents a facade to the store.  In other words, your app has GUI for IAP.  Behind the scenes, your app collaborates with the store server using StoreKit.  Your app is a client of the store server.

You must submit a screen capture of your store facade during the app review process.

Validating a productID is the process for insuring that the products your facade presents to the user are indeed products in the store server (configured using iTunesConnect.)

Receipt Subclasses

Subclasses:

  • bona fide Apple receipts, stored in the app bundle and maintained by the OS/framework
  • ad hoc receipts, stored e.g. in NSUserDefaults and maintained by your app

The difference is in how much code you need, and strength of receipt against hacking.

Restoration

A purchase is restorable when a purchaser:

  • deletes your app from a device
  • buys a new device
  • enables Family Sharing for other devices

The store maintains records of purchases.   Your app must collaborate.

If you use ad-hoc receipts, when a user deletes your app, the settings for the app are deleted.  When a user re-downloads your app,  since you are not using Apple receipts, you won’t know that the user has downloaded it before (in another transaction.)  Thats a weakness of ad-hoc receipts.  Your app won’t have any record that it was downloaded previously.  Presumably, most users will purchase rather than repetitively download your free app (just to reset the trial period.)

Creating products

You need to create a product on both sides:

  • in the real store using iTunesConnect
  • in your store facade (hard coding productIDs or putting them in a .plist file)

Testing

There is a sandbox for testing IAP.  (If it works in the sandbox, it should work in the real world.  The only way to test in the real world is to actually purchase; be the first one if you are worried about it.)

Test Cases

You should test:

  1. Normal purchase: success on the main path.
    1. User declines: in your store facade, answers “No thanks.”
    2. User declines: Settings>Restrictions>IAP is On (Restrictions enabled, IAP restriction On)
    3. User declines : Cancels StoreKit’s log in dialog to the store.
    4. User declines : Cancels StoreKit’s confirmation dialog for purchase
  2. Product invalid: for some reason, what your app thinks is a product is not in the store.  (Your app configuration is wrong, or the store has removed your product.)
  3. Restoration: user installs your app on another device, or deletes and reinstalls your app.
  4. Store facade can be paused and resumed with the Home button
  5. Store facade handles user bottom double swipe up the Control Center

For a trial-use app:

  1. In the trial period: all capabilities
  2. After the trial period: capabilities are denied
  3. After the trial period: you dun the user when they try to use a capability (you present your store facade.)

Counting touches in touchesBegan() and gestures

This discusses a surprise!  something I learned the hard way.

Many touch events may come via touchesBegan() for multi-finger touches .  For example, for a three finger touch, you may get one event having one touch followed a fraction of a second later by another event having two touches, where each touch is for a different finger (having a different location.)

In other words, you can NOT rely on getting one touch event having all the touches.    You shouldn’t check Set<UITouch>.count() to determine the number of fingers the user is using.

That is the job of a gesture recognizer.  A gesture recognizer typically establishes a short window in time, and gathers all the distinctly located touches begun in that window, to determine whether the count of touches meets the requirements of the gesture.  (I presume a gesture recognizer also filters out touches that later drop out of the gesture.)

Every gesture (the superclass  UIGestureRecognizer) has the method numberOfTouches().

Many gestures can be configured with a minimum count of touches.  This is only a minimum.  A user can use more fingers and the gesture still be recognized.

  • Pan: minimumNumberOfTouches()
  • LongPress, Tap: numberOfTouchesRequired()
  • etc.

Some of the gestures also allow a maximum to be defined.

In summary: if your app supports only one finger, you might be able to use touchesBegan() and touchesEnded() to crudely determine what the user is doing.  Otherwise, you should rely on gesture recognizers to count the number of touches.

Note that certain combinations of gestures (e.g. Tap and double Tap, with a dependency defined between them) might incur a lag for gesture recognition.   In other words, using touchesBegan, you can soon determine whether a user is acting, but using gestures you might later determine a user is acting.  It can get complicated.  For example, is the  lag between start of the real gesture and when a recognizer is in the Begin state or is the lag until the the single tap recognizer is in the Finished state (which comes no sooner than the double Tap recognizer being in the Canceled state) ?

Getting started with custom Swift frameworks

This is a broad overview.  I’m using Xcode 6.3.1.

tl;dr

Alternatives to creating a framework to be referenced in another project:

  1. add a framework target to a referencing project
  2. add a framework product to a referencing project, from a separate framework project
  3. add a framework project to a referencing project, from a separate framework project

Alternative 1 works best: you can archive the referencing project.  But the source files for the framework are all visible in the referencing project, even if they are also visible in a separate project for the framework, and regardless where the source files live.

Alternative 2:  this doesn’t seem to work.  You can’t archive the referencing project, and every time you build it for a different platform, you need to refresh the framework to one built for that platform.

Alternative 3: didn’t seem to work at all.

Disambiguating

‘Framework’ is Apple’s word for library.

‘Custom’ means: not provided by Apple.

A Swift framework is a ‘module’ that you ‘import.’

A ‘pure Swift framework’ is written entirely in Swift, without any embedded Objective-C or other languages.

A library has an API of methods you can call.

Why frameworks?

Frameworks let you reuse.

Two use cases:

– someone else has written and shared a framework

– you want to move your code into frameworks that you can share between projects

Basics of building a framework

Xcode has a template for framework projects.  See Build your own Cocoa Touch Frameworks, in pure Swift

Code the project with the ‘public’ access modifier on all methods you want exposed in the framework’s API.  Swift docs about Access Control.

Build the project for ‘iOS Device’.  (If you build it for the simulator, the Product>framework will be red in the Project Navigator.)

Building a downloaded framework

Download the source and hope the Xcode project is configured properly to build a framework product.

Start Xcode and choose Window> Welcome to Xcode.  Expect a window to open.  Choose “Open another project..”.  Expect a Finder window to open. Navigate to the .xcodeproj file inside the source you downloaded.  Choose OK.  Expect the project to open in Xcode.  Build as above (for iOS Device.)  Expect the build to succeed and for Products>.framework to turn black in the Project Navigator pane.

Using the framework in another project

Start another project (say an iOS app or game.)

Use the Finder to reference your project to your framework:

  • open the framework project and right click on the framework product.  Expect a pop-up menu.
  • choose “Show in Finder”.  Expect a Finder window to open, with your framework selected.
  • leave the Finder window open
  • in Xcode, close the framework project and open the referencing project
  • drag the selected framework from the Finder window into the Project Navigator pane of the referencing project.  Expect a dialog to open giving choices of copying, creating groups, etc.
  • choose OK (or change the options, I don’t understand them.)  Expect for the framework to appear in the Project Navigator pane (e.g. a lunchpail icon followed by SwiftState.framework.)

Now in your code you can insert ‘import “.  Expect Xcode to compile the import without errors.

Then you can insert code in your referencing project to call the API methods of the framework.

At this point, XCode complained that it could not find constructs from the framework.  ( Using the SwiftState framework from github, it could not find the StateEvent protocol defined by the framework. ) I changed the active scheme (in the upper left) from the “iPhone 6” simulator to “iOS Device” to solve that problem.  Apparently, the framework is built for a particular architecture (ARM architecture of an iPhone) and building for the simulator builds for the i86 architecture of the simulator?  I am not sure about this.

(This is far as I’ve gotten.)

Compiled libraries

Traditionally, you can obtain compiled libraries:

  • without source code
  • compiled for a certain architecture
  • with a header (.h or other) file defining the API

I am not sure you can do that for Swift frameworks.  So this blog might be limited to the case where you have the source (and often an Xcode project file .xcodeproj).

Dependencies

Libraries and frameworks can import, and thereby depend on, other libraries.

With libraries, you can get into ‘dependency hell’, where you don’t have the dependent libraries, or the graph of dependencies has version conflicts, etc.  If you are building all your custom frameworks yourself, this might not be a problem, unless you find old frameworks using old Swift constructs.

CocoaPods might help in this regard.

FAQ / Gotchas / Advice

A recounting of problems I encountered:

Background

My project was getting too large.  I wanted to move some classes into a framework to reduce coupling, and so I could share the framework.  The framework was code that I wished someone else had shared.  Code that was general, not the essence of my application. In my case a scoreboard for a Swift SpriteKit game.

My first advice is: in the original project, refactor the code to reduce coupling.  If you do that later, the fact that the code is now in two projects complicates the process (see below, Changing a Framework.)  In my case, I had several classes, like Scoreboard, HighScore, Players, etc. I reduced the coupling by making the Scoreboard own instances of the other classes, and making the Scoreboard delegate certain methods to its owned objects.

In other words, design a clean API for the framework.  Then implement that in the original project.  Only then move the code into a framework.

Changing a Framework

When I discovered problems in the API, and tried to make changes to the framework project, the changes were not reflected in the referencing project.  For example after adding a foo method to the API, I got “Scoreboard has no foo method.”

To verify it is a problem, I selected “ScoreBoard” in an “import Scoreboard” code, right clicked, and chose “Jump to Definition.” Then Xcode showed my a “Scoreboard.h” file, which defines the API. This file apparently is generated by the Swift compiler and is nowhere visible in the file system.  That never changed even after I rebuilt the framework project.   Even Cleaning the Build Directory and deleting the ModuleCache on both projects did not seem to help.  Also, the “file modified” date on …referencing project path…/Scoreboard.framework/Modules/ScoreBoard.swiftmodule/arm.swiftmodule  never changed.  Not sure that is related, but it seems likely.

I was unable to get it to refresh.  I ended up having to delete and recreate the framework project (not the source files, just the project.)

This problem seems to be restricted to changes to the API (to the .h file.)  When I made code changes to the framework that did not change the API, the changes did seem to propagate to the referencing project.

Targets in the Framework

In my framework project, I only have one target and I build for iOSDevice.  (If you ship for the OS X platform, you need another target?)

But you can build for different platforms (iOS Device or iOS Simulator.)  If you select the Product>foo.framework, and choose “Show in Finder” you will see that the framework is built for two platforms, where the parent directory is either:

  • Debug-iphoneos
  • Debug-iphonesimulator

Alternatives:

  1. One blog suggests building the framework as a Universal Binary.
  2. instead of adding the framework to your project, add the framework’s project to our project
  3. manually delete and add the framework built for a specific platform to your referencing project when you change platforms

Alternative 1:  I looked at it but it seemed rather more involved and low level than I wanted.

Alternative 2: I haven’t tried this but I suspect it is the easiest way to go.

Alternative 3:…

When you delete the framework from your referencing project (delete the reference) you must then:

  • add the framework back, but choosing from the parent directory for the platform (e.g. Debug-iphonesimulator)
  • add the framework again to General>Embedded Binaries
  • insure that the value (a list of paths) for Build Settings>SearchPaths>Framework Search Paths does not still include a path to the directory of the previous platform (delete that path if it does)

I imagine this alternative will get tedious if you switch back and forth between the iOS simulator and real devices often.

Minimum iOS Version

When I created the framework project, it was configured for a minimum iOS version 8.3.  My referencing project was configured for 8.1, and in my first build, Xcode complained.

So in the framework under the project target ScoreBoard>Build Settings>Deployment>iOS Deployment Target, I chose iOS 8.1.

Embedding a Binary

After I built and ran for a real iOS device, I got “dyld: Library not loaded: @rpath/Score.framework/Score.”  To fix that, in the referencing project, under the target, under General>Embedded Binaries”, I clicked the + button.  Expect a chooser dialog showing the contents of your project, which should include system frameworks and your custom framework.  Choose your custom framework, e.g. Scoreboard.framework.

When I did this, the framework also (automatically) appeared under “Linked Frameworks and Libraries” (but I had already done that?) so I deleted the duplicate.

Also when I did this, the framework automatically appeared under “Build Phases>Embed Frameworks”

The explanation is: system frameworks will be found on the device (not in your bundle).  Custom frameworks must be embedded.  All frameworks are linked in, but since they are dynamically linked, the final step of linking is done later (when the app is installed or run?)  Custom frameworks must be embedded in the bundle.

Archiving

When I added a framework product to a referencing project, then tried to archive a project target in the referencing project, it failed (the framework wasn’t visible.)  In other words, it only works for building in debug mode.

So I resorted to adding a target for the framework in the referencing project (instead of adding the framework product.)  When you add a target that is a “Cocoa Touch Framework”, one of the options is “Embed in Application”.  I chose to embed it in the main application target of the project.  Expect:

  • a folder e.g. “Score” to appear in the Project Navigator pane
  • the “Score.framework” to appear in the “General>Embedded Binaries” of the main app target.

Then in the “Score” folder, I added all the source files from their location in my “Score” framework project.

So I am left with a separate “Score” framework project, where I do source code control, and which I can share.  But the source is also added to the referencing project (in a separate “Source” group/folder).  Which seems pointless: the source code was originally in the referencing project, and the whole point of the exercise was to move it out.