Lanework Pro is a subscription — the tier seam, StoreKit 2, and Settings
Phase 3 of the one-app pivot (DESIGN 12 ▸ The entitlement / Distribution, ruled 2026-07-30; card c3a3ddd5). New Kanban/Tier/: Tier (.free/.pro — deliberately no .lapsed case; unsubscribed and lapsed are one state) and the pure decision Tier.resolve(from:now:) over SubscriptionFacts (expiration + willAutoRenew), unit-tested through all five named states: free, active, lapsed, offline-grace, never-online. The facts are a persisted cache (standard defaults), not a live view: StoreKit ages an expired subscription out of currentEntitlements locally, so an offline device and a real lapse are indistinguishable from that property alone — the cache holds the last answer, empty entitlements read as silence, and holds end only on a definitive answer (revocation, or the subscription-group status read Settings performs). That is 12's offline-grace trade, resolved toward the paying user. ProEntitlement is the local adapter (currentEntitlements + Transaction.updates, started from launch, never from a test host); ProStorefront holds everything networked (product load, purchase, AppStore.sync) and only the Settings section ever constructs one — the split is the enforcement of "never network on the open path". beginSession reads the tier once at composition; BoardSession.tier is a let with no path back in, so a lapse never rebinds an open session. makeHistoryProvider now takes the tier; both tiers bind the native stack until pro-m1 builds the git provider — the seam's consumer is named, not invented early. Settings gains the Pro section (subscribe with localized price, manage, restore; a quiet unreachable line, no indefinite spinner) — the third of the exactly-three Pro mentions; the About line gains its "…in Settings" pointer now that there is a Settings to point at. A successful purchase or restore offers once to reopen open boards (close + reopen through the ordinary paths). Configuration.storekit wired into the scheme's run action for ASC-free exercise; RELEASE.md gains the pro-m1 store-side steps and the rule that the product must not be configured before then. 1901 tests in 319 suites green. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
+21
@@ -66,6 +66,18 @@ targets:
|
||||
- path: CHANGELOG.md
|
||||
type: file
|
||||
buildPhase: resources
|
||||
# The local StoreKit configuration (12-editions.md ▸ Distribution) — the subscription group
|
||||
# "Lanework Pro" and its monthly product, mirroring what App Store Connect declares, so the
|
||||
# Settings Pro section's subscribe/manage/restore flow is exercisable in Xcode without an ASC
|
||||
# product existing yet. Attached to the scheme's run action below.
|
||||
#
|
||||
# **`buildPhase: none`, deliberately.** It is development configuration, not app content: the
|
||||
# scheme reads it, the shipped bundle must never carry it. Listed in the project anyway, for
|
||||
# the same reason `KanbanUITests`' verification documents are — visible where it belongs,
|
||||
# built into nothing.
|
||||
- path: Configuration.storekit
|
||||
type: file
|
||||
buildPhase: none
|
||||
dependencies:
|
||||
- package: Yams
|
||||
- package: swift-markdown
|
||||
@@ -160,6 +172,15 @@ schemes:
|
||||
Kanban: all
|
||||
run:
|
||||
config: Debug
|
||||
# StoreKit Testing in Xcode (12-editions.md ▸ Distribution): a ⌘R run resolves
|
||||
# `dev.rzen.indie.kanban.pro.monthly` against the local `Configuration.storekit` instead of
|
||||
# against the App Store, so the Settings Pro section shows a price and its purchase flow
|
||||
# completes on a machine with no App Store Connect product and no sandbox account.
|
||||
#
|
||||
# It changes nothing about a build or an archive — it is a *run action* setting, so `xcodebuild
|
||||
# build`, `xcodebuild test` and `scripts/release.sh` are untouched, and a shipped app resolves
|
||||
# the real product or reports that it cannot reach the App Store.
|
||||
storeKitConfiguration: Configuration.storekit
|
||||
test:
|
||||
config: Debug
|
||||
gatherCoverageData: false
|
||||
|
||||
Reference in New Issue
Block a user