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

Commit 88711fc

Browse files
committed
fix(init): coalesce concurrent initialization
Add initTask to OpenIapModule to deduplicate concurrent initConnection calls and have ensureConnection() auto-initialize when needed. Cancel in-flight init on endConnection to prevent races.
1 parent b1d5eff commit 88711fc

File tree

1 file changed

+28
-4
lines changed

1 file changed

+28
-4
lines changed

Sources/OpenIapModule.swift

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol {
1717
private var updateListenerTask: Task<Void, Error>?
1818
private var productManager: ProductManager?
1919
private let state = IapState()
20+
// Coalesce concurrent init attempts
21+
private var initTask: Task<Bool, Error>?
2022

2123
private override init() {
2224
super.init()
@@ -29,16 +31,20 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol {
2931

3032
/// Ensure connection is initialized before operations
3133
private func ensureConnection() async throws {
32-
let ok = await state.isInitialized
34+
var ok = await state.isInitialized
35+
if !ok {
36+
// Coalesce with ongoing initialization
37+
_ = try await initConnection()
38+
ok = await state.isInitialized
39+
}
3340
guard ok else {
3441
let error = OpenIapErrorEvent(
3542
code: OpenIapError.E_INIT_CONNECTION,
36-
message: "Connection not initialized. Call initConnection() first."
43+
message: "Connection not initialized"
3744
)
3845
emitPurchaseError(error)
3946
throw OpenIapError.purchaseFailed(reason: error.message)
4047
}
41-
4248
guard AppStore.canMakePayments else {
4349
let error = OpenIapErrorEvent(
4450
code: OpenIapError.E_IAP_NOT_AVAILABLE,
@@ -50,7 +56,22 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol {
5056
}
5157

5258
public func initConnection() async throws -> Bool {
53-
return try await initConnectionInternal()
59+
if let task = initTask {
60+
return try await task.value
61+
}
62+
let task = Task { [weak self] () -> Bool in
63+
guard let self = self else { return false }
64+
return try await self.initConnectionInternal()
65+
}
66+
initTask = task
67+
do {
68+
let result = try await task.value
69+
initTask = nil
70+
return result
71+
} catch {
72+
initTask = nil
73+
throw error
74+
}
5475
}
5576

5677
private func initConnectionInternal() async throws -> Bool {
@@ -87,6 +108,9 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol {
87108
}
88109

89110
private func endConnectionInternal() async throws -> Bool {
111+
// Cancel any in-flight initialization
112+
initTask?.cancel()
113+
initTask = nil
90114
await cleanupExistingState()
91115
return true
92116
}

0 commit comments

Comments
 (0)