[PR-26997] Support Apple poster pass styles - #5
Open
ilovepinkpony1 wants to merge 4 commits into
Open
Conversation
Emit a poster style dictionary (posterGeneric, iOS 27+) alongside the classic one so a single pass renders everywhere: devices that know the poster key use it, older ones ignore it and fall back to pass_type. Poster layouts drop the secondary/auxiliary rows for a single footer field, so the dictionary carries header, footer and back fields only. Also adds featured_actions (PR-26998) to the pass surface, and fixes the boardingPass merge silently discarding its result.
The posterGeneric layout renders header -> barcode -> primary -> footer, but the poster dictionary only carried header/footer/back, so poster passes drew an empty primary row. Add a poster_primary_fields hook (defaulting to the classic primary_fields) and emit it. The classic and poster dictionaries ship in the same pass.json, so one accessor cannot serve both slots -- the Talkable app maps its secondary_fields to the poster primaries. Also fix the featured_actions doc example: "link" is not one of Apple's featured action types. Tests move to test/test_generator_poster.rb / TestGeneratorPoster so they no longer collide with the test/test_generator.rb added by PR #4, and the Rails.root stub is now guarded so it cannot repoint the dummy app that the controller tests boot in the same rake process.
Add a 0.8.0 changelog entry. It names the poster hooks and the boarding_pass fix, which is a behaviour change: those keys used to be merged into a discarded copy of the boardingPass dictionary and never reached pass.json. Test that fix, and test that poster_primary_fields falls back to primary_fields -- PosterCard overrides the hook, so the default was uncovered. Name posterGeneric as the only poster style Wallet takes as a top-level key. posterEventTicket is a preferredStyleSchemes entry, not a style dictionary.
Every other optional key in pass_json uses a plain truthy check. This one called the hook twice and used .any?, which raises NoMethodError when a subclass returns nil instead of the [] default. present? covers both nil and [], so the default still emits no key.
zhuravel
approved these changes
Aug 27, 2026
zhuravel
left a comment
Member
There was a problem hiding this comment.
No problems found in this PR.
Reviewed main@6c0174d → vo-PR-26997@122af06 only.
Checks
ruby -Itest test/test_generator_poster.rb— 8 runs and 16 assertions passed.- Ruby syntax — five changed Ruby files passed.
- Full suite — not run:
sqlite3 ~> 1.4is not installed. - StandardRB — not run: the
standardgem is not installed. - Apple Wallet docs — poster and featured action schemas matched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds opt-in support for the iOS 27 Apple Wallet poster styles, and fixes
boarding_passkeys never reachingpass.json.Problem
The gem can only emit the classic pass layouts (
storeCard,coupon,eventTicket,generic,boardingPass). iOS 27 adds the poster styles, which use a different layout: logo -> logo text -> header fields -> background image -> barcode -> primary fields -> footer fields. There is no secondary or auxiliary row, and there is a footer row the classic layouts do not have. Wallet also renders up to two action buttons on the face of the pass from a top-levelfeaturedActionskey.boarding_passwas dead code. The old line waspass[:boardingPass].merge(@pass.boarding_pass)—Hash#mergereturns a new hash and the result was thrown away, so keys liketransitTypenever reachedpass.json.Solution
Four new hooks on
Passkit::BasePass, all with safe defaults so nothing changes for existing passes:poster_pass_typenil:posterGenericposter_primary_fieldsprimary_fieldsfooter_fields[]featured_actions[]A pass that sets
poster_pass_typeemits both style dictionaries in onepass.json. This is Apple's own backward-compatibility pattern: older iOS does not know theposterGenerickey, ignores it, and renders the classic dictionary instead. One pass file works on every device.poster_primary_fieldsis a separate hook rather than reusingprimary_fieldsbecause both dictionaries ship in the same file and the two slots hold different content — the classic layout puts primary fields above the barcode, the poster below it. It defaults toprimary_fieldsso passes that want the same content in both get it for free.posterGenericis the only poster style Wallet accepts as a top-level key.posterEventTicketis apreferredStyleSchemesarray entry, not a style dictionary.The boarding pass fix assigns the merge result back:
pass[:boardingPass] = pass[:boardingPass].merge(@pass.boarding_pass).generate_json_passis split into a one-line file writer plus apass_jsonbuilder so the payload can be tested without a signing certificate or a booted app.Version bumped to 0.8.0.
Demo
https://admin.bastion.talkable.com/
Screenshots
Checklist
Related Stories
Test Plan
test/test_generator_poster.rb— 7 tests, 15 assertions, all green:Coverage:
poster_primary_fieldsfalls back toprimary_fieldswhen a pass does not override itfeaturedActionsis emittedposterGeneric, nofeaturedActions)boarding_passkeys are merged into theboardingPassdictionary without wiping the generated fieldsBoth fixes were reverted locally to confirm the tests fail without them — 4 failures, then green again after restoring.
bundle exec standardrbadds no new offenses (42 before this branch, 42 after).bundle exec rakecannot run on this machine: it needs a.env(the repo ships.example.env), and even with the env vars set there is a pre-existing gem conflict —can't activate sqlite3 (>= 2.1), already activated sqlite3-1.7.3. Both failures reproduce onmain.Files Changed
lib/passkit/base_pass.rb— the four new hooks with defaultslib/passkit/generator.rb— emit the poster dictionary andfeaturedActions; fix the boarding pass merge; split outpass_jsonapp/models/passkit/pass.rb— delegate the four new hookslib/passkit/version.rb— 0.7.0 -> 0.8.0CHANGELOG.md— 0.8.0 entrytest/test_generator_poster.rb— new