StoreKit-2-Abos in SwiftUI: ein praktischer Leitfaden

Alles, was Sie brauchen, um in einer SwiftUI-App mit StoreKit 2 ein Abo zu verkaufen, in einer kleinen Store-Klasse, plus die Paywall-Regeln, die App Review prüft.

Mustafa GüderGründer, GuderTech4 Min. Lesezeit
storekit-2-subscriptions-swiftui.md

StoreKit 2 hat In-App-Käufe von einem Delegate-Labyrinth in ein paar Zeilen asynchrones Swift verwandelt. Das ist das Setup, das wir in unseren eigenen Apps nutzen: eine kleine Store-Klasse, eine Paywall und eine Handvoll Regeln, die App Review zufriedenstellen.

1. Produkte anlegen

Legen Sie in App Store Connect eine Abo-Gruppe an und fügen Sie Ihre Pläne hinzu, zum Beispiel monatlich und jährlich. Notieren Sie die Produkt-IDs. Für lokale Tests fügen Sie in Xcode eine StoreKit Configuration File mit denselben IDs hinzu und wählen sie in Ihrem Scheme aus. Dann können Sie im Simulator ohne Sandbox-Konto kaufen, verlängern und kündigen.

2. Ein Store für die ganze App

import StoreKit

@MainActor
final class Store: ObservableObject {
    @Published private(set) var products: [Product] = []
    @Published private(set) var isPro = false

    private let ids = ["com.example.pro.monthly", "com.example.pro.yearly"]
    private var updates: Task<Void, Never>?

    init() {
        // Renewals, refunds and purchases made on other devices arrive here.
        updates = Task { [weak self] in
            for await result in Transaction.updates {
                await self?.handle(result)
            }
        }
        Task {
            await loadProducts()
            await refreshEntitlements()
        }
    }

    func loadProducts() async {
        do {
            products = try await Product.products(for: ids).sorted { $0.price < $1.price }
        } catch {
            print("Could not load products: \(error)")
        }
    }

    func buy(_ product: Product) async throws {
        switch try await product.purchase() {
        case .success(let result):
            await handle(result)
        case .userCancelled, .pending:
            break
        @unknown default:
            break
        }
    }

    func restore() async {
        try? await AppStore.sync()
        await refreshEntitlements()
    }

    func refreshEntitlements() async {
        var active = false
        for await result in Transaction.currentEntitlements {
            if case .verified(let transaction) = result, transaction.revocationDate == nil {
                active = true
            }
        }
        isPro = active
    }

    private func handle(_ result: VerificationResult<Transaction>) async {
        guard case .verified(let transaction) = result else { return }
        await transaction.finish()
        await refreshEntitlements()
    }
}

Erstellen Sie ihn einmal, etwa als @StateObject in Ihrer App, und reichen Sie ihn mit .environmentObject weiter. Beginnen Sie so früh wie möglich, Transaction.updates zu beobachten; sonst können Käufe verloren gehen, die außerhalb der App abgeschlossen wurden.

3. Warum die Prüfung wichtig ist

Jede Transaktion kommt in einem VerificationResult. StoreKit prüft Apples Signatur auf dem Gerät; .verified bedeutet also, dass der Kauf echt ist. Schalten Sie Inhalte nur für geprüfte Transaktionen frei und rufen Sie nach der Auslieferung immer finish() auf.

4. Eine Paywall in wenigen Zeilen

Ab iOS 17 liefert SubscriptionStoreView eine vollständige, App-Review-freundliche Paywall:

SubscriptionStoreView(groupID: "YOUR_GROUP_ID") {
    VStack(spacing: 8) {
        Text("Go Pro").font(.largeTitle.bold())
        Text("Unlimited exports and every template.")
    }
}
.storeButton(.visible, for: .restorePurchases)

Wenn Sie Ihre eigene gestalten, zeigen Sie Preis und Zeitraum jedes Plans mit product.displayPrice, verlinken Sie Nutzungsbedingungen und Datenschutz und fügen Sie einen Wiederherstellen-Button hinzu.

5. Gratis-Testphasen und Einführungsangebote

Einführungsangebote richten Sie in App Store Connect ein. Damit „Gratis testen“ nur Menschen sehen, die es nutzen können, prüfen Sie zuerst die Berechtigung:

let eligible = await product.subscription?.isEligibleForIntroOffer ?? false

6. Was App Review bei einer Paywall prüft

  • Preis und Abrechnungszeitraum sind vor dem Kauf klar
  • Dauer der Gratis-Testphase und was danach passiert
  • Ein funktionierender Button zum Wiederherstellen von Käufen
  • Links zu Nutzungsbedingungen und Datenschutzerklärung
  • Keine Dark Patterns, etwa ein versteckter Schließen-Button

Den Rest behandelt unsere Liste der häufigsten App-Store-Ablehnungen.

7. Serverseitig, wenn nötig

Für eine App auf einem Gerät reichen Berechtigungen auf dem Gerät aus. Melden sich Nutzer auf mehreren Plattformen an oder brauchen Sie Umsatzdaten, ergänzen Sie Ihr Backend um App Store Server Notifications V2, damit Ihr Server von Verlängerungen und Erstattungen erfährt, sobald sie passieren.

Häufige Fragen

Brauche ich mit StoreKit 2 noch eine Belegprüfung?

Nicht auf dem Gerät: StoreKit 2 prüft signierte Transaktionen für Sie. Ein Server ist nur nötig, wenn Sie Berechtigungen plattform- oder kontoübergreifend teilen.

Wie teste ich Abos, ohne zu bezahlen?

Nutzen Sie für lokale Tests eine StoreKit Configuration File in Xcode, für End-to-End-Tests ein Sandbox-Konto oder TestFlight. Sandbox-Verlängerungen laufen in beschleunigtem Takt.

Was ist die Mindestversion von iOS für StoreKit 2?

StoreKit 2 funktioniert ab iOS 15. SubscriptionStoreView und die anderen Verkaufsansichten brauchen iOS 17.

Wie viel behält Apple von Abonnements ein?

Standardmäßig 30 %, 15 % für Mitglieder des Small Business Program und 15 % auf ein Abo nach dem ersten Jahr des Abonnenten.

TeilenXLinkedInWhatsApp
Mustafa Güder

Über den Autor

Mustafa Güder

Mustafa Güder ist Gründer von GuderTech und iOS-Entwickler. Nach zwei Jahren Studium in Australien kehrte er nach Adana zurück und entwickelte Scevia, Paw & Home und Street Legends.

Studium in Australien, 2 Jahre Adana, Türkei

Alle Artikel