StoreKit 2 Interview: Abonnementverwaltung und Beleg-Validierung

Beherrschen Sie iOS-Interview-Fragen zu StoreKit 2, Abonnementverwaltung, Beleg-Validierung und In-App-Kauf-Implementierung mit praktischen Swift-Codebeispielen.

iOS StoreKit 2 Abonnement-Architektur und Beleg-Validierung

StoreKit 2 stellt einen grundlegenden Wandel in der Handhabung von In-App-Käufen unter iOS dar. Mit iOS 15 eingeführt, vereinfacht dieses moderne Framework den Code für Abonnementverwaltung und Transaktionsvalidierung drastisch. Mit iOS 18 ist StoreKit 1 nun offiziell deprecated, sodass StoreKit 2 der einzige unterstützte Weg nach vorn ist. Technische iOS-Interviews behandeln dieses Thema regelmäßig, um die Expertise von Entwicklern in der App-Monetarisierung zu bewerten.

Wichtige Interview-Erkenntnis

StoreKit 2 nutzt eine native Swift-API mit async/await und macht komplexe Callbacks und clientseitige Beleg-Validierung überflüssig. Interviewer schätzen Kandidaten, die die grundlegenden Unterschiede zu StoreKit 1 klar artikulieren können sowie die neuen iOS 18.4-Features wie appTransactionID und SubscriptionOfferView.

Überblick über die StoreKit 2-Architektur

StoreKit 2 basiert auf einer modernen Architektur, die Swift Concurrency vollständig nutzt. Im Gegensatz zu StoreKit 1 sind alle Operationen asynchron und verwenden native Swift-Typen.

Verfügbare Produkte abrufen

Der erste Schritt besteht darin, die in App Store Connect konfigurierten Produkte zu laden. StoreKit 2 vereinfacht diese Operation mit einer deklarativen API.

ProductService.swiftswift
import StoreKit

actor ProductService {

    // Product identifiers configured in App Store Connect
    private let productIdentifiers: Set<String> = [
        "com.app.subscription.monthly",
        "com.app.subscription.yearly",
        "com.app.premium.lifetime"
    ]

    // Product cache to avoid repeated network calls
    private var cachedProducts: [Product] = []

    func loadProducts() async throws -> [Product] {
        // Return cache if available
        guard cachedProducts.isEmpty else {
            return cachedProducts
        }

        // Asynchronous request to the App Store
        let products = try await Product.products(for: productIdentifiers)
        cachedProducts = products
        return products
    }

    func product(for identifier: String) async throws -> Product? {
        let products = try await loadProducts()
        return products.first { $0.id == identifier }
    }
}

Die Verwendung eines actor gewährleistet Thread-Sicherheit bei der Verwaltung des Produkt-Caches.

StoreKit 2-Produkttypen

StoreKit 2 unterscheidet klar zwischen verschiedenen Produkttypen. Das Verständnis dieser Unterscheidung ist für Interviews unerlässlich.

ProductType+Extensions.swiftswift
import StoreKit

extension Product.ProductType {

    var displayName: String {
        switch self {
        case .consumable:
            // Consumable products (credits, lives, etc.)
            return "Consumable"
        case .nonConsumable:
            // Permanent purchases (feature unlock)
            return "Non-Consumable"
        case .autoRenewable:
            // Subscriptions with automatic renewal
            return "Auto-Renewable Subscription"
        case .nonRenewable:
            // Subscriptions without renewal (temporary pass)
            return "Non-Renewable Subscription"
        @unknown default:
            return "Unknown"
        }
    }
}

Automatisch verlängernde Abonnements stellen das dominante Geschäftsmodell für moderne iOS-Anwendungen dar.

Kauftransaktionen verarbeiten

Der Kaufprozess in StoreKit 2 wird dank async/await linear. Schluss mit Delegates und verschachtelten Callbacks aus StoreKit 1.

Einen Kauf einleiten

Ab iOS 18.2 erfordern Käufe einen UI-Kontext-Parameter. Diese Änderung stellt sicher, dass das Kauf-Sheet korrekt in der View-Hierarchie der App erscheint.

PurchaseManager.swiftswift
import StoreKit
import UIKit

actor PurchaseManager {

    enum PurchaseError: Error {
        case productNotFound
        case purchaseCancelled
        case purchasePending
        case verificationFailed
        case unknown
    }

    func purchase(_ product: Product, in scene: UIWindowScene) async throws -> Transaction {
        // Initiate purchase with UI context (iOS 18.2+)
        let result = try await product.purchase(confirmIn: scene)

        switch result {
        case .success(let verification):
            // Verify the transaction signature
            let transaction = try checkVerification(verification)

            // Mark the transaction as finished
            await transaction.finish()

            return transaction

        case .userCancelled:
            // User cancelled the purchase modal
            throw PurchaseError.purchaseCancelled

        case .pending:
            // Purchase pending (parental approval, etc.)
            throw PurchaseError.purchasePending

        @unknown default:
            throw PurchaseError.unknown
        }
    }

    private func checkVerification<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
        case .verified(let safe):
            // Transaction signed and verified by the App Store
            return safe
        case .unverified(_, let error):
            // Invalid or corrupted signature
            throw PurchaseError.verificationFailed
        }
    }
}

Die Methode finish() ist entscheidend: Sie signalisiert dem App Store, dass der Inhalt an den Benutzer ausgeliefert wurde.

Interview-Warnung

Vergessen Sie niemals, transaction.finish() nach der Inhaltsauslieferung aufzurufen. Eine nicht abgeschlossene Transaktion wird beim nächsten App-Start wiederhergestellt und verursacht unerwartetes Verhalten.

Hintergrundtransaktionen abhören

StoreKit 2 führt Transaction.updates ein, einen asynchronen Stream, der Transaktionen ausgibt, die außerhalb der App stattfinden (Verlängerungen, Family-Sharing-Käufe usw.).

TransactionObserver.swiftswift
import StoreKit

actor TransactionObserver {

    private var updateTask: Task<Void, Never>?

    func startObserving() {
        // Cancel any previous observation
        updateTask?.cancel()

        updateTask = Task(priority: .background) {
            // Infinite stream of transaction updates
            for await result in Transaction.updates {
                do {
                    let transaction = try self.checkVerification(result)

                    // Process the transaction based on its type
                    await self.handleTransaction(transaction)

                    // Always finish the transaction
                    await transaction.finish()
                } catch {
                    // Log the error for debugging
                    print("Transaction verification failed: (error)")
                }
            }
        }
    }

    func stopObserving() {
        updateTask?.cancel()
        updateTask = nil
    }

    private func handleTransaction(_ transaction: Transaction) async {
        switch transaction.productType {
        case .autoRenewable:
            // Update subscription status
            await updateSubscriptionStatus(transaction)
        case .nonConsumable:
            // Unlock premium feature
            await unlockFeature(transaction.productID)
        default:
            break
        }
    }

    private func checkVerification<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
        case .verified(let safe):
            return safe
        case .unverified:
            throw PurchaseError.verificationFailed
        }
    }

    private func updateSubscriptionStatus(_ transaction: Transaction) async {
        // Implementation of status update
    }

    private func unlockFeature(_ productID: String) async {
        // Implementation of feature unlock
    }

    enum PurchaseError: Error {
        case verificationFailed
    }
}

Die Transaktionsbeobachtung sollte unmittelbar beim App-Start beginnen, um keine Aktualisierungen zu verpassen.

Häufige Interview-Fragen zu StoreKit 2-Abonnements

iOS-Interviews enthalten oft spezifische Fragen zur Abonnementverwaltung. Hier sind die häufigsten mit detaillierten Antworten. Für weitere Swift-Interview-Themen siehe den Swift Structured Concurrency Guide.

Frage 1: Wie überprüft man den aktuellen Abonnementstatus?

StoreKit 2 bietet direkten Zugriff auf aktive Berechtigungen. Beachten Sie, dass Transaction.currentEntitlement(for:) seit iOS 18.4 deprecated ist. Die neue currentEntitlements(for:) gibt eine asynchrone Sequenz zurück, um mehrere Berechtigungen zu verarbeiten (z.B. Abonnement plus Family-Sharing-Zugriff).

SubscriptionStatusChecker.swiftswift
import StoreKit

struct SubscriptionStatusChecker {

    struct SubscriptionStatus {
        let isActive: Bool
        let expirationDate: Date?
        let willRenew: Bool
        let productID: String?
        let appTransactionID: UInt64?  // iOS 18.4+
    }

    static func checkCurrentStatus(for productID: String) async -> SubscriptionStatus {
        // Use the new currentEntitlements(for:) API (iOS 18.4+)
        var latestTransaction: Transaction?

        for await result in Transaction.currentEntitlements(for: productID) {
            if case .verified(let transaction) = result {
                if transaction.productType == .autoRenewable {
                    if latestTransaction == nil ||
                       transaction.purchaseDate > latestTransaction!.purchaseDate {
                        latestTransaction = transaction
                    }
                }
            }
        }

        guard let transaction = latestTransaction else {
            return SubscriptionStatus(
                isActive: false,
                expirationDate: nil,
                willRenew: false,
                productID: nil,
                appTransactionID: nil
            )
        }

        // Check if the subscription is still active
        let isActive = transaction.expirationDate ?? Date.distantPast > Date()

        // Retrieve renewal information
        let renewalInfo = await getRenewalInfo(for: transaction.productID)

        return SubscriptionStatus(
            isActive: isActive,
            expirationDate: transaction.expirationDate,
            willRenew: renewalInfo?.willAutoRenew ?? false,
            productID: transaction.productID,
            appTransactionID: transaction.appTransactionID
        )
    }

    private static func getRenewalInfo(for productID: String) async -> Product.SubscriptionInfo.RenewalInfo? {
        guard let product = try? await Product.products(for: [productID]).first,
              let subscription = product.subscription else {
            return nil
        }

        // Retrieve renewal status
        let status = try? await subscription.status.first
        return status?.renewalInfo
    }
}

Dieser Ansatz liefert den genauen Abonnementstatus ohne Serveraufrufe. Das appTransactionID-Feld, eingeführt in iOS 18.4, ermöglicht das Tracking von Kunden über Transaktionen hinweg ohne Server-zu-Server-Aufrufe.

Bereit für deine iOS-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

Frage 2: Wie verarbeitet man kostenlose Testphasen und Werbeangebote?

StoreKit 2 vereinfacht den Zugriff auf Angebotsinformationen. Interviewer testen häufig das Verständnis der verschiedenen Angebotstypen. Seit iOS 18.4 können Werbeangebote mit JWS über den neuen PromotionOfferV2SignatureCreator signiert werden, verfügbar in der App Store Server Library.

OfferManager.swiftswift
import StoreKit

struct OfferManager {

    enum OfferType {
        case freeTrial(duration: String)
        case payAsYouGo(periods: Int, price: Decimal)
        case payUpFront(duration: String, price: Decimal)
        case oneTime(price: Decimal)  // iOS 17.2+: offer codes for all product types
        case none
    }

    static func getIntroductoryOffer(for product: Product) -> OfferType {
        guard let subscription = product.subscription,
              let offer = subscription.introductoryOffer else {
            return .none
        }

        switch offer.paymentMode {
        case .freeTrial:
            // Free trial period
            return .freeTrial(duration: formatPeriod(offer.period))

        case .payAsYouGo:
            // Reduced price over multiple periods
            return .payAsYouGo(
                periods: offer.periodCount,
                price: offer.price
            )

        case .payUpFront:
            // Single payment for a period
            return .payUpFront(
                duration: formatPeriod(offer.period),
                price: offer.price
            )

        case .oneTime:
            // One-time offer code (consumables, non-consumables, non-renewing)
            return .oneTime(price: offer.price)

        @unknown default:
            return .none
        }
    }

    static func checkEligibility(for product: Product) async -> Bool {
        // Check if user can benefit from the offer
        guard let subscription = product.subscription else {
            return false
        }

        // isEligibleForIntroOffer indicates if this is a new subscriber
        return await subscription.isEligibleForIntroOffer
    }

    private static func formatPeriod(_ period: Product.SubscriptionPeriod) -> String {
        let value = period.value
        switch period.unit {
        case .day:
            return "(value) day(value > 1 ? "s" : "")"
        case .week:
            return "(value) week(value > 1 ? "s" : "")"
        case .month:
            return "(value) month(value > 1 ? "s" : "")"
        case .year:
            return "(value) year(value > 1 ? "s" : "")"
        @unknown default:
            return ""
        }
    }
}

Die Methode isEligibleForIntroOffer ist unerlässlich, um die Anzeige nicht verfügbarer Angebote zu vermeiden. Beachten Sie, dass Angebotscodes, die zuvor auf automatisch verlängernde Abonnements beschränkt waren, seit iOS 16.3 nun auch Verbrauchsprodukte, Nicht-Verbrauchsprodukte und nicht verlängernde Abonnements unterstützen.

Frage 3: Wie stellt man frühere Käufe wieder her?

Die Wiederherstellung von Käufen ist gemäß den App Store Review Guidelines eine obligatorische Funktion.

RestoreManager.swiftswift
import StoreKit

actor RestoreManager {

    struct RestoreResult {
        let restoredProducts: [String]
        let activeSubscriptions: [String]
    }

    func restorePurchases() async throws -> RestoreResult {
        var restoredProducts: [String] = []
        var activeSubscriptions: [String] = []

        // Synchronize with the App Store
        try await AppStore.sync()

        // Iterate through all user transactions
        for await result in Transaction.currentEntitlements {
            guard case .verified(let transaction) = result else {
                continue
            }

            switch transaction.productType {
            case .nonConsumable:
                // Restore permanent purchases
                restoredProducts.append(transaction.productID)

            case .autoRenewable:
                // Check if subscription is active
                if let expiration = transaction.expirationDate,
                   expiration > Date() {
                    activeSubscriptions.append(transaction.productID)
                }

            default:
                // Consumables are not restorable
                break
            }
        }

        return RestoreResult(
            restoredProducts: restoredProducts,
            activeSubscriptions: activeSubscriptions
        )
    }
}

AppStore.sync() erzwingt die Synchronisation mit Apples Servern, nützlich nach einer Neuinstallation.

SubscriptionOfferView: Modernes Abonnement-Merchandising

iOS 18.4 führt SubscriptionOfferView ein, eine SwiftUI-Komponente, die Abonnementoptionen mit automatischem Laden von Produktmetadaten anzeigt. Dies ist ein häufiges Interview-Thema für Entwickler, die abonnementbasierte Apps erstellen.

SubscriptionOfferViewExample.swiftswift
import SwiftUI
import StoreKit

struct SubscriptionSettingsView: View {
    let subscriptionGroupID = "com.app.subscription.group"
    
    var body: some View {
        VStack(spacing: 16) {
            // Show upgrade options for current subscribers
            SubscriptionOfferView(groupID: subscriptionGroupID)
                .visibleRelationship(.upgrade)
                .prefersPromotionalIcon(true)
                .subscriptionOfferViewDetailAction { path in
                    path.append("subscriptionDetail")
                }
            
            // Show current plan details
            SubscriptionOfferView(groupID: subscriptionGroupID)
                .visibleRelationship(.current)
        }
        .padding()
    }
}

Der Parameter visibleRelationship steuert, welche Abonnementstufen angezeigt werden: .upgrade zeigt Pläne eine Stufe höher, .downgrade zeigt niedrigere Stufen, .crossgrade zeigt gleichwertige Stufen mit besserem Wert, und .all zeigt jeden Plan in der Gruppe an.

Serverseitige Beleg-Validierung

Die serverseitige Validierung bleibt für Anwendungen mit kritischen Abonnements empfohlen. StoreKit 2 verwendet JWS (JSON Web Signatures) für moderne Validierung. Der deprecated verifyReceipt-Endpunkt wurde durch die App Store Server API ersetzt.

Transaktionsdaten für den Server extrahieren

ServerValidation.swiftswift
import StoreKit

struct TransactionPayload: Codable {
    let transactionID: String
    let originalTransactionID: String
    let productID: String
    let purchaseDate: Date
    let expirationDate: Date?
    let jwsRepresentation: String
    let appTransactionID: String?  // iOS 18.4+
}

actor ServerValidationService {

    private let apiEndpoint = "https://api.example.com/verify"

    func validateWithServer(_ transaction: Transaction) async throws -> Bool {
        // Create payload for the server
        let payload = TransactionPayload(
            transactionID: String(transaction.id),
            originalTransactionID: String(transaction.originalID),
            productID: transaction.productID,
            purchaseDate: transaction.purchaseDate,
            expirationDate: transaction.expirationDate,
            jwsRepresentation: transaction.jwsRepresentation ?? "",
            appTransactionID: transaction.appTransactionID.map { String($0) }
        )

        // Encode to JSON
        let encoder = JSONEncoder()
        encoder.dateEncodingStrategy = .iso8601
        let jsonData = try encoder.encode(payload)

        // Send to server
        var request = URLRequest(url: URL(string: apiEndpoint)!)
        request.httpMethod = "POST"
        request.httpBody = jsonData
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        let (data, response) = try await URLSession.shared.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            return false
        }

        // Decode server response
        let validationResponse = try JSONDecoder().decode(
            ValidationResponse.self,
            from: data
        )

        return validationResponse.isValid
    }
}

struct ValidationResponse: Codable {
    let isValid: Bool
    let subscriptionStatus: String?
}

Die jwsRepresentation enthält die kryptografische Signatur, die vom Server überprüfbar ist. Die neue appTransactionID ermöglicht präzises Kunden-Tracking ohne zusätzliche Server-Roundtrips.

App Store Server API

Apples App Store Server API bietet serverseitige Validierung mit JWS-Tokens, Server Notifications V2 für Echtzeit-Abonnement-Ereignisse und die neue SubscriptionStatus API, die Transaction IDs akzeptiert (iOS 18.4+) für direkte Statusabfragen.

Server-Benachrichtigungen konfigurieren

Server Notifications V2 ermöglichen den Empfang von Ereignissen in Echtzeit.

ServerNotificationTypes.swiftswift
enum NotificationType: String, Codable {
    case subscribed = "SUBSCRIBED"
    case didRenew = "DID_RENEW"
    case didFailToRenew = "DID_FAIL_TO_RENEW"
    case didChangeRenewalStatus = "DID_CHANGE_RENEWAL_STATUS"
    case expired = "EXPIRED"
    case gracePeriodExpired = "GRACE_PERIOD_EXPIRED"
    case offerRedeemed = "OFFER_REDEEMED"
    case priceIncrease = "PRICE_INCREASE"
    case refund = "REFUND"
    case renewalExtended = "RENEWAL_EXTENDED"
    case revoke = "REVOKE"
}

struct ServerNotification: Codable {
    let notificationType: NotificationType
    let subtype: String?
    let data: NotificationData

    struct NotificationData: Codable {
        let bundleId: String
        let environment: String
        let signedTransactionInfo: String
        let signedRenewalInfo: String?
    }
}

Diese Benachrichtigungen ermöglichen die Synchronisation des Abonnementstatus mit dem Backend.

Fehlerbehandlung und Randfälle

Interviews testen häufig das Verständnis von Fehlerszenarien und deren ordnungsgemäße Behandlung. Für mehr zu iOS-Architekturmustern siehe den iOS Senior Interview Guide zu Design Patterns.

Häufige StoreKit 2-Fehler

PurchaseErrorHandler.swiftswift
import StoreKit

struct PurchaseErrorHandler {

    enum UserFacingError {
        case networkError
        case paymentFailed
        case notAuthorized
        case productNotAvailable
        case unknown

        var message: String {
            switch self {
            case .networkError:
                return "Connection failed. Please check your internet connection."
            case .paymentFailed:
                return "Payment failed. Please verify your payment information."
            case .notAuthorized:
                return "In-app purchases are disabled on this device."
            case .productNotAvailable:
                return "This product is not currently available."
            case .unknown:
                return "An unexpected error occurred."
            }
        }
    }

    static func handle(_ error: Error) -> UserFacingError {
        // Check if it's a StoreKit error
        if let storeKitError = error as? StoreKitError {
            switch storeKitError {
            case .networkError:
                return .networkError
            case .userCancelled:
                // Don't show error, user cancelled
                return .unknown
            case .notAvailableInStorefront:
                return .productNotAvailable
            case .notEntitled:
                return .notAuthorized
            default:
                return .unknown
            }
        }

        // Purchase errors
        if let purchaseError = error as? Product.PurchaseError {
            switch purchaseError {
            case .invalidQuantity:
                return .unknown
            case .productUnavailable:
                return .productNotAvailable
            case .purchaseNotAllowed:
                return .notAuthorized
            default:
                return .unknown
            }
        }

        return .unknown
    }
}

Klare Fehlerbehandlung verbessert die Benutzererfahrung und vereinfacht das Debugging.

Offline-Modus-Unterstützung

OfflineCapabilityManager.swiftswift
import StoreKit

actor OfflineCapabilityManager {

    private let userDefaults = UserDefaults.standard
    private let entitlementsKey = "cached_entitlements"

    func cacheCurrentEntitlements() async {
        var entitlements: [String: Date] = [:]

        for await result in Transaction.currentEntitlements {
            if case .verified(let transaction) = result {
                entitlements[transaction.productID] = transaction.expirationDate
            }
        }

        // Save locally
        if let data = try? JSONEncoder().encode(entitlements) {
            userDefaults.set(data, forKey: entitlementsKey)
        }
    }

    func getCachedEntitlements() -> [String: Date] {
        guard let data = userDefaults.data(forKey: entitlementsKey),
              let entitlements = try? JSONDecoder().decode(
                  [String: Date].self,
                  from: data
              ) else {
            return [:]
        }

        // Filter expired entitlements
        let now = Date()
        return entitlements.filter { _, expiration in
            expiration > now
        }
    }

    func hasValidEntitlement(for productID: String) -> Bool {
        let cached = getCachedEntitlements()
        return cached[productID] != nil
    }
}

Lokales Caching erhält den Zugriff auf Premium-Funktionen auch ohne Konnektivität.

Best Practices für die Produktion

Empfohlene Architektur

SubscriptionService.swiftswift
import StoreKit
import Combine

@MainActor
final class SubscriptionService: ObservableObject {

    static let shared = SubscriptionService()

    @Published private(set) var products: [Product] = []
    @Published private(set) var purchasedProductIDs: Set<String> = []
    @Published private(set) var subscriptionStatus: SubscriptionStatus = .none

    private var transactionObserver: Task<Void, Never>?

    enum SubscriptionStatus {
        case none
        case active(expirationDate: Date)
        case expired
        case inGracePeriod
    }

    private init() {
        // Start transaction observation
        startTransactionObserver()

        // Load initial state
        Task {
            await loadProducts()
            await updateSubscriptionStatus()
        }
    }

    private func startTransactionObserver() {
        transactionObserver = Task.detached(priority: .background) {
            for await result in Transaction.updates {
                if case .verified(let transaction) = result {
                    await transaction.finish()
                    await self.updateSubscriptionStatus()
                }
            }
        }
    }

    func loadProducts() async {
        do {
            let productIDs = [
                "com.app.subscription.monthly",
                "com.app.subscription.yearly"
            ]
            products = try await Product.products(for: productIDs)
        } catch {
            print("Failed to load products: (error)")
        }
    }

    func purchase(_ product: Product) async throws {
        let result = try await product.purchase()

        switch result {
        case .success(let verification):
            if case .verified(let transaction) = verification {
                await transaction.finish()
                await updateSubscriptionStatus()
            }
        case .userCancelled:
            break
        case .pending:
            break
        @unknown default:
            break
        }
    }

    func updateSubscriptionStatus() async {
        var purchasedIDs: Set<String> = []
        var latestSubscription: Transaction?

        for await result in Transaction.currentEntitlements {
            if case .verified(let transaction) = result {
                purchasedIDs.insert(transaction.productID)

                if transaction.productType == .autoRenewable {
                    if latestSubscription == nil ||
                       transaction.purchaseDate > latestSubscription!.purchaseDate {
                        latestSubscription = transaction
                    }
                }
            }
        }

        purchasedProductIDs = purchasedIDs

        if let subscription = latestSubscription,
           let expiration = subscription.expirationDate {
            if expiration > Date() {
                subscriptionStatus = .active(expirationDate: expiration)
            } else {
                subscriptionStatus = .expired
            }
        } else {
            subscriptionStatus = .none
        }
    }

    deinit {
        transactionObserver?.cancel()
    }
}

Diese zentralisierte Architektur vereinfacht die Verwaltung des Kaufstatus in der gesamten Anwendung.

Quellen

Wichtige Erkenntnisse für StoreKit 2-Interviews

StoreKit 2 verwandelt die Verwaltung von In-App-Käufen in eine moderne und sichere Entwicklungserfahrung. Wichtige Punkte für ein Interview:

  • Native async/await-API eliminiert die komplexen Delegates und Callbacks aus StoreKit 1, das seit iOS 18 deprecated ist
  • Automatische Verifizierung durch VerificationResult übernimmt die kryptografische Transaktionsvalidierung
  • Transaction.updates bietet einen asynchronen Stream für Hintergrundtransaktionen einschließlich Verlängerungen und Family Sharing
  • Transaction.currentEntitlements(for:) ersetzt das deprecated currentEntitlement(for:) für die Handhabung mehrerer Berechtigungen
  • Das appTransactionID-Feld (iOS 18.4+) ermöglicht Kunden-Tracking ohne Server-zu-Server-Aufrufe
  • SubscriptionOfferView bietet eingebaute UI für Abonnement-Merchandising mit visibleRelationship für Upgrade-, Downgrade- und Crossgrade-Optionen
  • Werbeangebote können mit JWS über PromotionOfferV2SignatureCreator signiert werden
  • Angebotscodes unterstützen jetzt Verbrauchsprodukte, Nicht-Verbrauchsprodukte und nicht verlängernde Abonnements (iOS 16.3+)
  • Server-Validierung verwendet die App Store Server API mit JWS-Tokens und ersetzt den deprecated verifyReceipt-Endpunkt
  • Rufen Sie immer transaction.finish() nach der Inhaltsauslieferung auf, um unerwartetes Wiederherstellungsverhalten zu verhindern
  • Implementieren Sie Offline-Berechtigungs-Caching für nahtlose Benutzererfahrung ohne Konnektivität

Fang an zu üben!

Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.

Tägliche Challenge

Findest du den Bug in iOS?

Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Anthony Fillion-Maillet

Geschrieben von

Anthony Fillion-Maillet

Gründer von SharpSkill

Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.

Aktualisiert am 20. Oktober 2026

Tags

#ios
#storekit
#in-app-purchase
#swift
#interview

Teilen

Verwandte Artikel