Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)
Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 4 Min Lesezeit
0

StoreKit 2 subscriptions + a screenshot mode that bypasses purchases

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Two things every paid iOS app needs but nobody enjoys building:




  1. A correct subscription manager (purchase, restore, entitlement checks, transaction updates).

  2. A way to capture App Store screenshots of the paid screens — without having a live sandbox purchase every time.



Here's how FishGo does both, with the second piece deliberately punching a hole through the first.






A compact StoreKit 2 manager



StoreKit 2 is async/await-native, so the whole manager fits in one observable class. We use @Observable (iOS 17+) so SwiftUI views react to isPro changes:




CODE
@Observable
final class StoreManager {
static let shared = StoreManager()

private(set) var isPro = false
private(set) var products: [Product] = []
private(set) var purchaseError: String?

private let productIDs = ["pro_monthly", "pro_yearly"]
private var transactionListener: Task<Void, Never>?
}









Loading products






CODE
func loadProducts() async {
do {
let storeProducts = try await Product.products(for: productIDs)
products = storeProducts.sorted { $0.price < $1.price }
} catch {
purchaseError = "商品情報の取得に失敗しました"
}
}









Purchasing, with verification



The important part of StoreKit 2 is that every result is a VerificationResult you must check:




CODE
func purchase(_ product: Product) async {
purchaseError = nil
do {
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
await transaction.finish()
await updatePurchaseStatus()
case .userCancelled:
break
case .pending:
purchaseError = "購入処理が保留中です"
@unknown default:
break
}
} catch {
purchaseError = "購入に失敗しました: \(error.localizedDescription)"
}
}

nonisolated private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
switch result {
case .unverified:
throw StoreError.failedVerification
case .verified(let safe):
return safe
}
}









Listening for transactions out-of-band



Purchases can arrive outside your purchase flow (Ask to Buy approvals, renewals, another device). A long-lived listener keeps isPro correct:




CODE
private func listenForTransactions() -> Task<Void, Never> {
Task.detached { [weak self] in
for await result in Transaction.updates {
if let transaction = try? self?.checkVerified(result) {
await transaction.finish()
await self?.updatePurchaseStatus()
}
}
}
}






And the source of truth for entitlement is Transaction.currentEntitlements:




CODE
private func updatePurchaseStatus() async {
var hasEntitlement = false
for await result in Transaction.currentEntitlements {
if let transaction = try? checkVerified(result),
productIDs.contains(transaction.productID) {
hasEntitlement = true
break
}
}
isPro = hasEntitlement
}









The hole: a screenshot "shot mode"



Now the App Store screenshot problem. The paywall and the Pro-only screens look best when isPro == true, but you don't want to depend on a sandbox purchase succeeding during an automated screenshot run.



So StoreManager's initializer short-circuits when a launch flag is set:




CODE
init() {
// Shot mode: pin Pro state without touching StoreKit
if ShotMode.isEnabled && ShotMode.isPro {
isPro = true
return
}
transactionListener = listenForTransactions()
Task { await updatePurchaseStatus() }
}






ShotMode is just a thin reader over launch arguments / UserDefaults:




CODE
enum ShotMode {
static var isEnabled: Bool { UserDefaults.standard.bool(forKey: "SHOT_MODE") }
static var isPro: Bool { UserDefaults.standard.bool(forKey: "SHOT_PRO") }
}






Run the UI test with -SHOT_MODE 1 -SHOT_PRO 1 and the app boots straight into a deterministic Pro state — no network, no store, no flaky purchase. Combined with mocked forecast data, every screenshot is identical run-to-run.






Pitfalls





  • Always finish() a verified transaction. Unfinished transactions get replayed forever via Transaction.updates.


  • @unknown default is mandatory on the purchase-result switch — StoreKit can add cases.


  • Keep the shot-mode bypass narrow. It only forces isPro; it never fakes a real Transaction. The real verification path stays untouched for actual users.


  • Guard the bypass behind a launch argument, not a build flag you might ship. It only activates when explicitly passed at launch.






Takeaways




  • StoreKit 2 lets you write a full subscription manager in ~100 lines with async/await + @Observable.

  • Check VerificationResult everywhere; trust nothing unverified.

  • Drive entitlement from Transaction.currentEntitlements and keep a Transaction.updates listener alive.

  • A tiny launch-flag bypass makes paid-screen screenshots deterministic — as long as it stays scoped to UI state, not real receipts.






FishGo is on the App Store: https://apps.apple.com/app/id6774428559

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
Use custom web fonts in Google Sheets charts
2 Quellen
Introducing the new 1Password App for Google Chat
1 Quelle
Context-aware access controls are available for Gemini Enterprise in the Admin console
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten StoreKit 2 subscriptions + a screenshot mode that bypasses purchases

Thematisch verwandte Begriffe: StoreKit, subscriptions, screenshot, mode · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...