Repository navigation
The Trial is judged against the newest App Store date seen, not only the iPhone's clock (#936) - #965
Merged
Merged
Conversation
…the iPhone's clock (#936) Assisted-by: Claude Code:claude-sonnet-5-5
Skeyelab
enabled auto-merge (squash)
October 27, 2026 02:42
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
3 tasks
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.
Fixes #936 (code part; the device check stays open).
What
ProStorekeeps the newestsignedDatefrom a verified StoreKit entitlement or transaction update, saves it in UserDefaults (so it survives a relaunch), and passesmax(Date(), newest)asnowto the license. A clock held still or set back can no longer keep the Trial alive once the App Store has signed a later date.Entitlement.tieris unchanged, so the #935 rules hold (anowmore than a day before the start reads Free; days left are capped at 14).New
SignedClockinRecording(so it's testable without StoreKit):observe(an older date never replaces a newer one),now(clock:), and save/load. Only a verified result's date counts, since an unverified one could be forged.ProStore(status:)(tests) starts with an emptySignedClock, so a test doesn't depend on the device's saved date.Does
signedDateadvance on cached entitlements offline?Found, from Apple's docs (not measured on a device):
signedDateis "the date that the App Store signed the JWS transaction". A cached entitlement is the same signed value, so it keeps its old date while the phone is offline; it moves only when the App Store hands back a fresh one. I could not run StoreKit offline here.So the fix helps once the phone has been online after the clock was held. A phone kept offline the whole time with the date held is not caught, and I did not try to work around that (no network time call, per the brief).
Tests (red first)
SignedClockTests. Red run against a stub that ignored the signed date: 3 of 5 failed (held clock at day 2 with a signed day 20 still read as a Trial; an older date replaced nothing, a newer one was not kept; the saved date was not read back). The other 2 (no signed date seen; clock ahead of the signed date) passed on the first run, since they describe today's behavior. After the change all 5 pass.Full
./scripts/verify.shpasses (App/ changed).Checked only by tests / reading
The
ProStorewiring (where the date is read and written) is checked by reading and by the build, not by a test: it needs a StoreKit seam I did not add. Device check still open for Eric: with automatic time off and the date held, the Trial still ends 14 days after it started, after the phone has been online once.