expo-helium 3.0.14 → 3.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,7 @@ Pod::Spec.new do |s|
19
19
  s.static_framework = true
20
20
 
21
21
  s.dependency 'ExpoModulesCore'
22
- s.dependency 'Helium', '3.0.14'
22
+ s.dependency 'Helium', '3.0.15'
23
23
 
24
24
  # Swift/Objective-C compatibility
25
25
  s.pod_target_xcconfig = {
@@ -33,12 +33,31 @@ struct HasEntitlementResult: Record {
33
33
  var hasEntitlement: Bool? = nil
34
34
  }
35
35
 
36
- public class HeliumPaywallSdkModule: Module {
37
- // Single continuations for ongoing operations
38
- private var currentProductId: String? = nil
39
- private var purchaseContinuation: CheckedContinuation<HeliumPaywallTransactionStatus, Never>? = nil
40
- private var restoreContinuation: CheckedContinuation<Bool, Never>? = nil
36
+ // Singleton to manage purchase state that survives module deallocation
37
+ private class PurchaseStateManager {
38
+ static let shared = PurchaseStateManager()
39
+
40
+ // Always keep reference to the current module
41
+ var currentModule: HeliumPaywallSdkModule?
42
+
43
+ // Store active operations
44
+ var activePurchaseContinuation: CheckedContinuation<HeliumPaywallTransactionStatus, Never>?
45
+ var activeRestoreContinuation: CheckedContinuation<Bool, Never>?
46
+ var currentProductId: String?
47
+
48
+ private init() {}
49
+
50
+ func clearPurchase() {
51
+ activePurchaseContinuation = nil
52
+ currentProductId = nil
53
+ }
54
+
55
+ func clearRestore() {
56
+ activeRestoreContinuation = nil
57
+ }
58
+ }
41
59
 
60
+ public class HeliumPaywallSdkModule: Module {
42
61
  // Each module class must implement the definition function. The definition consists of components
43
62
  // that describes the module's functionality and behavior.
44
63
  // See https://docs.expo.dev/modules/module-api for more details about available components.
@@ -48,6 +67,10 @@ public class HeliumPaywallSdkModule: Module {
48
67
  // The module will be accessible from `requireNativeModule('HeliumPaywallSdk')` in JavaScript.
49
68
  Name("HeliumPaywallSdk")
50
69
 
70
+ OnCreate {
71
+ PurchaseStateManager.shared.currentModule = self
72
+ }
73
+
51
74
  // Sets constant properties on the module. Can take a dictionary or a closure that returns a dictionary.
52
75
  // Constants([
53
76
  // "PI": Double.pi
@@ -80,7 +103,7 @@ public class HeliumPaywallSdkModule: Module {
80
103
 
81
104
  let useDefaultDelegate = config["useDefaultDelegate"] as? Bool ?? false
82
105
 
83
- let delegateEventHandler: (HeliumEvent) -> Void = { [weak self] event in
106
+ let delegateEventHandler: (HeliumEvent) -> Void = { event in
84
107
  var eventDict = event.toDictionary()
85
108
  // Add deprecated fields for backwards compatibility
86
109
  if let paywallName = eventDict["paywallName"] {
@@ -95,47 +118,52 @@ public class HeliumPaywallSdkModule: Module {
95
118
  if let buttonName = eventDict["buttonName"] {
96
119
  eventDict["ctaName"] = buttonName
97
120
  }
98
- self?.sendEvent("onHeliumPaywallEvent", eventDict)
121
+ PurchaseStateManager.shared.currentModule?.sendEvent("onHeliumPaywallEvent", eventDict)
99
122
  }
100
123
 
101
124
  // Create delegate with closures that send events to JavaScript
102
125
  let internalDelegate = InternalDelegate(
103
126
  eventHandler: delegateEventHandler,
104
- purchaseHandler: { [weak self] productId in
105
- guard let self else { return .failed(PurchaseError.purchaseFailed(errorMsg: "Module not active!")) }
106
- // Check if there's already a purchase in progress and cancel it
107
- if let existingContinuation = self.purchaseContinuation {
127
+ purchaseHandler: { productId in
128
+ // First check singleton for orphaned continuation and clean it up
129
+ if let existingContinuation = PurchaseStateManager.shared.activePurchaseContinuation {
108
130
  existingContinuation.resume(returning: .cancelled)
109
- self.purchaseContinuation = nil
110
- self.currentProductId = nil
131
+ PurchaseStateManager.shared.clearPurchase()
132
+ }
133
+
134
+ // Get current module from singleton
135
+ guard let module = PurchaseStateManager.shared.currentModule else {
136
+ return .failed(PurchaseError.purchaseFailed(errorMsg: "Module not active!"))
111
137
  }
112
138
 
113
139
  return await withCheckedContinuation { continuation in
114
- // Store the continuation and product ID
115
- self.currentProductId = productId
116
- self.purchaseContinuation = continuation
140
+ PurchaseStateManager.shared.activePurchaseContinuation = continuation
141
+ PurchaseStateManager.shared.currentProductId = productId
117
142
 
118
143
  // Send event to JavaScript
119
- self.sendEvent("onDelegateActionEvent", [
144
+ module.sendEvent("onDelegateActionEvent", [
120
145
  "type": "purchase",
121
146
  "productId": productId
122
147
  ])
123
148
  }
124
149
  },
125
- restoreHandler: { [weak self] in
126
- guard let self else { return false }
127
- // Check if there's already a restore in progress and cancel it
128
- if let existingContinuation = self.restoreContinuation {
150
+ restoreHandler: {
151
+ // Check for orphaned continuation in singleton
152
+ if let existingContinuation = PurchaseStateManager.shared.activeRestoreContinuation {
129
153
  existingContinuation.resume(returning: false)
130
- self.restoreContinuation = nil
154
+ PurchaseStateManager.shared.clearRestore()
155
+ }
156
+
157
+ // Get current module from singleton
158
+ guard let module = PurchaseStateManager.shared.currentModule else {
159
+ return false
131
160
  }
132
161
 
133
162
  return await withCheckedContinuation { continuation in
134
- // Store the continuation
135
- self.restoreContinuation = continuation
163
+ PurchaseStateManager.shared.activeRestoreContinuation = continuation
136
164
 
137
165
  // Send event to JavaScript
138
- self.sendEvent("onDelegateActionEvent", [
166
+ module.sendEvent("onDelegateActionEvent", [
139
167
  "type": "restore"
140
168
  ])
141
169
  }
@@ -179,8 +207,9 @@ public class HeliumPaywallSdkModule: Module {
179
207
  }
180
208
 
181
209
  // Function for JavaScript to provide purchase result
182
- Function("handlePurchaseResult") { [weak self] (statusString: String, errorMsg: String?) in
183
- guard let continuation = self?.purchaseContinuation else {
210
+ Function("handlePurchaseResult") { (statusString: String, errorMsg: String?) in
211
+ guard let continuation = PurchaseStateManager.shared.activePurchaseContinuation else {
212
+ print("WARNING: handlePurchaseResult called with no active continuation")
184
213
  return
185
214
  }
186
215
 
@@ -197,21 +226,23 @@ public class HeliumPaywallSdkModule: Module {
197
226
  default: status = .failed(PurchaseError.unknownStatus(status: lowercasedStatus))
198
227
  }
199
228
 
200
- // Clear the references
201
- self?.purchaseContinuation = nil
202
- self?.currentProductId = nil
229
+ // Clear singleton state
230
+ PurchaseStateManager.shared.clearPurchase()
203
231
 
204
232
  // Resume the continuation with the status
205
233
  continuation.resume(returning: status)
206
234
  }
207
235
 
208
236
  // Function for JavaScript to provide restore result
209
- Function("handleRestoreResult") { [weak self] (success: Bool) in
210
- guard let continuation = self?.restoreContinuation else {
237
+ Function("handleRestoreResult") { (success: Bool) in
238
+ guard let continuation = PurchaseStateManager.shared.activeRestoreContinuation else {
239
+ print("WARNING: handleRestoreResult called with no active continuation")
211
240
  return
212
241
  }
213
242
 
214
- self?.restoreContinuation = nil
243
+ // Clear singleton state
244
+ PurchaseStateManager.shared.clearRestore()
245
+
215
246
  continuation.resume(returning: success)
216
247
  }
217
248
 
@@ -219,23 +250,23 @@ public class HeliumPaywallSdkModule: Module {
219
250
  Helium.shared.presentUpsell(
220
251
  trigger: trigger,
221
252
  eventHandlers: PaywallEventHandlers.withHandlers(
222
- onOpen: { [weak self] event in
223
- self?.sendEvent("paywallEventHandlers", event.toDictionary())
253
+ onOpen: { event in
254
+ PurchaseStateManager.shared.currentModule?.sendEvent("paywallEventHandlers", event.toDictionary())
224
255
  },
225
- onClose: { [weak self] event in
226
- self?.sendEvent("paywallEventHandlers", event.toDictionary())
256
+ onClose: { event in
257
+ PurchaseStateManager.shared.currentModule?.sendEvent("paywallEventHandlers", event.toDictionary())
227
258
  },
228
- onDismissed: { [weak self] event in
229
- self?.sendEvent("paywallEventHandlers", event.toDictionary())
259
+ onDismissed: { event in
260
+ PurchaseStateManager.shared.currentModule?.sendEvent("paywallEventHandlers", event.toDictionary())
230
261
  },
231
- onPurchaseSucceeded: { [weak self] event in
232
- self?.sendEvent("paywallEventHandlers", event.toDictionary())
262
+ onPurchaseSucceeded: { event in
263
+ PurchaseStateManager.shared.currentModule?.sendEvent("paywallEventHandlers", event.toDictionary())
233
264
  },
234
- onOpenFailed: { [weak self] event in
235
- self?.sendEvent("paywallEventHandlers", event.toDictionary())
265
+ onOpenFailed: { event in
266
+ PurchaseStateManager.shared.currentModule?.sendEvent("paywallEventHandlers", event.toDictionary())
236
267
  },
237
- onCustomPaywallAction: { [weak self] event in
238
- self?.sendEvent("paywallEventHandlers", event.toDictionary())
268
+ onCustomPaywallAction: { event in
269
+ PurchaseStateManager.shared.currentModule?.sendEvent("paywallEventHandlers", event.toDictionary())
239
270
  }
240
271
  ),
241
272
  customPaywallTraits: convertMarkersToBooleans(customPaywallTraits),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-helium",
3
- "version": "3.0.14",
3
+ "version": "3.0.16",
4
4
  "description": "Helium paywalls expo sdk",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",