Supporting iOS 18 Changes
iOS 18 have been announced a few days ago and, a few hours ago, the session including the upcoming changes to Apple Wallet have been published: https://developer.apple.com/videos/play/wwdc2024/10108/
Some new fields have been published to support the new UI for events.
That said, within september, a passkit-generator update will be published.
If anyone wants to attempt to generate the new passes with an iOS 18 Beta through passkit-generator, I kindly ask you to leave a comment here, so I'll know if I should publish an alpha version with some changes.
This issue will be soon updated with a summary of the changes in the schemas.
Recap from the discussion until now
-
Schema changes are available in this branch,
-
The new
eventTicketis a pass that requires NFC Entitlements -
Show hidden and useless updates
- We were able to extract the NFC certificate used by PassKit to generate an NFC pass from its signature and found what are the practical differences between a normal one and and NFC one. A certificate cannot be tampered, so we cannot generate one with NFC capabilities.
- Although RC was released and new model is cited inside the changelog and the web page, with only the details provided in the WWDC video (and NFC certificate),
it seems to be impossible yet to generate a valid eventTicket with the new UI.it is possible now to generate one. Apparently not even with 18 or iOS 18.1, it is possible to generate it.
-
Apple made at least one mistake in their video:
preferredStyleSchemesis a top level property. All the errors are marked in the second message with astrikethroughstyle, followed or preceded by the correct property. -
One of
semantics.venueRegionName,semantics.venueNameorsemantics.venueRoomis a required for the new layouto appear. -
The new layout applies to all the
semantics.eventTypebutPKEventTypeSportsfor which two more properties are needed:semantics.awayTeamAbbreviationandsemantics.homeTeamAbbreviation -
The new layout applies to
semantics.eventType == PKEventTypeLivePerformance, butsemantics.performerNamesis required. -
Each
relevantDatesentry allows a max time span of 1 day. -
Found lot more of root properties for event guide, for transfering and selling tickets and for live activies.
-
A new asset
venueMap.png(and resolutions, probably) can be added to bundle to show the map of the venue in the event guide -
iOS 18.1 b6 shows perfectly the live activity, with seat color highlighted, even in case of general admission
-
has been released with all the discussed changes. We are waiting for the stable release of iOS 18.1 to release a stable version of this library, in order to see if an updated documentation will come out (other than the N.D.A/private documents shared with companies) and if something more should be added.
-
(root).bagPolicyURL(string) (link to message) -
(root).orderFoodURL(string) (link to message) -
(root).parkingInformationURL(string) (link to message) -
(root).contactVenuePhoneNumber(string) (link to message) -
(root).contactVenueWebsite(string) (link to message) -
(root).accessibilityURL(string) (link to message) -
(root).addOnURL(string) (link to message) -
(root).purchaseParkingURL(string) (link to message) -
(root).merchandiseURL(string) (link to message) -
(root).transitInformationURL(string) (link to message) - ~
(root).eventTicket.preferredStyleSchemes(["posterEventTicket", "eventTicket"])~ -
(root).preferredStyleSchemes(["posterEventTicket", "eventTicket"]) -
(root).sellURL(string) (link to message) -
(root).transferURL(string) (link to message) -
(root).suppressHeaderDarkening(boolean) (link to message -
(root).footerBackgroundColor(boolean) (link to message -
(root).useAutomaticColor(boolean) (link to message -
(root).auxiliaryStoreIdentifiers(boolean) (link to message -
(root).eventTicket.additionalInfoFields(Field[]) (link to message) -
(root).relevantDates[].startDate(date time string) (link to message, link to message) -
(root).relevantDates[].endDate(date time string) (link to message, link to message) -
(root).relevantDates[].relevantDate(date time string) (link to message) - ~
semantics.relevantDates[].startDate(date time string)~ (link to message) - ~
semantics.relevantDates[].endDate(date time string)~ (link to message) - ~
semantics.seats[].venueEntranceGate(string)~ -
semantics.venueEntranceGate(string) (link to message) -
semantics.admissionLevel(string) -
semantics.eventStartDateInfo(object) (link to message -
semantics.venueParkingLotsOpenDate(date time string) (link to message) -
semantics.venueGatesOpenDate(date time string) (link to message) -
semantics.venueRegionName(string) (link to message) -
semantics.entranceDescription(string) (link to message) -
semantics.attendeeName(string) (link to message) -
semantics.eventLiveMessage(string) (link to message) -
semantics.playlistIDs(link to message) -
semantics.albumIDs(link to message) -
semantics.venueBoxOfficeOpenDate(link to message) -
semantics.venueOpenDate(date time string) (link to message -
semantics.venueCloseDate(link to message) -
semantics.venueDoorsOpenDate(link to message) -
semantics.venueFanZoneOpenDate(link to message) -
semantics.tailgatingAllowed(link to message) -
semantics.additionalTicketAttributes(link to message) -
semantics.seats[].seatAisle(link to message) -
semantics.venueEntranceDoor(link to message) -
semantics.venueEntrancePortal(link to message) -
semantics.admissionLevelAbbreviation(link to message) -
semantics.airplay[].airPlayDeviceGroupToken(link to message) -
semantics.seats[].seatLevel(link to message) -
semantics.seats[].seatSectionColor(link to message, link to message)
iOS 18.1 changes (released in v3.5.5)
-
eventLogoText -
EventDateInfo.unannounced -
EventDateInfo.undetermined
I'm able and willing to test if you're publishing an alpha!
@nickwelsh Okay great! I'll work on it, probably this weekend.
Before having a stable release, I'll wait for them to publish the updated documentation.
I think these are the new fields but some more could appear possibly.
P.s. Did you leave a 🌟 on the project? 👀
Schema changes are available in this branch, so you can install the package in NPM from git and build it.
I'll release a version in the next days if I have some time.
If you can successfully test, can you provide me screenshots of the results? I'm very curious!
I built a minimal version using the stable build to make sure things worked:
Code
// index.ts
import pk from "passkit-generator"
import 'dotenv/config'
import fs from "fs"
const PKPass = pk.PKPass;
try {
const pass = await PKPass.from({
model: "./model.pass",
certificates: {
wwdr: process.env.WWDR,
signerCert: process.env.CERT,
signerKey: process.env.KEY,
signerKeyPassphrase: process.env.CHALLENGE
},
}, {
// keys to be added or overridden
serialNumber: "AAGH44625236dddaffbda"
});
const buffer = pass.getAsBuffer();
fs.writeFile('pass.pkpass', buffer, (err) => {
if (err) throw err;
console.log('Buffer saved to file');
});
} catch (err) {
console.error(err)
}
{
"organizationName": "My Org",
"description": "My Event Description",
"teamIdentifier": "******",
"passTypeIdentifier": "******",
"backgroundColor": "rgb(248,252,252)",
"foregroundColor": "rgb(111,162,163)",
"labelColor": "rgb(0,52,68)",
"formatVersion": 1,
"eventTicket": {
"preferredStyleSchemes": [
"posterEventTicket",
"eventTicket"
]
},
"semantics": {
"eventType": "PKEventTypeLivePerformance",
"eventName": "South Bay Jazz Festival",
"eventStartDate": "2024-07-15T10:00:00-06:00",
"seats": [
{
"seatDescription": "Normal Seat",
"seatIdentifier": "112-12-16",
"seatNumber": "5",
"seatRow" : "3",
"seatSection": "100",
"venueEntranceGate": "3"
}
]
}
}
Which, indeed, builds a pass that works, obviously using the old UI. Swapping out to the alpha you provided throws an error:
error: Object schema cannot be a joi schema
at /Users/nick/Downloads/passkit/node_modules/passkit-generator/node_modules/@hapi/hoek/lib/error.js:25:1
at /Users/nick/Downloads/passkit/node_modules/passkit-generator/node_modules/@hapi/hoek/lib/assert.js:19:30
at method (/Users/nick/Downloads/passkit/node_modules/passkit-generator/node_modules/joi/lib/types/keys.js:413:21)
at method (/Users/nick/Downloads/passkit/node_modules/passkit-generator/node_modules/joi/lib/types/keys.js:323:15)
at /Users/nick/Downloads/passkit/node_modules/passkit-generator/lib/schemas/index.js:17:1
at require (native:1:1)
at /Users/nick/Downloads/passkit/node_modules/passkit-generator/lib/FieldsArray.js:48:25
at require (native:1:1)
at /Users/nick/Downloads/passkit/node_modules/passkit-generator/lib/PKPass.js:677:50
at require (native:1:1)
@nickwelsh Are you able to see which property failed to validate?
Seems like it's the new preferredStyleSchemes or rather the newly appended object in the schema. Removing the append() allows the pass to be built, but then preferredStyleSchemes isn't part of the schema and won't be included in the created pass.json.
// schemas/index.ts
// ...
eventTicket: PassFields.disallow("transitType").append(
Joi.object<PassProps["eventTicket"]>().keys({
/**
* New field coming in iOS 18
* `"eventTicket"` is the legacy style.
*
* If used, passkit will try to render following the old style
* first.
*
* Which means that `primaryFields`, `secondaryFields` and
* so on, are not necessary anymore for the new style,
* as semantics are preferred.
*/
preferredStyleSchemes: Joi.array().items(
Joi.string().allow("posterEventTicket", "eventTicket"),
),
}),
),
// ...
@nickwelsh interesting. Maybe I should have used .concat instead of that .append. What happens if you replace it? I've the commit ready, just in case.
I tried replace with .concat, and now PKPass are able to build without crashes. Verified with pass.json payload that data actually written.
Although, I have concerns with preferredStyleSchemes field, since it not as a part of PassFields. It means that I have to override it via .props which could make type-checking a bit tricky.
@rayriffy Hey, thanks for testing!
Two things:
-
Using
.propsto edit things it not the suggested way to do that as.propsreturns a deep clone ofpass.json; -
What Typescript says is tecnically right,
eventTicketproperty might not exist cause your pass might not be of a typeeventTicket. In a new version I could try to find a way to make it more safe, but I'm not exactly sure how. I could create and expose some Typescript narrowing guards...
Other than that, I still didn't provide a way to set manually the preferredStyleSchemes just like primaryFields and so on... maybe I should provide a just like the others.
thanks, although it's not a blocker though. you can make an improvements to an interface later in future version. for now i will have to forcefully mark that eventTicket actually exists.
pass.props.eventTicket!.preferredStyleSchemes = [...]
also i have another suggestion, currently i help testing your library by manually clone a project because referencing package with gtihub: in package.json does not includes any source code nor built code to use. my suggestion is in files also includes src/ directory, and make a script field prepare to run build:src should do the job
I've committed .concat and added a new getter/setter .preferredStyleSchemes to access and to set them with validation. Of course, both will throw if the type is not an eventTicket.
also i have another suggestion, currently i help testing your library by manually clone a project because referencing package with gtihub: in package.json does not includes any source code nor built code to use. my suggestion is in files also includes src/ directory, and make a script field prepare to run build:src should do the job
I never used github: protocol in NPM, but I think you can also get rid of it, as explained here and above in the same page: https://docs.npmjs.com/cli/v10/configuring-npm/package-json#github-urls
It should clone and provide the content from the repository. There, you should have the source.
Once you install the package from github, you can just change the directory to the dependency and use npm run build.
Let me know if it works.
Getting back to the pass, so, are you able to generate with with the new format?
unfortunately not at this moment, i tried with multiple relevant semantics fields and could not get it to work. lacking of documentation from apple is killing me, my best guess is maybe new event ticket format has not been added to the developer beta 1 yet.
maybe someone else also have a success? i would like to know as well.
Did you try to open it on a real iPhone? Could it be it is not available on the Simulator yet?
BTW I think it is still early for the documentation to come out. It will get surely updated in the next months.
nah doesn't work either, i will drop my pass.json here if anyone has any insights of what's wrong
Code
{
"formatVersion": 1,
"passTypeIdentifier": "",
"teamIdentifier": "",
"serialNumber": "ahsdg2",
"organizationName": "Creatorsgarten",
"description": "Creatorsgarten Event Ticket",
"foregroundColor": "rgb(255, 255, 255)",
"backgroundColor": "rgb(0, 0, 0)",
"labelColor": "rgb(255, 255, 255)",
"semantics": {
"eventName": "The โง่ Hackathon ครั้งที่ 8 แห่งประเทศ Thailand",
"eventType": "PKEventTypeLivePerformance",
"eventStartDate": "2024-07-13T00:00+07:00",
"eventEndDate": "2024-07-14T23:59+07:00",
"relevantDates": [
{
"startDate": "2024-07-13T08:00+07:00",
"endDate": "2024-07-14T23:59+07:00"
}
],
"admissionLevel": "ElysiaJS"
},
"relevantDate": "2024-07-13T01:00:00.000Z",
"barcodes": [
{
"format": "PKBarcodeFormatQR",
"message": "QGZRQR",
"altText": "QGZRQR",
"messageEncoding": "iso-8859-1"
}
],
"eventTicket": {
"headerFields": [
{
"key": "date",
"label": "DATE",
"value": "13 Jul"
}
],
"primaryFields": [
{
"key": "event",
"label": "EVENT",
"value": "The โง่ Hackathon ครั้งที่ 8 แห่งประเทศ Thailand"
}
],
"secondaryFields": [
{
"key": "loc",
"label": "LOCATION",
"value": "คณะวิศวกรรมศาสตร์ จุฬาลงกรณ์มหาวิทยาลัย"
}
],
"auxiliaryFields": [],
"backFields": [],
"preferredStyleSchemes": [
"posterEventTicket",
"eventTicket"
]
}
}
@rayriffy Did you try to add the venue and seats fields in semantics, like Nick wrote above? For what I understood, they are required to render the new layout...
"seats": [
{
"seatDescription": "Normal Seat",
"seatIdentifier": "112-12-16",
"seatNumber": "5",
"seatRow" : "3",
"seatSection": "100",
"venueEntranceGate": "3"
}
]
Also, I can suggest you connecting to Console.app and check if there are any logs about new things.
Let me add that someone was saying, in Apple Developers Forum, that the video was reporting the availability of some examples as downloadable resources, but it is not available under the video nor in the documentation, as opposed to the changes to Apple Pay.
I wonder if the fact this isn't working and the absence of a resource are due to the same reason, which is the lack of update in the first beta...
I used the pass.json rayriffy provided to generate a pass, renamed .pkpass to .zip, extracted it, and examined the pass.json in the generated pass. It does not include the preferredStyleSchemes field. It's like it's getting stripped out when the pass is being generated, because everything else from the original pass.json is still there.
@nickwelsh Okay interesting. Did you try with the changes of the last commit (432e3804295253fe786d213ddee192a2af985ee8) or the previous one or both?
You can tell NPM to clone a specific commit id.
That's because I changed the schemas a little bit in the last commit.
@nickwelsh fyi i did make an minor code change to reflect alex's new getter setter update from pass.props.eventTicket!.preferredStyleSchemes = [...] to pass.preferredStyleSchemes = [...]
Ah, I only had the style set in the pass.json in my model.pass. Once I explicitly added pass.preferredStyleSchemes to the js, the generated pass.json had the fields.
Ops! I forgot the possibility to import that field 😅 Last commit (dd085152515a6a2b15a84d29ee742fb9b27188ef) should include it
I proceeded adding unit tests for preferredStyleSchemes. I can confirm it gets now always added to the pass.json, wherever it comes from.
Let me know if you guys are able to generate and show a new layout event ticket.
Hi everyone! @alexandercerutti great to see you again haha, just thought I’d pop into the repo and leave a message about the iOS 18 Wallet updates but I can see you’re already on it! Great to see :)
@rayriffy mentioned the new event passes may not be on beta 1 - just wanted to confirm through a tweet I saw that they should be there (Apple’s WWDC invite pass updated on the beta), see: https://x.com/frederikriedel/status/1800253419304968439?s=46&t=mbu_2SzVSyE0jhQUv3QWWw
Will definitely be updating my backend and giving these changes a go once I’m free in about a week or so. Fingers crossed someone manages to get one generated and working in the meantime, but thanks a lot Alex, Nick and rayriffy for all of your testing so far :)
Hey @Saim-Khan1, glad you jumped on here!
So according to the tweet, the update could be already available in the first beta.
Perhaps only Venue data are required along with preferred schemas?
I tried adding venue information venueName, venueRegionName, venueLocation but doesn't work either. If WWDC pass actually proof that new layout is already added in developer beta I would like to see Apple's approach on .pkpass as well.
@rayriffy I’m asking around to folks who went to WWDC if they can share theirs with us… keep experimenting in the meantime
@rayriffy
nah doesn't work either, i will drop my
pass.jsonhere if anyone has any insights of what's wrong
I was looking again at the differences between your attempt on Simulator and your attempt on real device. On the code you show for real device, I don't see the new semantics.seats[].venueEntrance nor the whole seats structure. Perhaps that's the key?
I’m asking around to folks who went to WWDC if they can share theirs with us
Awesome, I think that would definitely go a long way – having a working example would be great!
I feel like the lack of secondaryLogo asset, could be a critical point, just like what happens with icon on different Apple OSs.