react-native-sdk-pianoio 0.3.2 → 0.3.4

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.
Files changed (37) hide show
  1. package/README.md +176 -186
  2. package/android/build.gradle +29 -42
  3. package/android/gradle.properties +33 -14
  4. package/android/src/main/java/com/sdkpianoio/ComposerPianoImpl.kt +366 -0
  5. package/android/src/main/java/com/sdkpianoio/SdkPianoioModule.kt +281 -507
  6. package/android/src/main/java/com/sdkpianoio/TokenService.kt +139 -0
  7. package/android/test.sh +494 -0
  8. package/ios/ComposerPianoImpl.swift +128 -225
  9. package/ios/MyComposerDelegate.swift +142 -109
  10. package/ios/SdkPianoio.swift +69 -143
  11. package/ios/SdkPianoioBridge.m +18 -46
  12. package/ios/TokenService.swift +219 -0
  13. package/lib/commonjs/NativeSdkPianoio.ts +34 -10
  14. package/lib/commonjs/PianoComposer.js +69 -51
  15. package/lib/commonjs/PianoComposer.js.map +1 -1
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/module/NativeSdkPianoio.ts +34 -10
  18. package/lib/module/PianoComposer.js +70 -51
  19. package/lib/module/PianoComposer.js.map +1 -1
  20. package/lib/module/index.js +0 -14
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/typescript/commonjs/src/NativeSdkPianoio.d.ts +27 -9
  23. package/lib/typescript/commonjs/src/NativeSdkPianoio.d.ts.map +1 -1
  24. package/lib/typescript/commonjs/src/PianoComposer.d.ts +45 -18
  25. package/lib/typescript/commonjs/src/PianoComposer.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  27. package/lib/typescript/module/src/NativeSdkPianoio.d.ts +27 -9
  28. package/lib/typescript/module/src/NativeSdkPianoio.d.ts.map +1 -1
  29. package/lib/typescript/module/src/PianoComposer.d.ts +45 -18
  30. package/lib/typescript/module/src/PianoComposer.d.ts.map +1 -1
  31. package/lib/typescript/module/src/index.d.ts.map +1 -1
  32. package/package.json +4 -1
  33. package/src/NativeSdkPianoio.ts +34 -10
  34. package/src/PianoComposer.tsx +76 -59
  35. package/src/index.tsx +0 -14
  36. package/android/src/main/AndroidManifestNew.xml +0 -2
  37. package/ios/services/TokenService.swift +0 -70
@@ -1,247 +1,150 @@
1
- //
2
- // ComposerPianoImpl.swift
3
- // SdkPianoio
4
- //
5
- //
6
1
  import Foundation
7
2
  import PianoComposer
8
3
  import PianoOAuth
9
4
  import React
10
5
 
6
+ /**
7
+ * ComposerPianoImpl - Main implementation class for the Piano SDK on iOS.
8
+ *
9
+ * This class is responsible for:
10
+ * - Managing the Piano SDK's Composer instance.
11
+ * - Configuring the experience request using a fluent builder pattern.
12
+ * - Executing Piano experiences and handling results via a delegate.
13
+ * - Managing React Native promises across asynchronous callbacks.
14
+ */
11
15
  @objcMembers public class ComposerPianoImpl: NSObject {
12
- private var composer: PianoComposer?
13
- private var delegateHelper: MyComposerDelegate?
14
16
 
15
- // These will hold the promise functions from React Native
16
- var promiseResolver: RCTPromiseResolveBlock?
17
- var promiseRejecter: RCTPromiseRejectBlock?
17
+ // MARK: - Properties
18
+
19
+ private var composer: PianoComposer?
20
+ private var tokenService: TokenService?
21
+ private var delegate: MyComposerDelegate?
22
+
23
+ // Promises to communicate results of async operations back to React Native
24
+ var experienceResolver: RCTPromiseResolveBlock?
25
+ var experienceRejecter: RCTPromiseRejectBlock?
26
+
27
+ // Stored configuration properties
28
+ private var tags: [String] = []
29
+ private var zoneId: String?
30
+ private var referrer: String?
31
+ private var url: String?
32
+ private var customVariables: [String: String] = [:]
33
+
34
+ // MARK: - Initialization
35
+
36
+ /**
37
+ * Initializes the implementation class and its dependencies.
38
+ *
39
+ * @param aid The Piano Application ID.
40
+ */
41
+ public func initialize(aid: String) {
42
+ print("ComposerPianoImpl: Initializing with AID: \(aid)")
43
+
44
+ // 1. Initialize and store the TokenService
45
+ self.tokenService = TokenService()
46
+ self.tokenService?.initialize(aid: aid)
47
+ print("ComposerPianoImpl: TokenService initialized")
48
+
49
+ // 2. Initialize the Composer instance
50
+ self.composer = PianoComposer(aid: aid, endpoint: PianoEndpoint.production)
51
+ print("ComposerPianoImpl: Composer instance created for PRODUCTION")
52
+
53
+ // 3. Initialize and set up the delegate
54
+ self.delegate = MyComposerDelegate(impl: self)
55
+ self.composer?.delegate = self.delegate
56
+ print("ComposerPianoImpl: Delegate initialized and set")
57
+ }
18
58
 
19
- // This is a static property to hold the AID
20
- public static var aid = ""
59
+ // MARK: - Configuration Methods
21
60
 
22
- @objc public func initializeComposer(_ aid: String) {
23
- ComposerPianoImpl.aid = aid
61
+ public func addTag(_ tag: String) {
62
+ self.tags.append(tag)
63
+ }
24
64
 
25
- // 1. Correctly initialize the TokenService first.
26
- let tokenService = TokenService()
65
+ public func addTags(_ tags: [String]) {
66
+ self.tags.append(contentsOf: tags)
67
+ }
27
68
 
28
- // 2. Initialize the delegate.
29
- self.delegateHelper = MyComposerDelegate()
69
+ public func setZoneId(_ zoneId: String) {
70
+ self.zoneId = zoneId
71
+ }
30
72
 
31
- // 3. Link the delegate back to this class so it can resolve promises.
32
- self.delegateHelper?.moduleImpl = self
73
+ public func setReferrer(_ referrer: String) {
74
+ self.referrer = referrer
75
+ }
33
76
 
34
- // 4. Initialize the composer.
35
- self.composer = PianoComposer(
36
- aid: ComposerPianoImpl.aid, endpoint: PianoEndpoint.productionEurope)
77
+ public func setUrl(_ url: String) {
78
+ self.url = url
79
+ }
80
+
81
+ public func setCustomVariable(_ name: String, value: String) {
82
+ self.customVariables[name] = value
83
+ }
37
84
 
38
- // 5. Set the composer's delegate property directly. This is the standard Swift way.
39
- self.composer?.delegate = self.delegateHelper
40
- }
85
+ public func setUserToken(_ token: String) {
86
+ // The iOS SDK requires setting the user token on the composer instance directly
87
+ _ = self.composer?.userToken(token)
88
+ }
41
89
 
42
- @objc public func executeComposer() {
43
- print("Esecuzione composer")
44
- DispatchQueue.main.async {
45
- if let composer = self.composer {
46
- print("Calling composer.execute()")
90
+ // MARK: - Execution Method
91
+
92
+ /**
93
+ * Executes the Piano experience with the current configuration.
94
+ *
95
+ * @param resolver The promise resolver from React Native.
96
+ * @param rejecter The promise rejecter from React Native.
97
+ */
98
+ public func executeExperience(
99
+ resolver: @escaping RCTPromiseResolveBlock,
100
+ rejecter: @escaping RCTPromiseRejectBlock
101
+ ) {
102
+ guard let composer = self.composer else {
103
+ rejecter("INIT_ERROR", "Composer not initialized.", nil)
104
+ return
105
+ }
106
+
107
+ print("ComposerPianoImpl: Executing experience")
108
+
109
+ // Store the promise functions to be called by the delegate later
110
+ self.experienceResolver = resolver
111
+ self.experienceRejecter = rejecter
112
+
113
+ // Configure the composer instance using the fluent interface
114
+ _ = composer
115
+ .tags(self.tags)
116
+ .zoneId(self.zoneId ?? "") // Safely unwrap optionals
117
+ .referrer(self.referrer ?? "") // Safely unwrap optionals
118
+ .url(self.url ?? "") // Safely unwrap optionals
119
+
120
+ for (key, value) in self.customVariables {
121
+ _ = composer.customVariable(name: key, value: value)
122
+ }
123
+
124
+ // Execute the experience. The result will be handled by the delegate.
47
125
  composer.execute()
48
- } else {
49
- print("⚠️ Composer è nil")
50
- self.promiseRejecter?(
51
- "composer_nil", "Piano Composer is not initialized.", nil)
52
- }
53
126
  }
54
- }
55
-
56
- @objc public func executeExperience(
57
- resolver: @escaping RCTPromiseResolveBlock,
58
- rejecter: @escaping RCTPromiseRejectBlock
59
- ) {
60
- self.promiseResolver = resolver
61
- self.promiseRejecter = rejecter
62
-
63
- self.executeComposer()
64
- }
65
-
66
- public func findViewBySelector(selector: String) -> UIView? {
67
- return nil
68
- }
69
-
70
- // --- Configuration methods ---
71
-
72
- @objc public func addTag(_ tag: String) {
73
- _ = composer?.tag(tag)
74
- }
75
-
76
- @objc public func addTags(_ tags: [String]) {
77
- _ = composer?.tags(tags)
78
- }
79
-
80
- @objc public func setZoneId(_ zoneId: String) {
81
- _ = composer?.zoneId(zoneId)
82
- }
83
-
84
- @objc public func setReferrer(_ referrer: String) {
85
- _ = composer?.referrer(referrer)
86
- }
87
-
88
- @objc public func setUrl(_ url: String) {
89
- _ = composer?.url(url)
90
- }
91
-
92
- @objc public func setCustomVariable(_ name: String, value: String) {
93
- _ = composer?.customVariable(name: name, value: value)
94
- }
95
-
96
- @objc public func setUserToken(_ token: String) {
97
- _ = composer?.userToken(token)
98
- }
99
-
100
- // method to get values and helpers
101
- @objc public func getAid() -> String {
102
- if composer != nil {
103
- return composer?.aid ?? ""
104
- } else {
105
- return ""
127
+
128
+ // MARK: - State Methods (Getters)
129
+
130
+ public func getTags() -> [String] {
131
+ return self.tags
106
132
  }
107
- }
108
-
109
- @objc public func getTags() -> [String] {
110
- if let tags = composer?.tags, !tags.isEmpty {
111
- return Array(tags)
112
- } else {
113
- return []
133
+
134
+ public func getZoneId() -> String {
135
+ return self.zoneId ?? ""
114
136
  }
115
- }
116
-
117
- @objc public func getComposer() -> PianoComposer? {
118
- return composer
119
- }
120
-
121
- @objc public func getZoneId() -> String {
122
- return composer?.zoneId ?? ""
123
- }
124
-
125
- @objc public func getReferrer() -> String {
126
- return composer?.referrer ?? ""
127
- }
128
-
129
- @objc public func getUrl() -> String {
130
- return composer?.url ?? ""
131
- }
132
-
133
- @objc public func getUserToken() -> String {
134
- return composer?.userToken ?? ""
135
- }
136
-
137
- // MARK: - Additional Methods for React Native Bridge
138
-
139
- @objc public func executeComposerWithResolver(
140
- resolver: @escaping RCTPromiseResolveBlock,
141
- rejecter: @escaping RCTPromiseRejectBlock
142
- ) {
143
- self.promiseResolver = resolver
144
- self.promiseRejecter = rejecter
145
- self.executeComposer()
146
- }
147
-
148
- @objc public func showLoginWithResolver(
149
- resolver: @escaping RCTPromiseResolveBlock,
150
- rejecter: @escaping RCTPromiseRejectBlock
151
- ) {
152
- self.promiseResolver = resolver
153
- self.promiseRejecter = rejecter
154
137
 
155
- DispatchQueue.main.async {
156
- if let composer = self.composer {
157
- // This would need to be implemented based on Piano SDK's login functionality
158
- print("Show login triggered")
159
- resolver(true)
160
- } else {
161
- rejecter("composer_nil", "Piano Composer is not initialized.", nil)
162
- }
138
+ public func getReferrer() -> String {
139
+ return self.referrer ?? ""
140
+ }
141
+
142
+ public func getUrl() -> String {
143
+ return self.url ?? ""
144
+ }
145
+
146
+ public func getUserToken() -> String {
147
+ // User token is write-only to the composer instance, so we return the stored value if needed.
148
+ return self.composer?.userToken ?? ""
163
149
  }
164
- }
165
-
166
- @objc public func showTemplateWithResolver(
167
- resolver: @escaping RCTPromiseResolveBlock,
168
- rejecter: @escaping RCTPromiseRejectBlock
169
- ) {
170
- self.promiseResolver = resolver
171
- self.promiseRejecter = rejecter
172
- self.executeComposer()
173
- }
174
-
175
- @objc public func showFormWithResolver(
176
- resolver: @escaping RCTPromiseResolveBlock,
177
- rejecter: @escaping RCTPromiseRejectBlock
178
- ) {
179
- self.promiseResolver = resolver
180
- self.promiseRejecter = rejecter
181
- self.executeComposer()
182
- }
183
-
184
- @objc public func showRecommendationsWithResolver(
185
- resolver: @escaping RCTPromiseResolveBlock,
186
- rejecter: @escaping RCTPromiseRejectBlock
187
- ) {
188
- self.promiseResolver = resolver
189
- self.promiseRejecter = rejecter
190
- self.executeComposer()
191
- }
192
-
193
- @objc public func nonSiteWithResolver(
194
- resolver: @escaping RCTPromiseResolveBlock,
195
- rejecter: @escaping RCTPromiseRejectBlock
196
- ) {
197
- self.promiseResolver = resolver
198
- self.promiseRejecter = rejecter
199
- self.executeComposer()
200
- }
201
-
202
- @objc public func userSegmentTrueWithResolver(
203
- resolver: @escaping RCTPromiseResolveBlock,
204
- rejecter: @escaping RCTPromiseRejectBlock
205
- ) {
206
- self.promiseResolver = resolver
207
- self.promiseRejecter = rejecter
208
- self.executeComposer()
209
- }
210
-
211
- @objc public func userSegmentFalseWithResolver(
212
- resolver: @escaping RCTPromiseResolveBlock,
213
- rejecter: @escaping RCTPromiseRejectBlock
214
- ) {
215
- self.promiseResolver = resolver
216
- self.promiseRejecter = rejecter
217
- self.executeComposer()
218
- }
219
-
220
- @objc public func meterActiveWithResolver(
221
- resolver: @escaping RCTPromiseResolveBlock,
222
- rejecter: @escaping RCTPromiseRejectBlock
223
- ) {
224
- self.promiseResolver = resolver
225
- self.promiseRejecter = rejecter
226
- self.executeComposer()
227
- }
228
-
229
- @objc public func meterExpiredWithResolver(
230
- resolver: @escaping RCTPromiseResolveBlock,
231
- rejecter: @escaping RCTPromiseRejectBlock
232
- ) {
233
- self.promiseResolver = resolver
234
- self.promiseRejecter = rejecter
235
- self.executeComposer()
236
- }
237
-
238
- @objc public func composerExecutionCompletedWithResolver(
239
- resolver: @escaping RCTPromiseResolveBlock,
240
- rejecter: @escaping RCTPromiseRejectBlock
241
- ) {
242
- self.promiseResolver = resolver
243
- self.promiseRejecter = rejecter
244
- self.executeComposer()
245
- }
246
-
247
150
  }
@@ -1,115 +1,148 @@
1
- //
2
- // MyComposerDelegate.swift
3
- // Pods
4
- //
5
- //
6
-
7
1
  import Foundation
8
2
  import PianoComposer
9
- import PianoOAuth
10
-
11
- // import PianoTemplate
3
+ import React
12
4
 
5
+ /**
6
+ * MyComposerDelegate - Implements the PianoComposerDelegate protocol to handle
7
+ * events from the native Piano SDK and communicate them back to React Native.
8
+ */
13
9
  @objcMembers public class MyComposerDelegate: NSObject, PianoComposerDelegate {
14
10
 
15
- // ✅ STEP 1: Add a weak reference back to the main implementation class.
16
- // This lets us access the stored promiseResolver and promiseRejecter.
17
- weak var moduleImpl: ComposerPianoImpl?
18
-
19
- // ✅ STEP 2: This is a generic helper function to resolve the promise.
20
- // We'll call this from all the different delegate methods.
21
- private func resolvePromise(with eventData: [String: Any]) {
22
- guard self.moduleImpl?.promiseResolver != nil else { return }
23
-
24
- self.moduleImpl?.promiseResolver?(eventData)
25
- self.clearPromiseHandlers()
26
- }
27
-
28
- private func rejectPromise(with error: Error) {
29
- guard self.moduleImpl?.promiseRejecter != nil else { return }
30
-
31
- let errorCode = "execution_failed"
32
- let errorMessage = error.localizedDescription
33
- self.moduleImpl?.promiseRejecter?(errorCode, errorMessage, error)
34
- self.clearPromiseHandlers()
35
- }
36
-
37
- private func clearPromiseHandlers() {
38
- self.moduleImpl?.promiseResolver = nil
39
- self.moduleImpl?.promiseRejecter = nil
40
- }
41
-
42
- // STEP 3: Implement the real delegate methods.
43
- // The Piano SDK will call these automatically.
44
-
45
- public func executeExperience(
46
- composer: PianoComposer, event: XpEvent
47
- ) {
48
- print("✅ Delegate received: executeExperience")
49
-
50
- // Correct: Access the experienceId from the public 'eventExecutionContext' object
51
- let experienceId = event.eventExecutionContext?.experienceId ?? ""
52
-
53
- let eventData: [String: Any] = [
54
- "eventType": event.eventType,
55
- "experienceId": experienceId,
56
- ]
57
-
58
- resolvePromise(with: eventData)
59
- }
60
-
61
- public func showTemplate(
62
- composer: PianoComposer, event: XpEvent, params: ShowTemplateEventParams?
63
- ) {
64
- print("✅ Delegate received: showTemplate")
65
-
66
- // Correct: Access the experienceId from the public 'eventExecutionContext' object
67
- let experienceId = event.eventExecutionContext?.experienceId ?? ""
68
-
69
- let eventData: [String: Any] = [
70
- "eventType": event.eventType,
71
- "experienceId": experienceId,
72
- "templateUrl": params?.templateUrl ?? "",
73
- "displayMode": params?.displayMode.rawValue ?? "",
74
- ]
75
-
76
- resolvePromise(with: eventData)
77
- }
78
-
79
- public func meterExpired(
80
- composer: PianoComposer, event: XpEvent, params: PageViewMeterEventParams?
81
- ) {
82
- print(" Delegate received: meterExpired")
83
-
84
- // Correct: Access the experienceId from the public 'eventExecutionContext' object
85
- let experienceId = event.eventExecutionContext?.experienceId ?? ""
86
-
87
- let eventData: [String: Any] = [
88
- "eventType": event.eventType,
89
- "experienceId": experienceId,
90
- "meterName": params?.meterName ?? "",
91
- ]
92
-
93
- resolvePromise(with: eventData)
94
- }
95
-
96
- public func composerExecutionFailed(_ composer: PianoComposer, error: Error) {
97
- print(" Delegate received: composerExecutionFailed")
98
- rejectPromise(with: error)
99
- }
100
-
101
- public func composerExecutionCompleted(composer: PianoComposer) {
102
- print("✅ Delegate received: composerExecutionCompleted")
103
- resolvePromise(with: ["eventType": "composerExecutionCompleted"])
104
- }
105
-
106
- public func experienceExecutionFailed(composer: PianoComposer, event: XpEvent, params: FailureEventParams?) {
107
- print("❌ Delegate received: experienceExecutionFailed")
108
- let error = NSError(domain: "PianoSDK", code: -1, userInfo: [NSLocalizedDescriptionKey: "Experience execution failed"])
109
- rejectPromise(with: error)
110
- }
111
-
112
- public func findViewBySelector(selector: String) -> UIView? {
113
- return nil
114
- }
11
+ // MARK: - Properties
12
+
13
+ /**
14
+ * A weak reference back to the main implementation class.
15
+ * This is used to access the stored promise resolver and rejecter
16
+ * and to avoid a strong reference cycle (memory leak).
17
+ */
18
+ private weak var impl: ComposerPianoImpl?
19
+
20
+ // MARK: - Initialization
21
+
22
+ /**
23
+ * Initializes the delegate with a reference back to the main implementation.
24
+ *
25
+ * @param impl The instance of ComposerPianoImpl.
26
+ */
27
+ init(impl: ComposerPianoImpl) {
28
+ self.impl = impl
29
+ }
30
+
31
+ // MARK: - Promise Handling Helpers
32
+
33
+ /**
34
+ * Resolves the stored promise with the provided event data.
35
+ *
36
+ * @param eventData A dictionary representing the event to be sent to JavaScript.
37
+ */
38
+ private func resolvePromise(with eventData: [String: Any]) {
39
+ guard let resolver = self.impl?.experienceResolver else { return }
40
+ resolver(eventData)
41
+ clearPromiseHandlers()
42
+ }
43
+
44
+ /**
45
+ * Rejects the stored promise with a given error.
46
+ *
47
+ * @param error The error that occurred.
48
+ */
49
+ private func rejectPromise(with error: Error) {
50
+ guard let rejecter = self.impl?.experienceRejecter else { return }
51
+ let errorCode = "EXECUTION_FAILED"
52
+ let errorMessage = error.localizedDescription
53
+ rejecter(errorCode, errorMessage, error)
54
+ clearPromiseHandlers()
55
+ }
56
+
57
+ /**
58
+ * Clears the stored promise handlers to prevent them from being called more than once.
59
+ */
60
+ private func clearPromiseHandlers() {
61
+ self.impl?.experienceResolver = nil
62
+ self.impl?.experienceRejecter = nil
63
+ }
64
+
65
+ // MARK: - PianoComposerDelegate Implementation
66
+
67
+ /**
68
+ * Called when a 'showTemplate' event is received from the Piano backend.
69
+ * This is the primary event for displaying a paywall or other experience.
70
+ */
71
+ public func showTemplate(composer: PianoComposer, event: XpEvent, params: ShowTemplateEventParams?) {
72
+ print("MyComposerDelegate: Received showTemplate event")
73
+ let eventData: [String: Any] = [
74
+ "eventType": "showTemplate",
75
+ "templateId": params?.templateId ?? "",
76
+ "templateVariantId": params?.templateVariantId ?? "",
77
+ "displayMode": params?.displayMode.rawValue ?? "inline",
78
+ "containerSelector": params?.containerSelector ?? ""
79
+ ]
80
+ resolvePromise(with: eventData)
81
+ }
82
+
83
+ /**
84
+ * Called when a meter is active.
85
+ */
86
+ public func meterActive(composer: PianoComposer, event: XpEvent, params: PageViewMeterEventParams?) {
87
+ print("MyComposerDelegate: Received meterActive event")
88
+ let eventData: [String: Any] = [
89
+ "eventType": "meterActive",
90
+ "meterName": params?.meterName ?? "",
91
+ "views": params?.views ?? 0,
92
+ "viewsLeft": params?.viewsLeft ?? 0,
93
+ "maxViews": params?.maxViews ?? 0
94
+ ]
95
+ resolvePromise(with: eventData)
96
+ }
97
+
98
+ /**
99
+ * Called when a meter has expired.
100
+ */
101
+ public func meterExpired(composer: PianoComposer, event: XpEvent, params: PageViewMeterEventParams?) {
102
+ print("MyComposerDelegate: Received meterExpired event")
103
+ let eventData: [String: Any] = [
104
+ "eventType": "meterExpired",
105
+ "meterName": params?.meterName ?? ""
106
+ ]
107
+ resolvePromise(with: eventData)
108
+ }
109
+
110
+ /**
111
+ * Called when the user segment is true.
112
+ */
113
+ public func userSegmentTrue(composer: PianoComposer, event: XpEvent) {
114
+ print("MyComposerDelegate: Received userSegmentTrue event")
115
+ resolvePromise(with: ["eventType": "userSegmentTrue"])
116
+ }
117
+
118
+ /**
119
+ * Called when the user segment is false.
120
+ */
121
+ public func userSegmentFalse(composer: PianoComposer, event: XpEvent) {
122
+ print("MyComposerDelegate: Received userSegmentFalse event")
123
+ resolvePromise(with: ["eventType": "userSegmentFalse"])
124
+ }
125
+
126
+ /**
127
+ * A general-purpose callback for when an experience is executed.
128
+ */
129
+ public func experienceExecute(composer: PianoComposer, event: XpEvent, params: ExperienceExecuteEventParams?) {
130
+ print("MyComposerDelegate: Received experienceExecute event")
131
+ // This is often a good place to resolve the promise if no other specific event is expected.
132
+ resolvePromise(with: ["eventType": "experienceExecute"])
133
+ }
134
+
135
+ /**
136
+ * Called when an error occurs during the experience execution.
137
+ */
138
+ public func experienceExecutionFailed(composer: PianoComposer, event: XpEvent, params: FailureEventParams?) {
139
+ print("MyComposerDelegate: Received experienceExecutionFailed event")
140
+ let error = NSError(
141
+ domain: "PianoSDK",
142
+ code: -1,
143
+ userInfo: [NSLocalizedDescriptionKey: "Experience execution failed by the server."]
144
+ )
145
+ rejectPromise(with: error)
146
+ }
115
147
  }
148
+