|
| 1 | +# Setting Up Pass Personalization |
| 2 | + |
| 3 | +Create a personalized pass for Apple Wallet. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +> Warning: This section is a work in progress. Testing is hard without access to the certificates required to develop this feature. If you have access to the entitlements, please help us implement this feature. |
| 8 | +
|
| 9 | +Pass Personalization lets you create passes, referred to as personalizable passes, that prompt the user to provide personal information during signup that is used to update the pass. |
| 10 | + |
| 11 | +> Important: Making a pass personalizable, just like adding NFC to a pass, requires a special entitlement issued by Apple. Although accessing such entitlements is hard if you're not a big company, you can learn more in [Getting Started with Apple Wallet](https://developer.apple.com/wallet/get-started/). |
| 12 | +
|
| 13 | +Personalizable passes can be distributed like any other pass. For information on personalizable passes, see the [Wallet Developer Guide](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/PassPersonalization.html#//apple_ref/doc/uid/TP40012195-CH12-SW2) and [Return a Personalized Pass](https://developer.apple.com/documentation/walletpasses/return_a_personalized_pass). |
| 14 | + |
| 15 | +### Implement the Data Model |
| 16 | + |
| 17 | +You'll have to make a few changes to your ``PassDataModel`` to support personalizable passes. |
| 18 | + |
| 19 | +A personalizable pass is just a standard pass package with the following additional files: |
| 20 | + |
| 21 | +- A `personalization.json` file. |
| 22 | + |
| 23 | + |
| 24 | +Implement the ``PassDataModel/personalizationJSON(on:)`` method. |
| 25 | +If the pass requires personalization, and if it was not already personalized, create the `PersonalizationJSON` struct, which will contain all the fields for the generated `personalization.json` file, and return it, otherwise return `nil`. |
| 26 | + |
| 27 | +In the ``PassDataModel/sourceFilesDirectoryPath(on:)`` method, you have to return two different directory paths, depending on whether the pass has to be personalized or not. If it does, the directory must contain the `[email protected]` file. |
| 28 | + |
| 29 | +Finally, you have to implement the ``PassDataModel/passJSON(on:)`` method as usual, but remember to use in the `PassJSON.Properties` initializer the user info that will be saved inside ``Personalization`` after the pass has been personalized. |
| 30 | +Each ``Personalization`` instance has a reference to the pass it belongs to, so you can easily retrieve the user info for the pass. |
| 31 | + |
| 32 | +```swift |
| 33 | +extension PassData { |
| 34 | + func passJSON(on db: any Database) async throws -> any PassJSON.Properties { |
| 35 | + // Here create the pass JSON data as usual. |
| 36 | + try await PassJSONData(data: self, pass: self.$pass.get(on: db)) |
| 37 | + } |
| 38 | + |
| 39 | + func personalizationJSON(on db: any Database) async throws -> PersonalizationJSON? { |
| 40 | + let pass = try await self.$pass.get(on: db) |
| 41 | + |
| 42 | + let personalization = try await Personalization.query(on: db) |
| 43 | + .filter(\.$pass.$id == pass.requireID()) |
| 44 | + .first() |
| 45 | + |
| 46 | + if personalization == nil { |
| 47 | + // If the pass requires personalization, create the personalization JSON struct. |
| 48 | + return PersonalizationJSON( |
| 49 | + requiredPersonalizationFields: [.name, .postalCode, .emailAddress, .phoneNumber], |
| 50 | + description: "Hello, World!" |
| 51 | + ) |
| 52 | + } else { |
| 53 | + // Otherwise, return `nil`. |
| 54 | + return nil |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + func sourceFilesDirectoryPath(on db: any Database) async throws -> String { |
| 59 | + if self.requiresPersonalization { |
| 60 | + // If the pass requires personalization, return the URL path to the personalization template, |
| 61 | + // which must contain the `[email protected]` file. |
| 62 | + return "SourceFiles/Passes/Personalization/" |
| 63 | + } else { |
| 64 | + // Otherwise, return the URL path to the standard pass template. |
| 65 | + return "SourceFiles/Passes/Standard/" |
| 66 | + } |
| 67 | + } |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +### Implement the Web Service |
| 72 | + |
| 73 | +After implementing the data model methods, there is nothing else you have to do. |
| 74 | + |
| 75 | +Build the pass bundle with a `PassBuilder` as usual and distribute it. |
| 76 | +The user will be prompted to provide the required personal information when they add the pass. |
| 77 | +Wallet will then send the user personal information to your server, which should be saved in the ``Personalization`` table. |
| 78 | +Immediately after that, Wallet will request the updated pass. |
| 79 | +This updated pass will contain the user personalization data that was previously saved inside the ``Personalization`` table. |
| 80 | +You can access the pass linked to the personalization data by using the ``Personalization/pass`` field. |
| 81 | + |
| 82 | +> Important: This updated and personalized pass **must not** contain the `personalization.json` file, so make sure that the ``PassDataModel/personalizationJSON(on:)`` method returns `nil` when the pass has already been personalized. |
| 83 | +
|
| 84 | +## Topics |
| 85 | + |
| 86 | +### Data Model Method |
| 87 | + |
| 88 | +- ``PassDataModel/personalizationJSON(on:)`` |
0 commit comments