Разработка
Осваиваем StoreKit 2
Фреймворк позволяет настроить непрерывный поток оформления покупок и подписок в приложении.
StoreKit предоставляет нам возможность получать доход от наших приложений. Фреймворк позволяет настроить непрерывный поток оформления покупок и подписок в приложении. StoreKit 2 представляет современный API на базе Swift для создания типобезопасных покупок.
Функции StoreKit 2 работают только на устройствах с iOS 15. Приложениям, которые по-прежнему поддерживают более старые версии ОС Apple рекомендуется использовать оригинальный StoreKit.
Прежде всего, мы должны настроить наш проект, добавив встроенные покупки на вкладке «Signing & Capabilities» проекта. Далее мы должны создать файл конфигурации StoreKit для тестирования покупок без сетевого соединения с App Store. Перейдите в меню File -> New -> File и выберите “StoreKit Configuration File”.
Можно создать файл конфигурации только для локальной сети и заполнить его тестовыми подписками и покупками. Другой вариант — получить список подписок и покупок из App Store Connect, установив флажок «Sync this file with an app in App Store Connect».
Последний шаг — запуск приложения с предопределенным файлом конфигурации StoreKit. Для этого необходимо отредактировать схему проекта и на вкладке настроек раздела Run выбрать файл конфигурации StoreKit. Теперь у вас есть полностью сконфигурированный проект, позволяющий тестировать покупки в приложении в Xcode.
Давайте начнем создание нашего пейвола с создания типа Store для обработки всей логики, связанной с покупками в приложении.
import StoreKit @MainActor final class Store: ObservableObject { @Published private(set) var products: [Product] = [] init() {} func fetchProducts() async { do { products = try await Product.products( for: [ "123456789", "987654321" ] ) } catch { products = [] } } }
Как видно из приведенного примера, мы определяем тип Store для получения и хранения списка продуктов, которые мы будем отображать на экране пейвола. Фреймворк StoreKit 2 предоставляет тип Product, инкапсулирующий всю логику, связанную с покупкой в приложении. Тип Product имеет статическую функцию products, которую мы можем использовать для получения списка продуктов, предоставив коллекцию идентификаторов.
struct ContentView: View { @StateObject private var store = Store() var body: some View { VStack { if store.products.isEmpty { ProgressView() } else { ForEach(store.products, id: \.id) { product in Button { Task { try await store.purchase(product) } } label: { VStack { Text(verbatim: product.displayName) .font(.headline) Text(verbatim: product.displayPrice) } } .buttonStyle(.borderedProminent) } } } .task { await store.fetchProducts() } } }
Мы используем тип Store для получения и отображения списка доступных покупок в приложении. Экземпляр типа Product содержит всю необходимую информацию, такую как название, описание и цена покупки в приложении.
Тип Product также содержит функцию purchase, которую мы можем использовать для запуска потока покупки в приложении для конкретного продукта. Она возвращает экземпляр перечисления PurchaseResult, определяющего три случая: успех (success), ожидание (pending) и отказ пользователя (userCancelled).
@MainActor final class Store: ObservableObject { // ... @Published private(set) var activeTransactions: Set<StoreKit.Transaction> = [] func purchase(_ product: Product) async throws { let result = try await product.purchase() switch result { case .success(let verificationResult): if let transaction = try? verificationResult.payloadValue { activeTransactions.insert(transaction) await transaction.finish() } case .userCancelled: break case .pending: break @unknown default: break } } }
Всякий раз, когда результат покупки доходит до состояния успеха, он предоставляет ассоциированное значение типа Transaction, определяющее успешную транзакцию. StoreKit оборачивает транзакцию в тип VerificationResult, позволяющий нам проверить, что транзакция правильно подписана и поступила из App Store.
Тип VerificationResult используется в StoreKit 2 для проверки того, что данные действительны и подписаны App Store. Он предоставляет нам вычисляемое свойство payloadValue, которое мы можем использовать для разворачивания подписанных данных или выброса ошибки, если данные подписаны неверно.
Как только транзакция будет получена, необходимо разблокировать приобретенную пользователем функцию и вызвать finish — функцию завершения для данной транзакции. Помните, что завершать транзакцию следует только после разблокировки приобретенной функции.
struct ContentView: View { @StateObject private var store = Store() var body: some View { VStack { Text("Purchased items: \(store.activeTransactions.count)") if store.products.isEmpty { ProgressView() } else { if store.activeTransactions.isEmpty { ForEach(store.products, id: \.id) { product in Button { Task { try await store.purchase(product) } } label: { VStack { Text(verbatim: product.displayName) .font(.headline) Text(verbatim: product.displayPrice) } } .buttonStyle(.borderedProminent) } } } } .task { await store.fetchProducts() } } }
Покупка становится отложенной (pending), когда включено подтверждение для покупки. В этом случае транзакция поступает позже, только после одобрения родителем. Для обработки такого рода транзакций необходимо наблюдать за потоком Transaction.updates. Чтобы не пропустить ни одной транзакции, мы должны начать отслеживать этот поток сразу после запуска приложения.
@MainActor final class Store: ObservableObject { @Published private(set) var activeTransactions: Set<StoreKit.Transaction> = [] private var updates: Task<Void, Never>? // ... init() { updates = Task { for await update in StoreKit.Transaction.updates { if let transaction = try? update.payloadValue { activeTransactions.insert(transaction) await transaction.finish() } } } } deinit { updates?.cancel() } }
StoreKit 2 предоставляет простой способ получения всех активных подписок и приобретенных товаров. В свойстве currentEntitlements типа Transaction перечислены все активные подписки и товары без рефанда.
@MainActor final class Store: ObservableObject { @Published private(set) var activeTransactions: Set<StoreKit.Transaction> = [] // ... func fetchActiveTransactions() async { var activeTransactions: Set<StoreKit.Transaction> = [] for await entitlement in StoreKit.Transaction.currentEntitlements { if let transaction = try? entitlement.payloadValue { activeTransactions.insert(transaction) } } self.activeTransactions = activeTransactions } }
Мы можем использовать свойство currentEntitlements для получения всех активных покупок при каждом запуске приложения или даже чаще. Активно отслеживая свойство currentEntitlements, мы избавляемся от необходимости восстанавливать покупки, поскольку currentEntitlements всегда содержит актуальный список активных подписок и непотребляемых покупок, даже если они были приобретены на другом устройстве.
Другие статьи о StoreKit 2
- Осваиваем StoreKit 2
- Осваиваем StoreKit 2: ProductView и StoreView в SwiftUI
- Осваиваем StoreKit 2: SubscriptionStoreView в SwiftUI
- Осваиваем StoreKit 2: модификаторы представлений в Swift
-
Новости1 месяц назад
Видеозвонки с Лили, Приключения и пианино — обновления Duolingo
-
Новости1 месяц назад
Видео и подкасты о мобильной разработке 2024.39
-
Видео и подкасты для разработчиков4 недели назад
Lua – идеальный встраиваемый язык
-
Новости4 недели назад
Poolside, занимающийся ИИ-программированием, привлек $500 млн