Deprecate StoreKit types

This commit is contained in:
Daniel Saidi 2022-06-20 12:43:10 +02:00
parent b0720dbe58
commit bab5074eec
8 changed files with 64 additions and 177 deletions

View File

@ -3,12 +3,16 @@
## 1.3
This version adjusts the library for Xcode 14.
This version adjusts the library for Xcode 14 and deprecates some things.
### ✨ New features
* `Collection+Async` is now available for all OS versions that are supported by the library.
### 🗑 Deprecations
* The `StoreKit` namespace has been deprecated and moved to a new library: https://github.com/danielsaidi/StoreKitPlus
## 1.2

View File

@ -8,28 +8,10 @@
import StoreKit
/**
This service class implements `StoreService` by integrating
with StoreKit.
The service keeps products and purchases in sync, using the
provided ``StoreContext``. An app can listen to any changes
in the context to drive UI changes.
You can configure a test app to use this service with local
products by adding a StoreKit configuration file to the app.
*/
@available(*, deprecated, message: "StandardStoreService has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
public class StandardStoreService: StoreService {
/**
Create a service instance for the provided `productIds`,
that syncs any changes to the provided `context`.
- Parameters:
- productIds: The IDs of the products to handle.
- context: The store context to sync with.
*/
public init(
productIds: [String],
context: StoreContext = StoreContext()) {
@ -47,16 +29,10 @@ public class StandardStoreService: StoreService {
private let storeContext: StoreContext
private var transactionTask: Task<Void, Error>?
/**
Get all available products.
*/
public func getProducts() async throws -> [Product] {
try await Product.products(for: productIds)
}
/**
Purchase a certain product.
*/
public func purchase(_ product: Product) async throws -> Product.PurchaseResult {
let result = try await product.purchase()
switch result {
@ -68,17 +44,10 @@ public class StandardStoreService: StoreService {
return result
}
/**
Restore purchases that are not on this device.
*/
public func restorePurchases() async throws {
try await syncTransactions()
}
/**
Sync product and purchase information from the store to
the provided store context.
*/
public func syncStoreData() async throws {
let products = try await getProducts()
await updateContext(with: products)
@ -86,13 +55,10 @@ public class StandardStoreService: StoreService {
}
}
@available(*, deprecated, message: "StandardStoreService has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
private extension StandardStoreService {
/**
Create a task that can be used to listen for and acting
on transaction changes.
*/
func getTransactionListenerTask() -> Task<Void, Error> {
Task.detached {
for await result in Transaction.updates {
@ -105,27 +71,18 @@ private extension StandardStoreService {
}
}
/**
Try resolving a valid transaction for a certain product.
*/
func getValidTransaction(for productId: String) async throws -> Transaction? {
guard let latest = await Transaction.latest(for: productId) else { return nil }
let result = try verifyTransaction(latest)
return result.isValid ? result : nil
}
/**
Handle the transaction in the provided `result`.
*/
func handleTransaction(_ result: VerificationResult<Transaction>) async throws {
let transaction = try verifyTransaction(result)
await updateContext(with: transaction)
await transaction.finish()
}
/**
Sync the transactions of all available products.
*/
func syncTransactions() async throws {
var transactions: [Transaction] = []
for id in productIds {
@ -136,9 +93,6 @@ private extension StandardStoreService {
await updateContext(with: transactions)
}
/**
Verify the transaction in the provided `result`
*/
func verifyTransaction(_ result: VerificationResult<Transaction>) throws -> Transaction {
switch result {
case .unverified(let transaction, let error): throw StoreServiceError.invalidTransaction(transaction, error)
@ -148,6 +102,7 @@ private extension StandardStoreService {
}
@MainActor
@available(*, deprecated, message: "StandardStoreService has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
private extension StandardStoreService {

View File

@ -1,92 +0,0 @@
//
// StoreContext.swift
// SwiftKit
//
// Created by Daniel Saidi on 2021-11-08.
// Copyright © 2021 Daniel Saidi. All rights reserved.
//
import StoreKit
import Combine
/**
This class can be used to manage store information in a way
that makes it observable.
Since the `Product` type isn't `Codable`, `products` is not
permanently persisted, which means that this information is
reset if the app restarts. Due to this, any changes to this
property will also update `productIds`, which gives you the
option to map the IDs to a local product representation and
present previously fetched products.
Note however that a `Product` instance is needed to perform
a purchase.
*/
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
public class StoreContext: ObservableObject {
public init() {
productIds = persistedProductIds
purchasedProductIds = persistedPurchasedProductIds
}
/**
The available products.
Since `Product` isn't `Codable`, this property can't be
persisted. It must be re-fetched when the app starts.
*/
public var products: [Product] = [] {
didSet { productIds = products.map { $0.id} }
}
/**
The available products IDs.
This list is permanently persisted and can be mapped to
a local product representation to present temp. product
info before `products` have been re-fetched.
*/
@Published
public private(set) var productIds: [String] = [] {
willSet { persistedProductIds = newValue }
}
/**
The purchased products IDs.
This list is permanently persisted and can be mapped to
a local product representation to present temp. product
info before `transactions` have been re-fetched.
*/
@Published
public private(set) var purchasedProductIds: [String] = [] {
willSet { persistedPurchasedProductIds = newValue }
}
/**
The active transactions.
Since `Transaction` isn't `Codable` this property can't
be persisted. It must be re-fetched when the app starts.
*/
public var transactions: [StoreKit.Transaction] = [] {
didSet { purchasedProductIds = transactions.map { $0.productID } }
}
// MARK: - Persisted Properties
@Persisted(key: key("productIds"), defaultValue: [])
private var persistedProductIds: [String]
@Persisted(key: key("purchasedProductIds"), defaultValue: [])
private var persistedPurchasedProductIds: [String]
}
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
private extension StoreContext {
static func key(_ name: String) -> String { "store.\(name)" }
}

View File

@ -8,19 +8,14 @@
import StoreKit
@available(*, deprecated, message: "StoreContext has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
public extension StoreContext {
/**
Whether or not a certain product has an active purchase.
*/
func isProductPurchased(id: String) -> Bool {
purchasedProductIds.contains(id)
}
/**
Whether or not a certain product has an active purchase.
*/
func isProductPurchased(_ product: Product) -> Bool {
isProductPurchased(id: product.id)
}

View File

@ -0,0 +1,52 @@
//
// StoreContext.swift
// SwiftKit
//
// Created by Daniel Saidi on 2021-11-08.
// Copyright © 2021 Daniel Saidi. All rights reserved.
//
import StoreKit
import Combine
@available(*, deprecated, message: "StoreContext has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
public class StoreContext: ObservableObject {
public init() {
productIds = persistedProductIds
purchasedProductIds = persistedPurchasedProductIds
}
public var products: [Product] = [] {
didSet { productIds = products.map { $0.id} }
}
@Published
public private(set) var productIds: [String] = [] {
willSet { persistedProductIds = newValue }
}
@Published
public private(set) var purchasedProductIds: [String] = [] {
willSet { persistedPurchasedProductIds = newValue }
}
public var transactions: [StoreKit.Transaction] = [] {
didSet { purchasedProductIds = transactions.map { $0.productID } }
}
@Persisted(key: key("productIds"), defaultValue: [])
private var persistedProductIds: [String]
@Persisted(key: key("purchasedProductIds"), defaultValue: [])
private var persistedPurchasedProductIds: [String]
}
@available(*, deprecated, message: "StoreContext has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
private extension StoreContext {
static func key(_ name: String) -> String { "store.\(name)" }
}

View File

@ -8,31 +8,12 @@
import StoreKit
/**
This protocol can be implemented by any classes that can be
used to manage store products.
*/
@available(*, deprecated, message: "StoreService has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
public protocol StoreService {
/**
Get all available products.
*/
func getProducts() async throws -> [Product]
/**
Purchase a certain product.
*/
func purchase(_ product: Product) async throws -> Product.PurchaseResult
/**
Restore purchases that are not on this device.
*/
func restorePurchases() async throws
/**
Sync product and purchase information from the store to
any implementation defined sync destination.
*/
func syncStoreData() async throws
}

View File

@ -9,12 +9,9 @@
import Foundation
import StoreKit
/**
This enum lists errors that can be thrown by store services.
*/
@available(*, deprecated, message: "StoreServiceError has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
public enum StoreServiceError: Error {
/// This is thrown if a transaction can't be verified.
case invalidTransaction(Transaction, VerificationResult<Transaction>.VerificationError)
}

View File

@ -8,15 +8,10 @@
import StoreKit
@available(*, deprecated, message: "This extension has been moved to StoreKitPlus - https://github.com/danielsaidi/StoreKitPlus")
@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
extension Transaction {
/**
Whether or not the transaction is valid.
The logic may have to be adjusted as more product types
are handled with this service.
*/
var isValid: Bool {
if revocationDate != nil { return false }
guard let date = expirationDate else { return false }