StoreKitManager.swift
3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//
// SubscriptionService.swift
// browser
//
// Created by Artem Talko on 23.11.2023.
//
import Foundation
import StoreKit
fileprivate enum StoreError: Error {
case failedVerification
}
final class StoreKitManager: ObservableObject {
@Published var storeProducts: [Product] = []
@Published var purchasedSubscriptions : [Product] = []
private var updateListenerTask: Task<Void, Error>? = nil
private let productDict: [String : String] // TODO: change to enum
init() {
if let plistPath = Bundle.main.path(forResource: "ProductList", ofType: "plist"),
let plist = FileManager.default.contents(atPath: plistPath) {
productDict = (try? PropertyListSerialization.propertyList(from: plist, format: nil) as? [String : String]) ?? [:]
} else {
productDict = [:]
}
updateListenerTask = listenForTransactions()
Task {
await requestProducts()
await updateCustomerProductStatus()
}
print("controller called")
}
deinit {
updateListenerTask?.cancel()
}
func listenForTransactions() -> Task<Void, Error> {
return Task.detached {
for await result in Transaction.updates {
do {
let transaction = try self.checkVerified(result)
await self.updateCustomerProductStatus()
await transaction.finish()
} catch {
print("Transaction failed verification")
}
}
}
}
@MainActor
func requestProducts() async {
do {
storeProducts = try await Product.products(for: productDict.values)
} catch {
print("Failed - error retrieving products \(error)")
}
}
func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
switch result {
case .unverified:
throw StoreError.failedVerification
case .verified(let signedType):
return signedType
}
}
func updateCustomerProductStatus() async {
var purchasedCourses: [Product] = []
for await result in Transaction.currentEntitlements {
do {
let transaction = try checkVerified(result)
if let course = storeProducts.first(where: { $0.id == transaction.productID}) {
purchasedCourses.append(course)
}
} catch {
print("Transaction failed verification")
}
self.purchasedSubscriptions = purchasedCourses
}
}
func purchase(_ product: Product) async throws -> Transaction? {
let result = try await product.purchase()
switch result {
case .success(let verificationResult):
let transaction = try checkVerified(verificationResult)
await updateCustomerProductStatus()
await transaction.finish()
return transaction
case .userCancelled, .pending:
return nil
default:
return nil
}
}
func isPurchased(_ product: Product) async throws -> Bool {
return purchasedSubscriptions.contains(product)
}
func restorePurchase() async throws {
try await AppStore.sync()
}
}