Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.

Commit 01f95ed

Browse files
hyochanclaude
andcommitted
feat: add Objective-C/KMP support with @objc completion handler wrappers
- Add OpenIapModule+ObjC.swift extension - Provide completion handler versions of all main async methods - Enable Kotlin Multiplatform interoperability via CocoaPods/cinterop - All @objc methods use [Any] for non-@objc compatible types This allows kmp-iap and other Kotlin Multiplatform projects to use OpenIAP without requiring direct Swift interop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9a99e5d commit 01f95ed

File tree

1 file changed

+245
-0
lines changed

1 file changed

+245
-0
lines changed

Sources/OpenIapModule+ObjC.swift

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import Foundation
2+
import StoreKit
3+
4+
// MARK: - Objective-C Bridge for Kotlin Multiplatform
5+
6+
@available(iOS 15.0, macOS 14.0, *)
7+
@objc public extension OpenIapModule {
8+
9+
// MARK: - Connection Management
10+
11+
@objc func initConnectionWithCompletion(_ completion: @escaping (Bool, Error?) -> Void) {
12+
Task {
13+
do {
14+
let result = try await initConnection()
15+
completion(result, nil)
16+
} catch {
17+
completion(false, error)
18+
}
19+
}
20+
}
21+
22+
@objc func endConnectionWithCompletion(_ completion: @escaping (Bool, Error?) -> Void) {
23+
Task {
24+
do {
25+
let result = try await endConnection()
26+
completion(result, nil)
27+
} catch {
28+
completion(false, error)
29+
}
30+
}
31+
}
32+
33+
// MARK: - Product Management
34+
35+
@objc func fetchProductsWithSkus(
36+
_ skus: [String],
37+
type: String?,
38+
completion: @escaping ([Any]?, Error?) -> Void
39+
) {
40+
Task {
41+
do {
42+
let productType = type.flatMap { ProductQueryType(rawValue: $0) }
43+
let request = ProductRequest(skus: skus, type: productType)
44+
let result = try await fetchProducts(request)
45+
46+
switch result {
47+
case .products(let products):
48+
completion(products, nil)
49+
case .subscriptions(let subscriptions):
50+
completion(subscriptions, nil)
51+
}
52+
} catch {
53+
completion(nil, error)
54+
}
55+
}
56+
}
57+
58+
@objc func getPromotedProductIOSWithCompletion(_ completion: @escaping (Any?, Error?) -> Void) {
59+
Task {
60+
do {
61+
let product = try await getPromotedProductIOS()
62+
completion(product as Any?, nil)
63+
} catch {
64+
completion(nil, error)
65+
}
66+
}
67+
}
68+
69+
// MARK: - Purchase Management
70+
71+
@objc func requestPurchaseWithSku(
72+
_ sku: String,
73+
quantity: Int,
74+
type: String?,
75+
completion: @escaping (Any?, Error?) -> Void
76+
) {
77+
Task {
78+
do {
79+
let productType = type.flatMap { ProductQueryType(rawValue: $0) } ?? .inApp
80+
let iosProps = RequestPurchaseIosProps(
81+
andDangerouslyFinishTransactionAutomatically: nil,
82+
appAccountToken: nil,
83+
quantity: quantity,
84+
sku: sku,
85+
withOffer: nil
86+
)
87+
let props = RequestPurchaseProps(
88+
request: .purchase(
89+
RequestPurchasePropsByPlatforms(android: nil, ios: iosProps)
90+
),
91+
type: productType
92+
)
93+
94+
let result = try await requestPurchase(props)
95+
96+
switch result {
97+
case .purchase(let purchase):
98+
completion(purchase as Any, nil)
99+
case .purchases(let purchases):
100+
completion(purchases?.first as Any, nil)
101+
case .none:
102+
completion(nil, nil)
103+
}
104+
} catch {
105+
completion(nil, error)
106+
}
107+
}
108+
}
109+
110+
@objc func restorePurchasesWithCompletion(_ completion: @escaping (Error?) -> Void) {
111+
Task {
112+
do {
113+
try await restorePurchases()
114+
completion(nil)
115+
} catch {
116+
completion(error)
117+
}
118+
}
119+
}
120+
121+
@objc func getAvailablePurchasesWithCompletion(_ completion: @escaping ([Any]?, Error?) -> Void) {
122+
Task {
123+
do {
124+
let purchases = try await getAvailablePurchases(nil)
125+
completion(purchases as [Any], nil)
126+
} catch {
127+
completion(nil, error)
128+
}
129+
}
130+
}
131+
132+
// MARK: - Transaction Management
133+
134+
@objc func finishTransactionWithPurchaseId(
135+
_ purchaseId: String,
136+
productId: String,
137+
isConsumable: Bool,
138+
completion: @escaping (Error?) -> Void
139+
) {
140+
Task {
141+
do {
142+
let purchaseInput = PurchaseInput(
143+
id: purchaseId,
144+
ids: nil,
145+
isAutoRenewing: false,
146+
platform: .ios,
147+
productId: productId,
148+
purchaseState: .purchased,
149+
purchaseToken: nil,
150+
quantity: 1,
151+
transactionDate: Date().timeIntervalSince1970
152+
)
153+
try await finishTransaction(purchase: purchaseInput, isConsumable: isConsumable)
154+
completion(nil)
155+
} catch {
156+
completion(error)
157+
}
158+
}
159+
}
160+
161+
@objc func getPendingTransactionsIOSWithCompletion(_ completion: @escaping ([Any]?, Error?) -> Void) {
162+
Task {
163+
do {
164+
let transactions = try await getPendingTransactionsIOS()
165+
completion(transactions as [Any], nil)
166+
} catch {
167+
completion(nil, error)
168+
}
169+
}
170+
}
171+
172+
@objc func clearTransactionIOSWithCompletion(_ completion: @escaping (Bool, Error?) -> Void) {
173+
Task {
174+
do {
175+
let result = try await clearTransactionIOS()
176+
completion(result, nil)
177+
} catch {
178+
completion(false, error)
179+
}
180+
}
181+
}
182+
183+
// MARK: - Validation
184+
185+
@objc func getReceiptDataIOSWithCompletion(_ completion: @escaping (String?, Error?) -> Void) {
186+
Task {
187+
do {
188+
let receipt = try await getReceiptDataIOS()
189+
completion(receipt, nil)
190+
} catch {
191+
completion(nil, error)
192+
}
193+
}
194+
}
195+
196+
// MARK: - Store Information
197+
198+
@objc func getStorefrontIOSWithCompletion(_ completion: @escaping (String?, Error?) -> Void) {
199+
Task {
200+
do {
201+
let storefront = try await getStorefrontIOS()
202+
completion(storefront, nil)
203+
} catch {
204+
completion(nil, error)
205+
}
206+
}
207+
}
208+
209+
// MARK: - Subscription Management
210+
211+
@objc func getActiveSubscriptionsWithCompletion(_ completion: @escaping ([Any]?, Error?) -> Void) {
212+
Task {
213+
do {
214+
let subscriptions = try await getActiveSubscriptions(nil)
215+
completion(subscriptions as [Any], nil)
216+
} catch {
217+
completion(nil, error)
218+
}
219+
}
220+
}
221+
222+
// MARK: - UI
223+
224+
@objc func presentCodeRedemptionSheetIOSWithCompletion(_ completion: @escaping (Bool, Error?) -> Void) {
225+
Task {
226+
do {
227+
let result = try await presentCodeRedemptionSheetIOS()
228+
completion(result, nil)
229+
} catch {
230+
completion(false, error)
231+
}
232+
}
233+
}
234+
235+
@objc func showManageSubscriptionsIOSWithCompletion(_ completion: @escaping ([Any]?, Error?) -> Void) {
236+
Task {
237+
do {
238+
let purchases = try await showManageSubscriptionsIOS()
239+
completion(purchases as [Any], nil)
240+
} catch {
241+
completion(nil, error)
242+
}
243+
}
244+
}
245+
}

0 commit comments

Comments
 (0)