• 26.08.2026 21:12:17
  • Admin Admin

SwiftUI eğitiminde StoreKit 2 ile satın alma, işlem doğrulama, abonelik yenileme ve iade senaryolarını tek bir yetki katmanında ele alın. Xcode StoreKit testi ve sunucu bildirimleriyle tutarlı erişim tasarlayın.

SwiftUI Eğitiminde StoreKit 2 ile Abonelik Yetkisi Yönetimi

SwiftUI eğitimi için abonelikte doğru sınır: ürün değil yetki

Bir abonelik ekranında UI'nin doğrudan Product nesnesine veya son satın alma sonucuna bakması hatalıdır. Kullanıcının erişimi, ürün fiyatından bağımsız bir entitlement olarak modellenmelidir: proAccess, teamAccess ya da exportAccess gibi. Bunun teknik nedeni, aynı ürünün yenilenmesi, geri alınması, aile paylaşımı, cihaz değiştirme ve iade işlemlerinin farklı transaction'lar üretmesidir. ios mvvm mimarisi içinde ViewModel'in yalnızca bu yetkiyi tüketmesi, StoreKit ayrıntılarını UI testlerinden ayırır. ios eğitimi veya ios kursu materyallerinde sık görülen isPurchased: Bool alanı, birden fazla abonelik paketi olduğunda hangi paketin hangi özelliği açtığını taşıyamaz.

enum Entitlement: Hashable, Sendable {
    case proAccess
    case exportAccess
}

protocol EntitlementProviding: Sendable {
    func activeEntitlements() async throws -> Set<Entitlement>
}

@MainActor
final class ExportViewModel: ObservableObject {
    @Published private(set) var canExport = false

    func load(using provider: EntitlementProviding) async {
        do {
            canExport = try await provider.activeEntitlements()
                .contains(.exportAccess)
        } catch {
            canExport = false
        }
    }
}

Bu sınırı test edilebilir kılmak için EntitlementProviding protokolüne bir fake uygulama verin. SwiftUI preview veya XCTest, StoreKit sandbox hesabına bağlanmadan ücretli ve ücretsiz durumları üretir. swift eğitimi kapsamında önemli ayrıntı şudur: fake nesnenin yalnızca true döndürmesi yerine farklı entitlement kümeleri döndürmesi gerekir; aksi halde paket bazlı erişim regresyonları görünmez.

StoreKit 2 ile satın alma ve Transaction doğrulaması

Ürünleri Product.products(for:) ile App Store Connect'teki product identifier listesi üzerinden yükleyin. Satın alma akışında .success sonucu tek başına yeterli değildir; StoreKit 2 sonucu VerificationResult<Transaction> olarak verir. .unverified durumunda imza doğrulanamadığı için içeriği açmayın ve transaction'ı finish() etmeyin. Bu ayrım, modifiye edilmiş uygulama ya da doğrulama hatasında satın alma hakkının yanlış verilmesini engeller.

import StoreKit

struct PurchaseService {
    func buy(productID: String, accountToken: UUID) async throws -> Bool {
        let products = try await Product.products(for: [productID])
        guard let product = products.first else {
            throw StoreError.productNotFound
        }

        let result = try await product.purchase(options: [
            .appAccountToken(accountToken)
        ])

        switch result {
        case .success(.verified(let transaction)):
            // Yerel yetki kaynağini yenilemeden UI'yi kalici olarak acmayin.
            await transaction.finish()
            return true
        case .success(.unverified(_, let error)):
            throw error
        case .pending:
            return false
        case .userCancelled:
            return false
        @unknown default:
            return false
        }
    }
}

enum StoreError: Error {
    case productNotFound
}

appAccountToken için kullanıcı e-postasından UUID türetmeyin. Sunucuda uygulama hesabı ile eşlenen, rastgele üretilmiş ve kalıcı bir UUID kullanın. Bu token, App Store Server Notifications V2 ve App Store Server API kayıtlarını uygulama hesabına bağlamaya yardım eder; kişisel veriyi Apple'a taşımadan korelasyon sağlar. iphone uygulama geliştirme projelerinde yaygın hata, satın alma başarılı olduğunda yalnızca bir UserDefaults bayrağı yazmaktır. Uygulama silinip kurulduğunda, başka cihazda oturum açıldığında veya iade yapıldığında bu bayrak StoreKit gerçeğinden kopar.

Swift kursu projelerinde Transaction.updates ile yenileme ve iade yönetimi

Uygulama yalnızca satın alma düğmesine basıldığında değil, her açılışta ve yaşam döngüsü boyunca transaction güncellemelerini tüketmelidir. Transaction.currentEntitlements mevcut hakları yeniden kurmak için, Transaction.updates ise uygulama çalışırken gelen yenileme, geri yükleme veya iade değişiklikleri için kullanılır. Güncellemeleri dinlememek, kullanıcı App Store ayarlarından satın almayı geri yüklediğinde ekranın uygulama yeniden açılana kadar kilitli kalmasına neden olabilir.

import StoreKit

actor StoreKitEntitlementProvider: EntitlementProviding {
    private let proProductIDs: Set<String> = [
        "com.acme.app.pro.monthly",
        "com.acme.app.pro.yearly"
    ]

    func activeEntitlements() async throws -> Set<Entitlement> {
        var result: Set<Entitlement> = []

        for await verification in Transaction.currentEntitlements {
            guard case .verified(let transaction) = verification else { continue }
            guard transaction.revocationDate == nil else { continue }
            guard proProductIDs.contains(transaction.productID) else { continue }

            if let expiresAt = transaction.expirationDate, expiresAt <= Date() {
                continue
            }
            result.insert(.proAccess)
            result.insert(.exportAccess)
        }
        return result
    }

    func listenForUpdates(onChange: @escaping @Sendable () async -> Void) async {
        for await verification in Transaction.updates {
            guard case .verified(let transaction) = verification else { continue }
            await onChange()
            await transaction.finish()
        }
    }
}

Transaction.updates dinleyicisini SwiftUI View'in .task bloğuna körlemesine koymayın; görünüm yeniden oluşturuldukça birden fazla dinleyici açılabilir. Uygulama seviyesinde tek bir sahip, örneğin @main App içindeki uzun ömürlü bir coordinator, dinleyiciyi başlatmalı ve iptal yaşam döngüsünü yönetmelidir. swiftui eğitimi sırasında atlanan incelik budur: aynı transaction için iki dinleyici finish() çağırsa bile sorun çoğunlukla görünmez, ancak tekrarlanan ağ senkronizasyonu ve çakışan UI state güncellemeleri üretir.

Xcode eğitimi ile StoreKit Configuration senaryolarını tekrarlanabilir test etme

Xcode'da bir .storekit dosyası oluşturup scheme'in Run Options bölümünde StoreKit Configuration olarak seçin. Dosyaya aylık ve yıllık otomatik yenilenen abonelikleri, bir tüketilebilir ürünü ve farklı subscription group seviyelerini ekleyin. Ardından StoreKit Transaction Manager üzerinden yenileme, expiration, refund ve billing retry senaryolarını elle tetikleyin. xcode eğitimi açısından bunun değeri, sandbox yenileme zamanlarını beklemeden aynı hata akışını tekrar oynatabilmenizdir.

// XCTest içinde gerçek App Store katalog sorgusu yapmadan akışı sınayın.
func testExpiredSubscriptionDoesNotGrantExport() async throws {
    let provider = StoreKitEntitlementProviderForTest(
        transactions: [
            .init(productID: "com.acme.app.pro.monthly",
                  expirationDate: Date().addingTimeInterval(-60),
                  revocationDate: nil)
        ]
    )

    let entitlements = try await provider.activeEntitlements()
    XCTAssertFalse(entitlements.contains(.exportAccess))
}

Test matrisine özellikle şu dört durumu ekleyin: satın alma sırasında .pending, doğrulanamayan transaction, aktif aboneliğin revocationDate alması ve farklı subscription group içindeki ürün değişimi. .pending sonucu aile onayı veya ödeme yetkilendirmesi nedeniyle saatler sonra tamamlanabilir; bu durumda satın alma ekranını hata gibi kapatmak yerine transaction güncellemelerinin sonucu getirmesine izin verin. Bu yaklaşım, swift kursu örneklerinde sık kullanılan 'başarısız oldu, tekrar satın al' yönlendirmesinden daha doğru bir durum makinesi kurar.

App Store yayınlama öncesi sunucu otoritesi ve bildirim işleme

Cihaz tarafındaki StoreKit doğrulaması UI erişimi için yararlıdır, fakat kota, ekip lisansı veya web istemcisi gibi çapraz platform yetkilerinde sunucu da transaction zincirini doğrulamalıdır. App Store Server Notifications V2 isteğindeki signedPayload bir JWS'tir; içindeki data.signedTransactionInfo da ayrı bir JWS olabilir. Sunucuda Apple sertifika zincirini ve imzayı doğruladıktan sonra originalTransactionId temelinde abonelik kaydını güncelleyin. Sadece notificationType alanına güvenip imzayı atlamak, sahte HTTP isteğiyle abonelik açılmasına yol açar.

Bildirim handler'ını idempotent tasarlayın. Veritabanında transactionId için unique index oluşturun ve aynı bildirimi ikinci kez aldığınızda tekrar kredi eklemeyin. DID_RENEW, EXPIRED, REFUND ve REVOKE olaylarını loglayın; ardından kritik state değişimlerinde App Store Server API ile ilgili originalTransactionId için güncel durumu sorgulayın. Bildirimlerin teslim sırası garanti edilmediğinden, 'son gelen olay son durumdur' varsayımı yerine transaction'ın imzalı tarihlerini ve abonelik durumunu karşılaştırın.

app store yayınlama kontrol listesine gerçek cihaz sandbox testi, restore purchases akışı, gizlilik politikası bağlantısı, abonelik koşulları ve App Review için test hesabı talimatlarını ekleyin. App Review notlarında ürün kimliklerini ve premium ekranına ulaşma adımlarını yazın. Özellikle içerik sunucudan indiriliyorsa, inceleme hesabının entitlement kaydı ile App Store sandbox transaction kaydını aynı kullanıcıya bağladığınızı doğrulayın; aksi halde inceleme cihazında satın alma başarılı olsa bile API 403 döndürebilir.

Sık Sorulan Sorular

iOS eğitimi kapsamında StoreKit 2 transaction finish ne zaman çağrılmalı?

Yalnızca .verified transaction aldıktan ve uygulamanın gerekli yetki senkronizasyonunu başlattıktan sonra çağrılmalıdır. .unverified sonucu finish etmeyin. Consumable ürünlerde sunucuya kredi yazma işlemini transactionId ile idempotent yaptıktan sonra finish etmek, uygulama işlem sırasında kapanırsa transaction'ın yeniden teslim edilmesini güvenli hale getirir.

SwiftUI eğitimi için abonelik durumunu UserDefaults ile saklamak yeterli mi?

Hayır. UserDefaults yalnızca kısa ömürlü UI cache'i olabilir. Uygulama açılışında Transaction.currentEntitlements ile durumu yeniden kurun, uygulama çalışırken Transaction.updates dinleyin ve iade için revocationDate kontrol edin. Sunucu tarafı erişimi varsa imzalı transaction verisini ayrıca doğrulayın.

iPhone uygulama geliştirme sürecinde StoreKit abonelikleri Xcode ile nasıl test edilir?

Bir .storekit configuration dosyası oluşturun, scheme Run Options altında seçin ve StoreKit Transaction Manager'dan renewal, expiration, refund ve billing retry olaylarını tetikleyin. XCTest'te entitlement sağlayıcısını protokolle soyutlayarak süresi geçmiş, geri alınmış ve aktif transaction kümelerini ağ bağımlılığı olmadan test edin.

iOS MVVM mimarisi içinde StoreKit 2 hangi katmanda olmalı?

StoreKit çağrılarını View veya ViewModel'e yaymak yerine EntitlementProviding gibi bir protokol arkasındaki servis katmanına koyun. ViewModel bu servisten yalnızca Set<Entitlement> veya ekranına özel bir yetki modeli almalıdır. Böylece StoreKit Configuration, fake provider ve App Store sandbox senaryoları ViewModel testlerinde değiştirilebilir.

AI / LLM Discovery

Bu makale Opendart Akademi iOS / Swift eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt