Pass Design and Creation

The files that make up a pass are arranged in a package called the pass package. At the center of the pass is a JSON file named pass.json which defines the pass. The file contains information that identifies the pass, the text that appears on it, and other information about the pass. In addition to this file, you provide images and localization data. Figure 3-1 shows the directory structure for a sample pass that provides an icon, a logo, and a thumbnail. The pass is localized in English and Chinese, it has both at Retina and non-Retina sizes of the image assets, and the logo is localized.

Figure 3-1  Directory structure of a sample pass

Most of your work in creating a pass is done by providing keys and values in the pass.json file.

Passes Are Identified by Pass Type Identifier and Serial Number

The pass type identifier is conceptually similar to a bundle identifier or a class name. The value for the passTypeIdentifier key in the pass specifies the pass type identifier. It is a string, chosen by you, that defines a class or category of passes. It always begins with pass. and uses reverse DNS style—for example, pass.com.example.membership-card. Use the Certificates, Identifiers & Profiles area of the Member Center to register a pass type identifier. The pass type identifier must match the certificate used to sign the pass.

The exact meaning of each pass type identifier is determined by you. You specify what types of pass an app can interact with as part of the app’s entitlements. This allows you to have app-specific passes if you are developing multiple apps. A different certificate is used for signing passes with different pass type identifiers. This means that groups in your company that work with different types of passes don’t need to share signing infrastructure, certificates, or private keys.

The serial number is a string that uniquely identifies the pass within the scope of its pass type. The value for the serialNumber key in the pass specifies the serial number. The serial number is opaque to Passbook; you are free to assign it in whatever way makes sense. A monotonically increasing integer or a UUID are convenient ways to assign the serial number.

The combination of pass type identifier and serial number is used throughout Passbook to uniquely identify a pass. Two passes of the same type with the same serial number are understood to be the same pass, even if other information on them differs. For example, when a pass is updated, the new version has the same pass type identifier and serial number as the old version, so the new version replaces the old version.

The difference between pass type identifiers and serial numbers is similar to the difference between classes and instances of a class: pass type identifiers designate an abstract kind of thing, and serial numbers identify a specific concrete thing. For example, suppose an airline is creating passes for boarding passes and for its loyalty card program. All of the boarding passes you create have the same pass type identifier because they’re all the same type of pass, but each one has its own serial number. Loyalty cards have a different pass type identifier than boarding passes, because they’re a different type of pass.

Required Information Is Provided by Top-Level Keys

In addition to the pass type identifier and serial number, there are several other keys that every pass contains.

The version of the file format is specified by the formatVersion key; its value is the number 1.

The team identifier is a series of letters and numbers issued to you by Apple. The value for the teamIdentifier key in the pass specifies the team identifier. It must match the Team ID of the certificate used to sign the pass. You can find your Team ID in Member Center, or you can find it in Keychain Access by looking at the Organizational Unit field of your certificate.

The organization name is displayed on the lock screen when your pass is relevant and by apps such as Mail which act as a conduit for passes. The value for the organizationName key in the pass specifies the organization name. Choose a name that users will recognize and associate with your organization or company.

The description lets VoiceOver make your pass accessible to blind and low vision users. The value for the description key in the pass specifies the description. The description should start with a high-level term such as “Membership card”, “Weekly coupon”, or “Bus ticket” followed by one or two small pieces of information such as the coupon’s offer and the store where it’s valid. Don’t try to summarize the entire contents of the pass, but include enough detail to let users distinguish between passes of the same type.

Pass Style Sets the Overall Visual Appearance

The pass’s style determines the overall visual appearance of the pass and the template for placement of information on the pass. The most distinctive visual indication of the style is at the top edge of the pass: event tickets have a small cutout, coupons have a perforated edge, and so on.

Unlike pass type identifiers, which you define, the pass styles are part of the API, as is their meaning. There is no facility for you to change them or add new ones. Pass type identifiers categorize passes in a very specific way; pass styles categorize passes in a much more general high-level way.

You specify the pass style by providing the corresponding key at the top level of the pass.json file:

The value of this key is a dictionary containing the fields that hold the pass content. Listing 3-1 shows a pass that uses the boarding pass style, with a placeholder for the field dictionaries.

Listing 3-1  Partial pass showing top-level keys

{
    "description" : "Boarding pass for October 4, San Francisco to London",
    "formatVersion" : 1,
    "passTypeIdentifier" : "pass.com.example.boarding-pass",
    "serialNumber" : "123456",
    "boardingPass" : {
        <<field dictionaries>>
    }
}

The pass style controls how fields are laid out and which images can be used. Table 3-1 shows the images supported by each pass style and the number and placement of fields for each pass style. Pass style also affects relevance information—for details, see “Relevance Information Displays Passes on the Lock Screen.”

Table 3-1  Pass styles, images, and layout

Pass style

Supported images

Layout

Boarding pass

logo, icon, footer

See Figure 3-2

Coupon

logo, icon, strip

See Figure 3-3

Event ticket

logo, icon, strip, background, thumbnail

If you specify a strip image, do not specify a background image or a thumbnail.

See Figure 3-4

Generic

logo, icon, thumbnail

See Figure 3-5

Store card

logo, icon, strip

See Figure 3-6

Figure 3-2  Layout of a boarding pass
Figure 3-3  Layout of a coupon
Figure 3-4  Layout of an event ticket
Figure 3-5  Layout of a generic pass
Figure 3-6  Layout of a store card

The pass style determines the maximum number of fields that can appear on the front of a pass:

The number of fields displayed on the pass also depends on the length of the text in each field. If there is too much text, some fields may not be displayed.

Fields Contain Text Displayed on the Pass

The information shown on the pass is broken up into fields. Each field is defined by a dictionary which gives it a value and label (which are displayed to the user), a unique key, and optional information about how its value should be formatted. Listing 3-2 shows a pass with a few simple fields.

The primary fields contain the most important information and are shown prominently on the pass. Secondary fields are less important and less prominent, and auxiliary fields even less. Header fields contain highly salient information, and they are the only field that is visible when the passes are stacked up in Passbook; use them sparingly.

Listing 3-2  Pass with simple fields

{
    "description" : "Boarding pass for October 4, San Francisco to London",
    "formatVersion" : 1,
    "passTypeIdentifier" : "pass.com.example.boarding-pass",
    "serialNumber" : "123456",
    "boardingPass" : {
        "primaryFields" : [
            {
                "key" : "origin",
                "label" : "San Francisco",
                "value" : "SFO"
            },
            {
                "key" : "destination",
                "label" : "London",
                "value" : "LHR"
            }
        ],
        "secondaryFields" : [
            {
                "key" : "boarding-gate",
                "label" : "Gate",
                "value" : "F12"
            }
        ],
        "auxiliaryFields" : [
            {
                "key" : "seat",
                "label" : "Seat",
                "value" : "7A"
            },
            {
                "key" : "passenger-name",
                "label" : "Passenger",
                "value" : "John Appleseed"
            }
        ],
        "transitType" : "PKTransitTypeAir"
    }
}

The ordering between the lists of fields is not significant, but the ordering of fields within the list is significant. For example, putting the primary fields before or after the secondary fields doesn’t change where the primary and secondary fields appear, but putting the seat assignment before or after the passenger name changes the ordering of those two fields on the pass. This is because the key/value pairs in a dictionary aren’t ordered, but lists are.

For a complete list of the keys used in a field dictionary and their possible values, see “Field Dictionary Keys” in Passbook Package Format Reference.

Fields Support Formatting

There are three kinds of formatting you can apply to a field: alignment, date formatters, and number formatters:

Letting Passbook handle dates, times, and currency amounts ensures the right display formatting based on the user’s locale. Listing 3-3 shows an example of date and number formatting.

Listing 3-3  Partial pass with date and number formatting

{
    ...
 
    "description" : "Concert ticket to see The Hectic Glow",
    "eventTicket" : {
        "primaryFields" : [
            {
                "key" : "event-name",
                "label" : "Event",
                "value" : "The Hectic Glow in concert"
            }
        ],
        "secondaryFields" : [
            {
                "dateStyle" : "PKDateStyleMedium",
                "isRelative" : true,
                "key" : "doors-open",
                "label" : "Doors open",
                "timeStyle" : "PKDateStyleShort",
                "value" : "2013-08-10T19:30-06:00"
            },
            {
                "key" : "seating-section",
                "label" : "Seating section",
                "numberStyle" : "PKNumberStyleSpellOut",
                "textAlignment" : "PKTextAlignmentRight",
                "value" : 5
            }
        ]
    }
}

The Back of the Pass Provides Additional Space for Text

Space on the front of the pass is at a premium: the number of fields is limited, and the contents of fields must be brief. In contrast, the back of the pass can have as many fields as needed and the contents of fields can be much longer. If there is too much content fit on the screen at once, the back lets the user scroll through it. As the contents of each field on the back gets longer, the field’s font size is reduced.

The back of the pass gives you a place to put information that is too long to fit on the front of the pass, extra information that isn’t as important, and general information that may be helpful for your users. Examples of back fields include terms and conditions, the full address of a venue, your customer service phone number, and the URL for your website.

Listing 3-4 shows examples of back fields. These go in the same place in the pass file as the rest of the fields—this key is a sibling to primaryFields and auxiliaryFields.

Listing 3-4  Partial pass with back fields

{
    ...
 
    "backFields" : [
        {
            "key" : "frequent-flier-number",
            "label" : "Frequent flier number",
            "value" : "1234-5678"
        },
        {
            "key" : "website",
            "label" : "Track my checked bags",
            "value" : "http://www.example.com/track-bags/XYZ123"
        },
        {
            "key" : "customer-service",
            "label" : "Customer service",
            "value" : "(800) 555-0199"
        },
        {
            "key" : "terms",
            "label" : "Terms and Conditions",
            "value" : "O Fortuna velut luna statu variabilis, semper crescis aut decrescis; vita detestabilis nunc obdurat et tunc curat ludo mentis aciem, egestatem, potestatem dissolvit ut glaciem.\n\n Sors immanis et inanis, rota tu volubilis, status malus, vana salus semper dissolubilis, obumbrata et velata michi quoque niteris; nunc per ludum dorsum nudum fero tui sceleris.\n\n Sors salutis et virtutis michi nunc contraria, est affectus et defectus semper in angaria.  Hac in hora sine mora corde pulsum tangite; quod per sortem sternit fortem, mecum omnes plangite!"
        }
    ]
}

The text of the back fields is run through data detectors for URLs and phone numbers, which appear as live links. Users can tap on the URL to launch it in Safari and on phone numbers to dial them. Text on the back of the card can include line breaks, escaped in the JSON file as \n.

To provide a link to an app, provide a list of iTunes Store item identifiers as the value of the associatedStoreIdentifiers key at the top level of the pass.json file.

Barcodes Link Passes to Your Records

Your server has the up-to-date records about your business, and the pass is just a copy of those records from some time in the past. The most common use for a barcode is a unique ID that points to corresponding records on your server. This let you consult your server to update a balance, void a coupon, or confirm that a ticket is valid.

To add a barcode to a pass, provide a value for the barcode key at the top level of the pass.json file. The value is a dictionary that describes the barcode you want to display. Specify the barcode format and provide the message to be displayed and the encoding used by the message. The optional alternate text is rendered near the barcode and contains a code to be entered manually if the barcode can’t be scanned. Listing 3-5 shows part of an example pass that contains a barcode.

Listing 3-5  Partial pass with a barcode

{
    ...
 
    "barcode" : {
        "message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
        "format" : "PKBarcodeFormatPDF417",
        "messageEncoding" : "iso-8859-1"
    }
}

Barcode scanners and software typically use the ISO 8859-1 encoding (also known as Latin-1) or the Windows-1252 encoding (sometimes incorrectly labelled Latin-1). Unicode in particular is poorly supported by most systems. Passbook itself supports all encodings supported by the NSString class.

The barcode message is just data, there’s nothing that limits you from using it to carry additional information. For example, suppose you need to scan passes in a setting without network access, which makes it impossible to communicate with your database. You can sign the data you need with a private key, and use the result as the barcode. When the pass is scanned, validating the signature lets you trust the data in the barcode. Although this approach doesn’t let you enforce a one-time-use pass, it could be useful for applications where passes are valid for a predictable period of time.

The amount of data that you can include in a barcode depends on the barcode format and the conditions under which the barcode is scanned. The barcode format defines a hard maximum that it supports, but scanning conditions often lower this limit. For example, consider a barcode displayed on a non-Retina device under poor lighting, and scanned by an inexpensive barcode scanner. Test your passes in real-world conditions on a variety of devices to ensure that the barcode can be scanned reliably.

Pass Colors Can Be Customized

By default, Passbook chooses the colors for your pass background and text. To override a color, provide a value for the appropriate key at the top level of the pass.json file. Colors are specified as RGB values—for example, rgb(0, 255, 0) is bright green. You can customize three colors:

Images Fill Their Allotted Space

The pass layout allots a certain area on the front of the pass for each image. Images are scaled (preserving aspect ratio) to fill this allotted space. Images with a different aspect ratio than their allotted space are cropped after being scaled. The space allotted is as follows:

Relevance Information Displays Passes on the Lock Screen

Passes let your users take some action in the real world, so accessing them needs to be easy and fast. Passbook integrates with the lock screen to make relevant passes immediately accessible from the lock screen. To take advantage of lock screen integration, add information about where and when your pass is relevant. A pass is relevant at the time and place that the user can redeem it. For example, a gym membership card is relevant at the gym, and a boarding pass is relevant at the time the flight begins boarding.

You provide a point in time and points in space in the pass. Passbook determines the appropriate duration of time and radius in space around these points that the pass appears on the lock screen. Don’t try to manipulate the time and place that pass appears on the lock screen by providing inaccurate relevance information.

Relevance information is passive: it helps users find passes when they need them by putting relevant passes right on the lock screen. It doesn’t present alerts or post notifications. This is in contrast to the notification posted when a pass updates, as described in “Updating a Pass.”

To provide a relevant date, add the relevantDate key at the top level of the pass. Its value is a W3C timestamp, either a complete date plus hours and minutes or a complete date plus hours minutes and seconds. For more information about the timestamp format, see Time and Date Formats on the W3C website.

To provide relevant locations, add the locations key at the top level of the pass. Its value is an array of dictionaries with longitude, latitude, and optional altitude information. Listing 3-6 shows part of an example pass which provides both a relative date and three relevant locations.

A pass can have up to ten relevant locations. In many cases, you have more than ten relevant locations so you have to pick the best ten. There are a variety of ways to do this. For example, you could have settings in your account management system to let customers add their favorite stores or the stores that they frequently visit. Like other data in the pass, relevance data can be changed when the pass is updated. A pass could initially have very generic relevance information which is replaced over time with relevant locations tailored by the user.

Listing 3-6  Partial pass with relevance information

{
    ...
 
    "description" : "Example pass showing relevance information",
    "locations" : [
        {"latitude" : 37.3229, "longitude" : -122.0323},
        {"latitude" : 37.3286, "longitude" : -122.0143},
        {
            "altitude" : 10.0,
            "latitude" : 37.331,
            "longitude" : -122.029,
            "relevantText" : "Store nearby on 3rd and Main."
        }
    ],
    "relevantDate" : "2014-12-05T09:00-08:00"
}

Depending on the pass style, the relevance information is interpreted in slightly different ways, as listed in Table 3-2. When the location is interpreted with a small radius, the current location must be on the order of a hundred meters or closer; with a large radius, on the order of a thousand meters or closer. In both cases, the exact relevance radius is an implementation detail and may change.

If you provide relevance information, the pass style also impacts what kind of information is required, optional, and not supported. For example, a coupon that provides relevance information must specify a relevant location. All pass styles support leaving off relevance information completely.

Table 3-2  Pass style and relevance information

Pass style

Relevant Date

Relevant Locations

Relevance

Boarding pass

Optional.

Optional.

Interpreted with a large radius.

Relevant if the date and any location matches.

Prior to iOS 6.1, relevant if the date or any location matches.

Without a relevant date, relevant if the location matches.

Coupon

Not supported.

Required if relevance information is provided.

Interpreted with a small radius.

Relevant if any location matches.

Event ticket

Optional.

Prior to iOS 7.1, required if relevance information is provided.

Optional.

Interpreted with a large radius.

Relevant if the date and any location matches.

Without a relevant location, relevant if the date matches.

Generic

Optional.

Required if relevance information is provided.

Interpreted with a small radius.

Relevant if the date and any location matches.

Without a relevant date, relevant if any location matches.

Store card

Not supported.

Required if relevance information is provided.

Interpreted with a small radius.

Relevant if any location matches.

The system provides the relevant text for passes that have time-based relevance. For passes with relevant locations, you provide a description of why the pass is relevant at a given location. This can be as simple as “Store nearby” or include a brief description of how to find the relevant location, such as cross streets or landmarks. Don’t include your organization name in the relevant text, and don’t include instructions to the user such as “Redeem this pass at XYZ”.

Passes Support Localization

There are three aspects to localizing a pass: displaying numbers and dates in the correct format, providing localized strings, and providing localized images.

Before you include localizations in a pass, consider whether you need it. You generate a pass for each user, and you have information about your users. If you let users tell you their preferred localizations through account settings, or if you can infer the localization from the geographic region, you can often include a small number of localizations or even produce a pass only in the preferred localization. Limiting the number of localizations helps keep pass size small by not providing localized images.

Use field formatters to display numbers and dates in the user’s preferred format and language. For more information about formatters, see “Fields Support Formatting.”

Use a strings file to provide localized strings. A strings file lists strings that appear in the pass.json file and the string to display for a particular language. For example, given the following pass fragment from an airline ticket:

{
    ...
 
    "primaryFields" : [
        {
            "key" : "origin",
            "label" : "Seville",
            "value" : "SVQ"
        },
        {
            "key" : "destination",
            "label" : "London",
            "value" : "LHR"
        }
    ]
}

To localize this pass into Spanish and English, do the following:

  1. Change pass file to factor out the text being localized:

    {
    ...
     
        "primaryFields" : [
            {
                "key" : "origin",
                "label" : "origin_SVQ",
                "value" : "SVQ"
            },
            {
                "key" : "destination",
                "label" : "destination_LHR",
                "value" : "LHR"
            }
        ]
    }

    The field values are no longer the text to be display—now they indicate which entry in the strings file should be used.

  2. Create an es.lproj directory in the pass package. Create a strings file named pass.strings containing the Spanish strings:

    "origin_SVQ" = "Sevilla";
    "destination_LHR" = "Londres";
  3. Create an en.lproj directory in the pass package. Create a strings file named pass.strings containing the English strings:

    "origin_SVQ" = "Seville";
    "destination_LHR" = "London";

If there is more than one localization, each localization needs its own .lproj directory, as shown earlier in Figure 3-1. If the text in the pass were left in English and Spanish text were provided in a strings file, the pass would display in Spanish only. For more information about strings files, see “Localizing String Resources” in Internationalization Programming Topics.

Images that need to be localized appear in the .lproj directories for their respective languages. Images that don’t need localization appear at the top level of the pass. Don’t include an image at the top level and inside a .lproj directory. For more information about localizing images, see “Localized Resource Files” in iOS App Programming Guide.

Passes Are Cryptographically Signed and Compressed

Passes are cryptographically signed and compressed before being distributed. This establishes the identity of the signer and lets your customers be sure the pass hasn’t been modified since being signed.

To create the manifest file, recursively list the files in the package (except the manifest file and signature), calculate the SHA-1 hash of the contents of those files, and store the data in a dictionary. The keys are relative paths to the file from the pass package. The values are the SHA-1 hash (hex encoded) of the data at that path. Write this to the file manifest.json at the top level of the pass package.

To create the signature file, make a PKCS #7 detached signature of the manifest file, using the private key associated with your signing certificate. Include the WWDR intermediate certificate as part of the signature; you can download this certificate from Apple’s website. Write the signature to the file signature at the top level of the pass package. Include the date and time that the pass was signed using the S/MIME signing-time attribute.

To compress the pass, create a ZIP archive of the contents of the pass package. This ZIP archive is what you distribute to users.

Passes Are Distributed via Email, the Web, or Your App

Mail and Safari have support for passes on iOS 6 and later and on OS X v10.8.2 and later, so you can use them to distribute passes by email or on a website. On iOS, they add passes to the pass library directly. On OS X, they use iCloud to add passes to the user’s iOS devices.

The advantage of sending passes as an email attachment is simplicity. When the user views the email, the pass appears and can be added to their pass library. During development, this is an easy way to get a pass onto your device. The disadvantage is that you can’t interact with the pass-installation process. For example, an email can’t present an alternate representation for older devices that don’t support passes.

Hosting passes on your web server gives you more control over the user experience, but is less simple. You can give your users a link to a landing page that lets them add the pass to their pass library. This page can also offer a representation of the pass in older formats such as PDF. This landing page also gives you a place to ask the user to log in if needed.

Using your own app to install passes is appropriate when your app provides a richer experience by integrating with your systems and business. For information about how your app can interact with passes, see “Interacting with Passes in Your App.”

Your apps, website, and emails can use the Add to Passbook badge to give your users a visual queue to add the pass to Passbook. For more information, see the Passbook for Developers section of the Developer website.

Debugging Passes

If the pass doesn’t display and add to Passbook, check the log for a description of what went wrong. When testing in the iOS Simulator app, errors are logged to the system log, which you can view with the Console app. When testing on a device, errors are logged to the device’s console which you can view from the Xcode organizer window. Common errors include malformed JSON files, misspelled keys or values, pass type identifiers that don’t match your certificate, and signatures that omit the WWDR intermediate certificate.