What is Passbook? Passbook is an application provided with iOS 6, which collects and displays passes on the iPhone. Passbook represents a storage mechanism for app developers to maintain information in a system-level application that is readily accessible. Passes are a digital representation of the tokens of information in your wallet including boarding passes, membership cards, coupons, event tickets, and etc. Besides displaying tokens of information, Passbook also displays passes on screen automatically based on location and time relevancy, and also allows dynamic updates to the passes in the library.

Using the fictitious CapTech Ice Store as an example, when you walk into the store your phone will display the icon for the store membership card on your lock screen. Once a purchase is made, the pass server will then send a push notification to your phone to update the rewards balance automatically in the pass.

As of now, Passbook is not a digital wallet with mobile payment abilities, but mobile payments is certainly within strategic striking distance.

Passes

Currently, there are 5 styles of passes available:

  1. boarding pass
  2. coupon
  3. event ticket
  4. store card (rewards card)
  5. generic pass

As a developer, you have the ability to change barcode type (2d barcode or QR code), displayed text, background image and colors, and icons using a JSON file template. However the passes generated must follow the design guidelines issued by Apple. Feel free to download the sample passes from the Apple Developer portal to gain a deeper understanding of each style.

In the Passes directory, you can see there are several .pkpass files including their raw contents in the pass.raw folder. You can email the signed pkpasses to yourself and open them up on your iphone with iOS 6.0. As of early Sept 2012, Gmail does not recognize the pkpass mimetype, thus you should avoid using Gmail if at all possible.

Also take note that several programs interfere with the pkpass mimetype. Examples of applications that prevent correct loading of pkpasses include logmein, airsharing, box, receipts, good reader, so they may need to be deleted and reinstalled on the phone after a pass has been installed.

This tutorial will focus on introducing passes and how to create them and interact with them locally on the device. A second Passbook tutorial, which will be posted in a couple days, will describe the ecosystem of Passbook, and show you how to generate, deliver, and update passes remotely.

What is inside a pass?

Pass files are packaged as a zipped bundle of files with the .pkpass file extension. Passes do not contain any executable code, but are created from a folder containing the following files:

  1. pass.json: a JSON file that defines the pass.
  2. Pass images:
  3. manifest.json: a file containing a hash of every file in the package.
  4. The signature file: a PKCS#7 signature of the manifest.json file

How to create a Pass

Using the example of the fictitious CapTech Ice Cream Store, you will generate a membership rewards card for customers to collect points towards a free dessert. Normally pass generation is automatically done on a server with a web service of your design, but this post demonstrates generating passes locally on your machine. As mentioned earlier, a second post on integrating PassKit into your web service infrastructure will be posted in the next few days.

Store Card

You can also download the raw Pass materials used to generate the rewards card at the end of this tutorial.

Generate a Pass Certificate

The steps to generate a pass on the developer website are similar to the steps to generate a developer or distribution certificate.

  1. First thing is to sign in to your developer account, click on the provisioning portal, and click on Pass Type IDs on the left navigation bar.

    Create Pass Type ID
  2. Click on New Pass Type ID to generate a new pass id and certificate. Select a Pass Type ID and common name description.

    Create Pass Type ID - 2
  3. You may need to generate a Certificate Signing Request (CSR) using Keychain Access. This process is similar to the certificate request for the developer and distribution certificates. Please follow the instructions for this process to generate a CSR.

    CSR
  4. After the certificate is generated, you can download and install the certificate and confirm that it shows up in Keychain Access.

    Download Pass Certificate

    Private Key

What is inside the pass.json file?

Let's take a look at the pass.json file. Here are the top-level keys of an example rewards card to the CapTech Ice Cream Store.

{
 "formatVersion" : 1,
 "passTypeIdentifier" : "pass.captech.ventures.blog",
 "serialNumber" : "xxxxxxx",
 "teamIdentifier" : "xxxxxxx",
 "storeCard" : {…}
...
}

The formatVersion is required to be 1 for the current version of Passbook. The passTypeIdentifier, team identifier, and organization name match what is created in the Developer Portal. The passTypeIdentifier defines a class or category of passes. This value must correspond with your signing certificate to generate a correctly signed pass. The serial number is a unique string that identifies this pass. You will need to automatically generate this in your web service.

Since we cannot provide our certificate for you to sign the pass, the passTypeIdentifier and teamIdentifier need be replaced with the ones generated in the developer portal for your account. This allows you to use your certificates to sign this pass.

After specifying entitlement and identifier fields, you need to specify the type of pass style. For this example, you will use the storeCard style to generate the membership card. The remaining dictionary keys are optional and specify information to be displayed on the pass header, front, and back. For this pass example, you will populate the barcode, foreground and background colors, and other optional text fields shown by the listing below.

"barcode" : {
 "message" : "123456789",
 "format" : "PKBarcodeFormatPDF417",
 "mesageEncoding" : "iso-8859-1"
 },
"foregroundColor" : "rgb(255, 255, 255)",
 "backgroundColor" : "rgb(78, 74, 50)",
"storeCard" : {
 "primaryFields" : [
 {
 "key" : "rewards",
 "label" : "total rewards",
 "value" : 25,
 }
 ],
 "auxiliaryFields" : [
 {
 "key" : "level",
 "label" : "LEVEL",
 "value" : "Gold"
 },
 ],
"backFields" : [
 {
 "key" : "terms",
 "label" : "TERMS AND CONDITIONS",
…
]

There are many more fields in the pass.json file that can be used such as locations and relevantDate, which are optional fields used to set relevancy of the pass. A complete list of possible fields is available in the PassKit documentation on the Apple Developer website, which is currently available at http://developer.apple.com/passbook/.

Sign and package a .pkpass file

You will need to sign and package the pass directory into a .pkpass file. Usually this is done programmatically on a server in response to a request for a pass. However, to illustrate the steps needed to sign and package a.pkpass file, you can use a local application that Apple has provided. Part 2 of this tutorial will show you how to generate and update a pass remotely via a webservice.

From the Passbook Session Materials provided by Apple, locate the signpass project. This tool turns .raw directories into .pkpass files. Note that signpass requires a certificate matching the passTypeIdentifier of the pass to be present in the Keychain. Please see steps earlier in this tutorial on getting a certificate.

After building this tool, invoke it like so:

signpass -p /path/to/MyPass.raw

The steps that the application will do for you are:

  1. Validate that the passTypeIdentifier matches pass signing identity in keychain.
  2. Generate the manifest file.
  3. Generate the signature file using the pass certificate.
  4. Compress folder and rename to .pkpass extension.

This results in a .pkpass file that you can deliver to your device. Unfortunately, as of this writing, there is no quick way to view a generated pass. That means you will have to install the passes to preview and test them. There is also no easy way to determine how to fix your .pkpass file if it doesn't work except to load it on the iPhone and troubleshoot via the console. Apple includes passbook in the iPhone Simulator, but you will need to develop an application to load .pkpass files via the PKPass Framework.

How do you deliver a pass?

Now that you know how to generate a pass, what can you do with them? There are three delivery methods: download via email, download via website, and display inside a companion application.

To deliver a pass via email, you just need to attach the .pkpass file to an email and send it. The Mail.app client on the iPhone will automatically recognize that the attachment is a .pkpass mimetype and will allow you to add the file to your Passbook.

To allow a user to download a pass from a website, declare the MIME type as application/vnd.apple.pkpass so that Mobile Safari handle it as a pass. Loading a webpage with this MIME type initiates the Passbook installation process.

You can also use the PassKit framework to programmatically load a pass from within a mobile application. The next section will show you how to use the PassKit framework to load, access, and replace a pass on the device.

What can an application do with a pass?

To programmatically load, access, and modify a pass within an application, you would use the PassKit framework, which provides an Objective-C API for interacting with the user's pass library. For instance, the ice cream store mobile app can allow a customer to order a dessert online and add a pass to the user's library to let them claim their order, or update the users membership rewards card with the amount of points they deserve. An example application has been provided for download at the bottom of this post.

The next couple of sections will go over the following topics:

  1. Adding pass entitlements to your application
  2. Verifying that the pass library is available
  3. Adding a pass to the library
  4. Accessing a pass
  5. Modifying a pass
  6. Removing a pass

Adding pass entitlements to your application

Once you have created an Xcode project that will read and write pkpasses, you have to remember to enable Entitlements for the target. Go to the Target Name / Summary Tab and click on the checkbox labeled Entitlements. This will generate a TargetName.entitlements file for you. Make sure to either use pass type identifiers from the provisioning profile, or manually add pass type identifiers for the passes you want to access.

{C}

Check that the pass library is available

First use isPassLibraryAvailable to determine that the pass library exists and is accessible. After the pass library is determined to be readable, create a PKPassLibrary object.

<span class="co2">//check if pass library is available</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="sy0">!</span><span class="br0">[</span>PKPassLibrary isPassLibraryAvailable<span class="br0">]</span><span class="br0">)</span> <span class="br0">{</span>
 UIAlertView<span class="sy0">*</span> alertView <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>UIAlertView alloc<span class="br0">]</span> initWithTitle<span class="sy0">:</span><span class="co3">@</span><span class="st0">"Pass Library Error"</span> message<span class="sy0">:</span><span class="co3">@</span><span class="st0">"The Pass Library is not available."</span> delegate<span class="sy0">:</span><span class="kw2">nil</span> cancelButtonTitle<span class="sy0">:</span><span class="co3">@</span><span class="st0">"OK"</span> otherButtonTitles<span class="sy0">:</span><span class="kw2">nil</span><span class="br0">]</span>;
 <span class="br0">[</span>alertView show<span class="br0">]</span>;
<span class="br0">}</span>
_passLib <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>PKPassLibrary alloc<span class="br0">]</span> init<span class="br0">]</span>;

Adding a pass

To add a pass to the library:

  1. Create an instance of PKPass object, initialized with the pass's data.
  2. Use the containsPass: method to check whether the pass is in the library, even if it doesn't have the entitlements to read passes in the library.
  3. If the pass does not exist, and the pass library is available, use an instance of the PKAddPassesViewController class to request authorization from the user to add the pass. This view controller will also display the pass before it is added to the pass library.
<span class="sy0">-</span> <span class="br0">(</span>IBAction<span class="br0">)</span>addPassButtonPressed<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>sender <span class="br0">{</span>
 
 <span class="co2">//load StoreCard.pkpass from resource bundle</span>
 <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span class="kw5">NSString</span></a> <span class="sy0">*</span>filePath <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSBundle_Class/"><span class="kw5">NSBundle</span></a> mainBundle<span class="br0">]</span> pathForResource<span class="sy0">:</span><span class="co3">@</span><span class="st0">"StoreCard"</span> ofType<span class="sy0">:</span><span class="co3">@</span><span class="st0">"pkpass"</span><span class="br0">]</span>;
 <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/"><span class="kw5">NSData</span></a> <span class="sy0">*</span>data <span class="sy0">=</span> <span class="br0">[</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/"><span class="kw5">NSData</span></a> dataWithContentsOfFile<span class="sy0">:</span>filePath<span class="br0">]</span>;
 <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/"><span class="kw5">NSError</span></a> <span class="sy0">*</span>error;
 
 <span class="co2">//init a pass object with the data</span>
 PKPass <span class="sy0">*</span>pass <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>PKPass alloc<span class="br0">]</span> initWithData<span class="sy0">:</span>data error<span class="sy0">:&</span>amp;error<span class="br0">]</span>;
 
 <span class="co2">//init a pass library</span>
 PKPassLibrary<span class="sy0">*</span> passLib <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>PKPassLibrary alloc<span class="br0">]</span> init<span class="br0">]</span>;
 
 <span class="co2">//check if pass library contains this pass already</span>
 <span class="kw1">if</span><span class="br0">(</span><span class="br0">[</span>passLib containsPass<span class="sy0">:</span>pass<span class="br0">]</span><span class="br0">)</span> <span class="br0">{</span>
 
 <span class="co2">//pass already exists in library, show an error message</span>
 UIAlertView<span class="sy0">*</span> alertView <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>UIAlertView alloc<span class="br0">]</span> initWithTitle<span class="sy0">:</span><span class="co3">@</span><span class="st0">"Pass Exists"</span> message<span class="sy0">:</span><span class="co3">@</span><span class="st0">"The pass you are trying to add to Passbook is already present."</span> delegate<span class="sy0">:</span><span class="kw2">nil</span> cancelButtonTitle<span class="sy0">:</span><span class="co3">@</span><span class="st0">"OK"</span> otherButtonTitles<span class="sy0">:</span><span class="kw2">nil</span><span class="br0">]</span>;
 <span class="br0">[</span>alertView show<span class="br0">]</span>;
 
 <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
 
 <span class="co2">//present view controller to add the pass to the library</span>
 PKAddPassesViewController <span class="sy0">*</span>vc <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>PKAddPassesViewController alloc<span class="br0">]</span> initWithPass<span class="sy0">:</span>pass<span class="br0">]</span>;
 <span class="br0">[</span>vc setDelegate<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>self<span class="br0">]</span>;
 <span class="br0">[</span>self presentViewController<span class="sy0">:</span>vc animated<span class="sy0">:</span><span class="kw2">YES</span> completion<span class="sy0">:</span><span class="kw2">nil</span><span class="br0">]</span>;
 <span class="br0">}</span>
<span class="br0">}</span>

Accessing a pass

To access a pass in the library, the application must have entitlements to read and write that specific pass type identifier.

  1. Use the passes method to return a list of passes in the user's library. Only passes that your app has entitlements for will be returned.
  2. Or, use the passWithPassTypeIdentifier:serialNumber: method to retrieve a single pass.
  3. To access general fields in the pass, use the icon, localizedName, localizedDescription, organizationName properties of the PKPass class.
  4. To access specific fields in the pass, use the localizedValueForFieldKey method of the PKPass class.

The example and screenshot below provide an example of how information retrieved from the pass can be displayed.

<span class="sy0">-</span> <span class="br0">(</span>IBAction<span class="br0">)</span>accessPassButtonPressed<span class="sy0">:</span><span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span>sender <span class="br0">{</span>
 
 <span class="co2">//init a pass library</span>
 PKPassLibrary<span class="sy0">*</span> passLib <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>PKPassLibrary alloc<span class="br0">]</span> init<span class="br0">]</span>;
 
 <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/"><span class="kw5">NSArray</span></a> <span class="sy0">*</span> passArray <span class="sy0">=</span> <span class="br0">[</span>passLib passes<span class="br0">]</span>;
 NSLog<span class="br0">(</span><span class="co3">@</span><span class="st0">"number of passes in library are: %d"</span>,<span class="br0">[</span>passArray count<span class="br0">]</span><span class="br0">)</span>;
 
 _numberOfPasses.text <span class="sy0">=</span> <span class="br0">[</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span class="kw5">NSString</span></a> stringWithFormat<span class="sy0">:</span><span class="co3">@</span><span class="st0">"%d"</span>,<span class="br0">[</span>passArray count<span class="br0">]</span><span class="br0">]</span>;
 
 <span class="co2">//if more tha one pass in library, just use the first one.</span>
 <span class="kw1">if</span> <span class="br0">(</span><span class="br0">[</span>passArray count<span class="br0">]</span> > <span class="nu0">0</span><span class="br0">)</span>
 <span class="br0">{</span>
 PKPass <span class="sy0">*</span>onePass <span class="sy0">=</span> <span class="br0">[</span>passArray objectAtIndex<span class="sy0">:</span><span class="nu0">0</span><span class="br0">]</span>;
 
 <span class="co2">//access general fieldnames</span>
 _localizedName.text <span class="sy0">=</span> <span class="br0">[</span>onePass localizedName<span class="br0">]</span>;
 _organizationName.text <span class="sy0">=</span> <span class="br0">[</span>onePass organizationName<span class="br0">]</span>;
 
 <span class="co2">//access a specific field name</span>
 _rewardsBalance.text <span class="sy0">=</span> <span class="br0">[</span>onePass localizedValueForFieldKey<span class="sy0">:</span><span class="co3">@</span><span class="st0">"rewards"</span><span class="br0">]</span>;
 <span class="br0">}</span>
<span class="br0">}</span>

{C}

Note: there is no method to display a pass in your application, you are only provided with functions to retrieve information from the pass. This is because Apple expects that passes will be displayed in the Passbook application instead of your application. There is a way to display a pass directly in Passbook by sending it the passes url:

<span class="br0">[</span>sharedApp openURL<span class="sy0">:</span><span class="br0">[</span>tappedPass passURL<span class="br0">]</span><span class="br0">]</span>

Modifying a pass

To modify a pass in the library, the application must first have entitlements to write that specific pass type identifier. Use the passWithPassTypeIdentifier:serialNumber: method of the PKPassLibrary class to find a pass. After retrieving the pass object, you have two ways to modify the pass:

  1. Create a new pass and replace the old pass, using the replacePassWithPass: method of the PKPassLibrary class. The passTypeIdentifier and serialNumber of the old pass must match the pass that is replacing it. The new pass will not be displayed to the user automatically, so you must notify the user perhaps by listening to PKPassLibraryDidChangeNotification in your application.
  2. You can also use the removePass: method of the PKPassLibrary class, to remove a pass from the pass library, and then add a new pass with a different serial number.

One thing to remember is that you cannot modify and generate a new pass on the device; you must obtain it from somewhere else such as your web service.