Beginning Passbook in iOS 6: Part 2/2
Note from Ray: This is the second iOS 6 tutorial in the iOS 6 Feast! This tutorial is an abbreviated version of one of the chapters from our new book iOS 6 By Tutorials. Marin Todorov wrote this chapter – the same guy who wrote most of the “bonus” chapters in iOS 5 by Tutorials. […] By Marin Todorov.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Note from Ray: This is the second iOS 6 tutorial in the iOS 6 Feast! This tutorial is an abbreviated version of one of the chapters from our new book iOS 6 By Tutorials. Marin Todorov wrote this chapter – the same guy who wrote most of the “bonus” chapters in iOS 5 by Tutorials. Enjoy!
This is a blog post by iOS Tutorial Team member Marin Todorov, a software developer with 12+ years of experience, an independent iOS developer and the creator of Touch Code Magazine.
If you followed part one of the series, you know so far how to put together a pass bundle, how to sign it, how to send over e-mail. I am sure that your fingers itch to dig deeper into creating passes – the hottest, newest technology from Apple.
Buckle up – we’re continuing exactly where we left off last time!
Additional pass information
The pass has two sides: on the front you can add text information for several predefined areas; on the back you can show as many fields as you want. Let’s first do some front-end maintenance. ☺
Open up pass.json and find the primaryFields array declaration. On the same level, inside the coupon dictionary, you are going to add more sections to show more fields.
As you will be adding another key to the coupon dictionary, you need to first add a comma, and then define the new key. Add that comma after the closing square bracket at the bottom of the file (have a look at the picture below for the exact location).
On the next line after the comma, paste in this JSON code:
"secondaryFields" : [
{
"key" : "expires",
"label" : "Valid for",
"value" : "Lifetime"
}
],
"auxiliaryFields" : [
{
"key" : "date",
"label" : "Promo starts on",
"dateStyle" : "PKDateStyleMedium",
"timeStyle" : "PKDateStyleNone",
"value" : "2013-01-11T00:00Z"
}
]
Now in addition to the primaryFields dictionary, you have secondaryFields and auxiliaryFields – these are the extra fields on the front side of the pass. They are not as visually prominent as the primary fields, but are still important enough to be on the front.
The auxiliary fields section should be of particular interest to you – it has some new properties that you haven’t used before. This field, unlike the rest of the ones you have, is a date field. Passbook takes care to convert the date/time values to the local time of the user, and also to format and localize the final text.
Have a look at the date field properties:
- value – it’s a W3C datetime formatted value (for boring documentation on it, check http://www.w3.org/TR/NOTE-datetime).
- dateStyle – the style to format the date part; must be one of: PKDateStyleNone, PKDateStyleShort, PKDateStyleMedium, PKDateStyleLong or PKDateStyleFull.
- timeStyle – the same as the date style field; use the same constant names to define the time style.
That’s done! It’s time for you to do another build and see how the pass looks this time! You’re having fun already, right?
The build steps to perform:
- Make sure the JSON code looks OK (if not, be sure to use http://jsonlint.com/ to check).
- In Terminal, navigate to your working folder and get the SHA1 checksum of pass.json by running:
openssl sha1 pass.json
- Place the pass.json SHA1 checksum in the manifest.json file.
- Generate the detached signature for the pass by running:
openssl smime -binary -sign -certfile WWDR.pem -signer passcertificate.pem -inkey passkey.pem -in manifest.json -out signature -outform DER -passin pass:12345
- And finally, zip up the files into a .pkpass package:
zip -r freehugcoupon.pkpass manifest.json pass.json signature logo.png logo@2x.png icon.png icon@2x.png strip.png strip@2x.png
- Send the file via email to your iOS 6 powered device and have a look!
openssl sha1 pass.json
openssl smime -binary -sign -certfile WWDR.pem -signer passcertificate.pem -inkey passkey.pem -in manifest.json -out signature -outform DER -passin pass:12345
zip -r freehugcoupon.pkpass manifest.json pass.json signature logo.png logo@2x.png icon.png icon@2x.png strip.png strip@2x.png
Mission accomplished! Your pass should look like the one in the picture below:
The front is now almost done, so do something for the back!
Open up pass.json and add a comma after the last closing square bracket (you should be proficient in adding the comma to the right spot by now). After the comma, add a fields dictionary for the back of the card:
"backFields" : [
{
"key" : "extras",
"label" : "Extras",
"value" : "Your friends receive 50% off price"
},
{
"key" : "phone",
"label" : "For more info",
"value" : "800-1234567890"
},
{
"key" : "terms",
"label" : "TERMS AND CONDITIONS",
"value" : "Free hugs last 18 seconds and must be claimed on your birthday. Bring your pass or an id"
}
]
Fortunately the back of the pass is scrollable, so you can add as much info as you need. (Yes, sometimes those Terms and Conditions sections get pretty lengthy, so scrolling is absolutely necessary.)
Now – it’s time to see what the fields you just added look like on the pass. Build again following the instructions from last time and send the pass via email in order to see it on your device. Tap on the “i” button and the back will flip into view – it should look like the picture below:
So far so good! Also check out the phone number – it’s automatically converted to a link, which you can tap on to be taken directly to the Phone app. You can include other interactive information on the back too – like addresses (which open up in Maps) and email addresses (which of course open up in Mail).
The Pass Preview application
You probably already feel that it would be great if the process of building and previewing passes could be a little bit easier and faster. I mean, come on, do we really have to email the pass to ourselves every time we want to preview it?
Well – good news! Since I know you are going to want try out many color combinations, pictures and texts before settling on the perfect pass, in this part of the chapter you’re going to build an iOS app that will allow you to preview your passes a lot faster inside the iPhone simulator on your Mac. Yay!
Note: OSX 10.8.2 has now support for pass previewing straight on your mac. If you double click on a .pkpass file you will see how the pass looks like. However the only way to check whether the pass is valid still is to open it up on your device or simulator and try to import it.
Note: OSX 10.8.2 has now support for pass previewing straight on your mac. If you double click on a .pkpass file you will see how the pass looks like. However the only way to check whether the pass is valid still is to open it up on your device or simulator and try to import it.
There’s one way to install passes in Passbook that I haven’t mentioned yet. Besides sending a pass file over email and downloading it straight from a web server, you can also use the new PassKit framework in iOS 6 to install a pass from within an iOS app.
PassKit is a very simple framework, as it contains only three classes. To build a simple pass preview application, you are going to use the PKAddPassesViewController. This class takes in a PKPass instance and displays it on the screen.
In real life, your app will most probably fetch a pass from your server, or get it via iCloud, but in this example you’re just going to use passes that are bundled with the app.
Let’s go!
From Xcode’s menu, select File/New/Project…, and then select iOS/Application/Single View Application and click Next. Name the project “PassesPreview” and make sure it’s an iPhone app. Also ensure that Use Storyboards and Use Automatic Reference Counting are checked. Save the project in a location of your choice.
In the Project Navigator, select the Xcode project (the top item in the project tree) and in the strip on the right make sure the “PassesPreview” target is selected. Next click on the Build Phases tab and open up the Link Binary With Libraries strip.
Click on the (+) button, and in the list of frameworks double-click on “PassKit.framework” to include it in your Xcode project (or simply select PassKit.framework and tap the (+) button).
Now select MainStoryboard.storyboard – the app interface pops up. You need a full screen table, so grab a table view from the Objects palette and drop it on the main view:
Next, hold the Ctrl key and drag to the view controller object on the bottom palette. A popup menu will appears. Click on “dataSource”, then repeat, and the second time choose “delegate”. The table is now connected properly to your view controller.
And now, finally some Objective-C goodness!
Open up ViewController.m and replace its contents with this:
#import "ViewController.h"
#import <PassKit/PassKit.h> //1
@interface ViewController () //2
<UITableViewDelegate, UITableViewDataSource,
PKAddPassesViewControllerDelegate>
{
NSMutableArray *_passes; //3
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//4
if (![PKPassLibrary isPassLibraryAvailable]) {
[[[UIAlertView alloc] initWithTitle:@"Error"
message:@"PassKit not available"
delegate:nil
cancelButtonTitle:@"Pitty"
otherButtonTitles: nil] show];
return;
}
}
@end
The code is pretty basic and there are just four things to note:
- First you import the PassKit framework headers.
- In the class extension of the ViewController class, you make it conform to the data source and delegate protocols of UITableView, and finally to the PKAddPassesViewControllerDelegate protocol.
- You declare an array instance variable to hold the list of all the bundled pass files.
- Finally in viewDidLoad, if the result of [PKPassLibrary isPassLibraryAvailable] is NO then you show a message to let the user know they don’t have Passbook available. (The user in this case is you, but it was a nice opportunity to show you how to check for Passbook availability, so I snuck it in.)
The goal will be to check if there are passes bundled with the app and if so, to show a list of them. The app will also show nice previews of the passes when the user taps on a given pass file name.
Add the following at the end of viewDidLoad:
// 1 initialize objects
_passes = [[NSMutableArray alloc] init];
//2 load the passes from the resource folder
NSString* resourcePath =
[[NSBundle mainBundle] resourcePath];
NSArray* passFiles = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:resourcePath
error:nil];
//3 loop over the resource files
for (NSString* passFile in passFiles) {
if ( [passFile hasSuffix:@".pkpass"] ) {
[_passes addObject: passFile];
}
}
What this simple code does is:
- First initializes the _passes array.
- Gets the list of all app resource files into the passFiles array.
- Checks all file names in passFiles and adds the ones having extension .pkpass to the _passes array.
Pretty simple! Now you have the names of all passes bundled with your app. Next, let’s show them in the table!
Add these three methods to ViewController.m in order to get the table view running:
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _passes.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (!cell) cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
NSString *object = _passes[indexPath.row];
cell.textLabel.text = object;
return cell;
}
More simple code – you’ve probably done this many times already. However, do note some of the new code changes: you can now get the count of elements in an array by accessing the count method (it’s not a property) with return _passes.count. Getting elements out of an array (or a dictionary) is now simpler, too:
NSString *object = _passes[indexPath.row];
Some of these changes, like the dot-notation for methods, were around before iOS 6, but they are still fairly new. Whereas other stuff like the new NSArray notation is brand-spanking new, as you can read about in Chapter 1, “What’s New in Objective-C.”
This should be enough – you can run the project and see no warnings or errors, and get an empty table view on screen.
OK, time to include some passes with the app. First download the passes I’ve prepared in advance for you: Passes.zip. Extract the files and copy all the .pkpass files from the Passes folder into your Xcode project by dragging the .pkpass files and dropping them on your Xcode project root.
Note: Make sure to check that those files are added to the PassesPreview target. By default, Xcode won’t include them, as they are not code files or other known iOS resources.
Note: Make sure to check that those files are added to the PassesPreview target. By default, Xcode won’t include them, as they are not code files or other known iOS resources.
Note: When you are working and designing your own passes do NOT check the “Copy items into …” checkbox. It’s much simpler to just link the .pkpass files from your Pass folder into the Xcode project. When you do code changes or replace an image, then you just rebuild the pass and restart the Xcode project, and the changed pass pops up on the screen.
Note: When you are working and designing your own passes do NOT check the “Copy items into …” checkbox. It’s much simpler to just link the .pkpass files from your Pass folder into the Xcode project. When you do code changes or replace an image, then you just rebuild the pass and restart the Xcode project, and the changed pass pops up on the screen.
OK, now that you’ve got some passes in the app, hit run and you’ll see the list of passes:
Finally, you need to add the code to show the selected pass on the screen. Add it to ViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{ //1
NSString* passName = _passes[indexPath.row];
[self openPassWithName:passName];
}
-(void)openPassWithName:(NSString*)name
{
//2
NSString* passFile = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent: name];
//3
NSData *passData = [NSData dataWithContentsOfFile:passFile];
//4
NSError* error = nil;
PKPass *newPass = [[PKPass alloc] initWithData:passData
error:&error];
//5
if (error!=nil) {
[[[UIAlertView alloc] initWithTitle:@"Passes error"
message:[error
localizedDescription]
delegate:nil
cancelButtonTitle:@"Ooops"
otherButtonTitles: nil] show];
return;
}
//6
PKAddPassesViewController *addController =
[[PKAddPassesViewController alloc] initWithPass:newPass];
addController.delegate = self;
[self presentViewController:addController
animated:YES
completion:nil];
}
All right – there’s a fair bit of code, but it’s easy to go over it:
- First, when a table row is tapped you call openPassWithName.
- Inside openPassWithName you recreate the full path to the .pkpass file.
- Then you read the contents of the .pkpass file into an NSData instance.
- Then you create a new PKPass object by calling its initializer initWithData:error:.
- You check the error variable for any load issues.
- In the end, you create a new PKAddPassesViewController instance – this is a special view controller, which takes in a PKPass object and displays it. Assign the delegate property to self and present the controller modally on the screen.
You need one more method – the one called when the user closes the pass dialogue. (You’re not going to do anything special here, but it’s good to know your options for when you develop something more complicated.)
Add this method required by the PKAddPassesViewController delegate protocol:
#pragma mark - Pass controller delegate
-(void)addPassesViewControllerDidFinish: (PKAddPassesViewController*) controller
{
//pass added
[self dismissViewControllerAnimated:YES completion:nil];
}
Now you can run the project again and tap on the row saying FreeHug.pkpass – so cool! You can preview the passes and also import them into the Passbook app in your iPhone Simulator.
This is exactly the same dialogue that popped up inside your Mail app when you opened a pass attachment. Note that if you have an identical pass in your Passbook already, the Add button at the top right corner will be disabled.
Success!
One last touch – add the following at the end of viewDidLoad:
if ([_passes count]==1) {
[self openPassWithName:[_passes objectAtIndex:0]];
}
When you are working on refining a single pass, you can save yourself the extra tap!
So far, you know everything you need to know about how to build a coupon, and you know how to easily test and preview your passes. This is the perfect combination of skills to approach the next topic in this part of the chapter: getting acquainted with the different styles of passes.