From 38f2791dd23929dfefd63949e950149829063fb5 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 6 Oct 2025 22:23:34 -0700 Subject: [PATCH 01/10] chore: update Iterable-iOS-SDK dependency to version 6.6.1 in podspec --- Iterable-React-Native-SDK.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iterable-React-Native-SDK.podspec b/Iterable-React-Native-SDK.podspec index 884417e1f..0d023409f 100644 --- a/Iterable-React-Native-SDK.podspec +++ b/Iterable-React-Native-SDK.podspec @@ -17,7 +17,7 @@ Pod::Spec.new do |s| s.private_header_files = "ios/**/*.h" # Load Iterables iOS SDK as a dependency - s.dependency "Iterable-iOS-SDK", "6.5.4.1" + s.dependency "Iterable-iOS-SDK", "6.6.1" # Basic Swift support s.pod_target_xcconfig = { From e36be70967b7d0b01b002ec3a2003f4ad4d6442a Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 6 Oct 2025 22:24:37 -0700 Subject: [PATCH 02/10] chore: remove tempfix and update Embed Pods Frameworks build phase in Xcode project --- example/ios/Podfile | 2 -- .../project.pbxproj | 21 ++----------------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index 89b12bd69..43e1952fb 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -26,8 +26,6 @@ target 'ReactNativeSdkExample' do :app_path => "#{Pod::Config.instance.installation_root}/.." ) - pod 'Iterable-iOS-SDK', :git => 'https://github.com/Iterable/iterable-swift-sdk.git', :branch => 'hotfix/MOB-12091-temp-fix' - post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( diff --git a/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj b/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj index fe2da2b97..70285d987 100644 --- a/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj +++ b/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj @@ -174,8 +174,8 @@ 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 70E3A2A47E764F7A78602595 /* [CP] Embed Pods Frameworks */, EDF40E5EF2B0A60C77B1B71B /* [CP] Copy Pods Resources */, + 0CB5E31E0822E1F3F03A4481 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -282,24 +282,7 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 24A6D3DBDA584D8F55796A6D /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 89B6BEF2485B9536DDD45973 /* [CP] Embed Pods Frameworks */ = { + 0CB5E31E0822E1F3F03A4481 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( From 356e76784c55ce51daf8df9bff0738e301732d65 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 6 Oct 2025 22:26:02 -0700 Subject: [PATCH 03/10] feat: implement user notification handling and remote notification registration in AppDelegate --- .../ReactNativeSdkExample/AppDelegate.swift | 99 +++++++++++++++++-- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/example/ios/ReactNativeSdkExample/AppDelegate.swift b/example/ios/ReactNativeSdkExample/AppDelegate.swift index 5b9504eb5..927836431 100644 --- a/example/ios/ReactNativeSdkExample/AppDelegate.swift +++ b/example/ios/ReactNativeSdkExample/AppDelegate.swift @@ -5,10 +5,13 @@ // Created by Loren Posen on 6/11/25. // +import UIKit import React -import ReactAppDependencyProvider import React_RCTAppDelegate -import UIKit +import ReactAppDependencyProvider +import UserNotifications + +import IterableSDK @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -21,6 +24,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { + NSLog("FINISHED LAUNCHING WITH OPTIONS") + ITBInfo() + let delegate = ReactNativeDelegate() let factory = RCTReactNativeFactory(delegate: delegate) delegate.dependencyProvider = RCTAppDependencyProvider() @@ -36,8 +42,73 @@ class AppDelegate: UIResponder, UIApplicationDelegate { launchOptions: launchOptions ) + setupUserNotificationCenter() + return true } + + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + NSLog("REGISTERED FOR REMOTE NOTIFICATIONS") + ITBInfo() + IterableAPI.register(token: deviceToken) + } + + func application(_ application: UIApplication, + didFailToRegisterForRemoteNotificationsWithError + error: Error) { + NSLog("FAILED TO REGISTER FOR REMOTE NOTIFICATIONS") + ITBInfo("error: \(error)") + } + + func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + NSLog("RECEIVED REMOTE NOTIFICATIONS") + ITBInfo() + IterableAppIntegration.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler) + } + + func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + NSLog("RECEIVED UNIVERSAL LINK") + NSLog("userActivity: \(userActivity)") + ITBInfo() + guard let url = userActivity.webpageURL else { + return false + } + + return IterableAPI.handle(universalLink: url) + } + + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + NSLog("OPEN URL") + NSLog("url: \(url)") + NSLog("options: \(options)") + ITBInfo() + return RCTLinkingManager.application(app, open: url, options: options) + } + + private func setupUserNotificationCenter() { + UNUserNotificationCenter.current().delegate = self + UNUserNotificationCenter.current().getNotificationSettings { settings in + if settings.authorizationStatus != .authorized { + ITBInfo("Not authorized") + // not authorized, ask for permission + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, _ in + ITBInfo("auth: \(success)") + if success { + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() + } + } + // TODO: Handle error etc. + } + } else { + // already authorized + ITBInfo("Already authorized") + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() + } + } + } + } } class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { @@ -46,10 +117,24 @@ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { } override func bundleURL() -> URL? { - #if DEBUG - RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") - #else - Bundle.main.url(forResource: "main", withExtension: "jsbundle") - #endif +#if DEBUG + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") +#else + Bundle.main.url(forResource: "main", withExtension: "jsbundle") +#endif + } +} + +extension AppDelegate: UNUserNotificationCenterDelegate { + // App is running in the foreground + public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + NSLog("WILL PRESENT NOTIFICATION") + completionHandler([.alert, .badge, .sound]) + } + + // The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from applicationDidFinishLaunching:. + public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + NSLog("DID RECEIVE NOTIFICATION RESPONSE") + IterableAppIntegration.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler) } } From 04cb0d1050f9acf1fe5c4cdb2c8be4b94b527f38 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 6 Oct 2025 22:32:17 -0700 Subject: [PATCH 04/10] feat: add pauseAuthRetries method and enhance auth failure handling in ReactIterableAPI --- ios/RNIterableAPI/RNIterableAPI.mm | 8 ++++++++ ios/RNIterableAPI/ReactIterableAPI.swift | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index a7490f2ee..91955f797 100644 --- a/ios/RNIterableAPI/RNIterableAPI.mm +++ b/ios/RNIterableAPI/RNIterableAPI.mm @@ -273,6 +273,10 @@ - (void)passAlongAuthToken:(NSString *_Nullable)authToken { [_swiftAPI passAlongAuthToken:authToken]; } +- (void)pauseAuthRetries:(BOOL)pauseRetry { + [_swiftAPI pauseAuthRetries:pauseRetry]; +} + - (void)wakeApp { // Placeholder function -- this method is only used in Android } @@ -499,6 +503,10 @@ - (void)wakeApp { [_swiftAPI passAlongAuthToken:authToken]; } +RCT_EXPORT_METHOD(pauseAuthRetries : (BOOL)pauseRetry) { + [_swiftAPI pauseAuthRetries:pauseRetry]; +} + RCT_EXPORT_METHOD(wakeApp) { // Placeholder function -- this method is only used in Android } diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index 163e34199..7f0c511cd 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -484,6 +484,12 @@ import React authHandlerSemaphore.signal() } + @objc(pauseAuthRetries:) + public func pauseAuthRetries(pauseRetry: Bool) { + ITBInfo() + IterableAPI.pauseAuthRetries(pauseRetry) + } + // MARK: Private private var shouldEmit = false private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self)) @@ -662,6 +668,19 @@ extension ReactIterableAPI: IterableInAppDelegate { } extension ReactIterableAPI: IterableAuthDelegate { + public func onAuthFailure(_ authFailure: IterableSDK.AuthFailure) { + ITBInfo() + + var failureDict: [String: Any] = [:] + failureDict["userKey"] = authFailure.userKey + failureDict["failedAuthToken"] = authFailure.failedAuthToken + failureDict["failedRequestTime"] = authFailure.failedRequestTime + failureDict["failureReason"] = authFailure.failureReason.rawValue + + sendEvent(withName: EventName.handleAuthFailureCalled.rawValue, + body: failureDict) + } + public func onAuthTokenRequested(completion: @escaping AuthTokenRetrievalHandler) { ITBInfo() DispatchQueue.global(qos: .userInitiated).async { @@ -682,6 +701,8 @@ extension ReactIterableAPI: IterableAuthDelegate { DispatchQueue.main.async { completion(nil) } + // TODO: RN should be able to handle nil case as well. Or we can wrap this up under one of the existing AuthFailure. But again, its not a authFailure in this one. Its a timeout error. + // TODO: Create a Dictionary representing AuthFailure object due to `null` auth token and pass it in body instead of passing `nil` self.delegate?.sendEvent( withName: EventName.handleAuthFailureCalled.rawValue, body: nil as Any?) From 47cdab36280d09602b876fbdc9b99284ea1c3083 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 6 Oct 2025 23:05:04 -0700 Subject: [PATCH 05/10] chore: update Xcode project configuration and enable authHandler in IterableAppProvider --- .../project.pbxproj | 74 +++++++++---------- example/src/hooks/useIterableApp.tsx | 28 +++---- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj b/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj index 70285d987..2bf23431b 100644 --- a/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj +++ b/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj @@ -10,9 +10,9 @@ 00E356F31AD99517003FC87E /* ReactNativeSdkExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeSdkExampleTests.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 779227342DFA3FB500D69EC0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779227332DFA3FB500D69EC0 /* AppDelegate.swift */; }; - 77F63EC390061314C0718D51 /* libPods-ReactNativeSdkExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F395BEFC7809290D1773C84F /* libPods-ReactNativeSdkExample.a */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; A3A40C20801B8F02005FA4C0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1FC6B09E65A7BD9F6864C5D8 /* PrivacyInfo.xcprivacy */; }; + CC7C0C660DB585466CC95446 /* libPods-ReactNativeSdkExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D7C71B2515F0E53180477AEC /* libPods-ReactNativeSdkExample.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -29,19 +29,19 @@ 00E356EE1AD99517003FC87E /* ReactNativeSdkExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeSdkExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* ReactNativeSdkExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeSdkExampleTests.m; sourceTree = ""; }; + 054F9627BFE1F378023F2570 /* Pods-ReactNativeSdkExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.debug.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* ReactNativeSdkExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactNativeSdkExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeSdkExample/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeSdkExample/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeSdkExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; 1FC6B09E65A7BD9F6864C5D8 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeSdkExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 627A5082522E8122626A42E9 /* Pods-ReactNativeSdkExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.debug.xcconfig"; sourceTree = ""; }; + 2BE74655C68E80463F6CD81B /* Pods-ReactNativeSdkExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.release.xcconfig"; sourceTree = ""; }; 779227312DFA3FB500D69EC0 /* ReactNativeSdkExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactNativeSdkExample-Bridging-Header.h"; sourceTree = ""; }; 779227322DFA3FB500D69EC0 /* ReactNativeSdkExampleTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactNativeSdkExampleTests-Bridging-Header.h"; sourceTree = ""; }; 779227332DFA3FB500D69EC0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = ReactNativeSdkExample/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeSdkExample/LaunchScreen.storyboard; sourceTree = ""; }; - C37A515B34C484F156F48110 /* Pods-ReactNativeSdkExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.release.xcconfig"; sourceTree = ""; }; + D7C71B2515F0E53180477AEC /* libPods-ReactNativeSdkExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeSdkExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - F395BEFC7809290D1773C84F /* libPods-ReactNativeSdkExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeSdkExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -56,7 +56,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 77F63EC390061314C0718D51 /* libPods-ReactNativeSdkExample.a in Frameworks */, + CC7C0C660DB585466CC95446 /* libPods-ReactNativeSdkExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,7 +99,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - F395BEFC7809290D1773C84F /* libPods-ReactNativeSdkExample.a */, + D7C71B2515F0E53180477AEC /* libPods-ReactNativeSdkExample.a */, ); name = Frameworks; sourceTree = ""; @@ -138,8 +138,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - 627A5082522E8122626A42E9 /* Pods-ReactNativeSdkExample.debug.xcconfig */, - C37A515B34C484F156F48110 /* Pods-ReactNativeSdkExample.release.xcconfig */, + 054F9627BFE1F378023F2570 /* Pods-ReactNativeSdkExample.debug.xcconfig */, + 2BE74655C68E80463F6CD81B /* Pods-ReactNativeSdkExample.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -169,13 +169,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeSdkExample" */; buildPhases = ( - 00A09C8D745F4A4962CFCB16 /* [CP] Check Pods Manifest.lock */, + 787BEB56F90C9C0AEE4C88D5 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - EDF40E5EF2B0A60C77B1B71B /* [CP] Copy Pods Resources */, - 0CB5E31E0822E1F3F03A4481 /* [CP] Embed Pods Frameworks */, + 152370F00B0C82FBF20ABDA2 /* [CP] Embed Pods Frameworks */, + 6099F4827CE15646F9A0205B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -244,28 +244,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 00A09C8D745F4A4962CFCB16 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ReactNativeSdkExample-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -282,7 +260,7 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 0CB5E31E0822E1F3F03A4481 /* [CP] Embed Pods Frameworks */ = { + 152370F00B0C82FBF20ABDA2 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -299,7 +277,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - EDF40E5EF2B0A60C77B1B71B /* [CP] Copy Pods Resources */ = { + 6099F4827CE15646F9A0205B /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -316,6 +294,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; + 787BEB56F90C9C0AEE4C88D5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ReactNativeSdkExample-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -406,7 +406,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 627A5082522E8122626A42E9 /* Pods-ReactNativeSdkExample.debug.xcconfig */; + baseConfigurationReference = 054F9627BFE1F378023F2570 /* Pods-ReactNativeSdkExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -436,7 +436,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C37A515B34C484F156F48110 /* Pods-ReactNativeSdkExample.release.xcconfig */; + baseConfigurationReference = 2BE74655C68E80463F6CD81B /* Pods-ReactNativeSdkExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/example/src/hooks/useIterableApp.tsx b/example/src/hooks/useIterableApp.tsx index e561b22f8..80d039619 100644 --- a/example/src/hooks/useIterableApp.tsx +++ b/example/src/hooks/useIterableApp.tsx @@ -167,20 +167,20 @@ export const IterableAppProvider: FunctionComponent< config.inAppHandler = () => IterableInAppShowResponse.show; // NOTE: Uncomment to test authHandler failure - // config.authHandler = () => { - // console.log(`authHandler`); - - // return Promise.resolve({ - // authToken: 'SomethingNotValid', - // successCallback: () => { - // console.log(`authHandler > success`); - // }, - // // This is not firing - // failureCallback: () => { - // console.log(`authHandler > failure`); - // }, - // }); - // }; + config.authHandler = () => { + console.log(`authHandler`); + + return Promise.resolve({ + authToken: 'SomethingNotValid', + successCallback: () => { + console.log(`authHandler > success`); + }, + // This is not firing + failureCallback: () => { + console.log(`authHandler > failure`); + }, + }); + }; setItblConfig(config); From b22b9eaefe2fe41d2906a6c6a70cef7f7d3070b3 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 6 Oct 2025 23:12:15 -0700 Subject: [PATCH 06/10] refactor: delegate event sending for auth failure handling in ReactIterableAPI --- ios/RNIterableAPI/ReactIterableAPI.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index 7f0c511cd..f04b08e42 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -677,8 +677,9 @@ extension ReactIterableAPI: IterableAuthDelegate { failureDict["failedRequestTime"] = authFailure.failedRequestTime failureDict["failureReason"] = authFailure.failureReason.rawValue - sendEvent(withName: EventName.handleAuthFailureCalled.rawValue, - body: failureDict) + delegate?.sendEvent( + withName: EventName.handleAuthFailureCalled.rawValue, + body: failureDict) } public func onAuthTokenRequested(completion: @escaping AuthTokenRetrievalHandler) { From 16c2c9ab88bc64bcfd35a40855812cbb180ab487 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 6 Oct 2025 23:20:28 -0700 Subject: [PATCH 07/10] fix: improve JWT error logging and alert messaging for auth failures --- example/src/hooks/useIterableApp.tsx | 39 ++++++++++++--------- src/core/enums/IterableAuthFailureReason.ts | 22 ++++++------ 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/example/src/hooks/useIterableApp.tsx b/example/src/hooks/useIterableApp.tsx index 80d039619..d648dd25c 100644 --- a/example/src/hooks/useIterableApp.tsx +++ b/example/src/hooks/useIterableApp.tsx @@ -15,6 +15,7 @@ import { IterableInAppShowResponse, IterableLogLevel, IterableRetryBackoff, + IterableAuthFailureReason, } from '@iterable/react-native-sdk'; import { Route } from '../constants/routes'; @@ -134,9 +135,15 @@ export const IterableAppProvider: FunctionComponent< }; config.onJWTError = (authFailure) => { - console.error('Error fetching JWT:', authFailure); + console.log('onJWTError', authFailure); + + const failureReason = + typeof authFailure.failureReason === 'string' + ? authFailure.failureReason + : IterableAuthFailureReason[authFailure.failureReason]; + Alert.alert( - `Error fetching JWT: ${authFailure.failureReason}`, + `Error fetching JWT: ${failureReason}`, `Token: ${authFailure.failedAuthToken}` ); }; @@ -167,20 +174,20 @@ export const IterableAppProvider: FunctionComponent< config.inAppHandler = () => IterableInAppShowResponse.show; // NOTE: Uncomment to test authHandler failure - config.authHandler = () => { - console.log(`authHandler`); - - return Promise.resolve({ - authToken: 'SomethingNotValid', - successCallback: () => { - console.log(`authHandler > success`); - }, - // This is not firing - failureCallback: () => { - console.log(`authHandler > failure`); - }, - }); - }; + // config.authHandler = () => { + // console.log(`authHandler`); + + // return Promise.resolve({ + // authToken: 'SomethingNotValid', + // successCallback: () => { + // console.log(`authHandler > success`); + // }, + // // This is not firing + // failureCallback: () => { + // console.log(`authHandler > failure`); + // }, + // }); + // }; setItblConfig(config); diff --git a/src/core/enums/IterableAuthFailureReason.ts b/src/core/enums/IterableAuthFailureReason.ts index a86c6f782..a61f7fa7e 100644 --- a/src/core/enums/IterableAuthFailureReason.ts +++ b/src/core/enums/IterableAuthFailureReason.ts @@ -8,32 +8,32 @@ export enum IterableAuthFailureReason { * An auth token's expiration must be less than one year from its issued-at * time. */ - AUTH_TOKEN_EXPIRATION_INVALID = 'AUTH_TOKEN_EXPIRATION_INVALID', + AUTH_TOKEN_EXPIRATION_INVALID, /** The token has expired. */ - AUTH_TOKEN_EXPIRED = 'AUTH_TOKEN_EXPIRED', + AUTH_TOKEN_EXPIRED, /** Token has an invalid format (failed a regular expression check). */ - AUTH_TOKEN_FORMAT_INVALID = 'AUTH_TOKEN_FORMAT_INVALID', + AUTH_TOKEN_FORMAT_INVALID, /** `onAuthTokenRequested` threw an exception. */ - AUTH_TOKEN_GENERATION_ERROR = 'AUTH_TOKEN_GENERATION_ERROR', + AUTH_TOKEN_GENERATION_ERROR, /** Any other error not captured by another constant. */ - AUTH_TOKEN_GENERIC_ERROR = 'AUTH_TOKEN_GENERIC_ERROR', + AUTH_TOKEN_GENERIC_ERROR, /** Iterable has invalidated this token and it cannot be used. */ - AUTH_TOKEN_INVALIDATED = 'AUTH_TOKEN_INVALIDATED', + AUTH_TOKEN_INVALIDATED, /** The request to Iterable's API did not include a JWT authorization header. */ - AUTH_TOKEN_MISSING = 'AUTH_TOKEN_MISSING', + AUTH_TOKEN_MISSING, /** `onAuthTokenRequested` returned a null JWT token. */ - AUTH_TOKEN_NULL = 'AUTH_TOKEN_NULL', + AUTH_TOKEN_NULL, /** * Iterable could not decode the token's payload (`iat`, `exp`, `email`, * or `userId`). */ - AUTH_TOKEN_PAYLOAD_INVALID = 'AUTH_TOKEN_PAYLOAD_INVALID', + AUTH_TOKEN_PAYLOAD_INVALID, /** Iterable could not validate the token's authenticity. */ - AUTH_TOKEN_SIGNATURE_INVALID = 'AUTH_TOKEN_SIGNATURE_INVALID', + AUTH_TOKEN_SIGNATURE_INVALID, /** * The token doesn't include an `email` or a `userId`. Or, one of these * values is included, but it references a user that isn't in the Iterable * project. */ - AUTH_TOKEN_USER_KEY_INVALID = 'AUTH_TOKEN_USER_KEY_INVALID', + AUTH_TOKEN_USER_KEY_INVALID, } From edfa70f1ecff61464a0e74c528c5575fe6aeb2d1 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Thu, 9 Oct 2025 04:16:26 -0700 Subject: [PATCH 08/10] refactor: remove unnecessary NSLog statements from AppDelegate.swift --- example/ios/ReactNativeSdkExample/AppDelegate.swift | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/example/ios/ReactNativeSdkExample/AppDelegate.swift b/example/ios/ReactNativeSdkExample/AppDelegate.swift index 927836431..677a4fa9d 100644 --- a/example/ios/ReactNativeSdkExample/AppDelegate.swift +++ b/example/ios/ReactNativeSdkExample/AppDelegate.swift @@ -24,7 +24,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { - NSLog("FINISHED LAUNCHING WITH OPTIONS") ITBInfo() let delegate = ReactNativeDelegate() @@ -48,7 +47,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - NSLog("REGISTERED FOR REMOTE NOTIFICATIONS") ITBInfo() IterableAPI.register(token: deviceToken) } @@ -56,19 +54,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { - NSLog("FAILED TO REGISTER FOR REMOTE NOTIFICATIONS") ITBInfo("error: \(error)") } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - NSLog("RECEIVED REMOTE NOTIFICATIONS") ITBInfo() IterableAppIntegration.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler) } func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { - NSLog("RECEIVED UNIVERSAL LINK") - NSLog("userActivity: \(userActivity)") ITBInfo() guard let url = userActivity.webpageURL else { return false @@ -78,9 +72,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { - NSLog("OPEN URL") - NSLog("url: \(url)") - NSLog("options: \(options)") ITBInfo() return RCTLinkingManager.application(app, open: url, options: options) } @@ -128,13 +119,11 @@ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { extension AppDelegate: UNUserNotificationCenterDelegate { // App is running in the foreground public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - NSLog("WILL PRESENT NOTIFICATION") completionHandler([.alert, .badge, .sound]) } // The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from applicationDidFinishLaunching:. public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { - NSLog("DID RECEIVE NOTIFICATION RESPONSE") IterableAppIntegration.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler) } } From d95ae90d0170dcd5a950966c390492c2fc7c4ef8 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 13 Oct 2025 10:24:19 -0700 Subject: [PATCH 09/10] fix: standardize authentication failure reason representation across platforms --- src/core/enums/IterableAuthFailureReason.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/enums/IterableAuthFailureReason.ts b/src/core/enums/IterableAuthFailureReason.ts index a61f7fa7e..51c610c4f 100644 --- a/src/core/enums/IterableAuthFailureReason.ts +++ b/src/core/enums/IterableAuthFailureReason.ts @@ -2,6 +2,10 @@ * The reason for the failure of an authentication attempt. * * This is generally related to JWT token validation. + * + * FIXME: Android returns the string (EG: `'AUTH_TOKEN_EXPIRATION_INVALID'`), + * but iOS returns the enum value (EG: `0`). These should be standardized so + * that they both return the same type on either platform. */ export enum IterableAuthFailureReason { /** From 70fa36a2a8f065255e4bc4f90a9a6724d1fd0fae Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Tue, 14 Oct 2025 15:15:06 -0700 Subject: [PATCH 10/10] feat: implement retry policy configuration in IterableConfig for iOS --- .../project.pbxproj | 58 +++++++++---------- ios/RNIterableAPI/Serialization.swift | 12 ++++ 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj b/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj index 2bf23431b..74e4dc4c9 100644 --- a/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj +++ b/example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj @@ -11,8 +11,8 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 779227342DFA3FB500D69EC0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779227332DFA3FB500D69EC0 /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + 81F6A9EA0E1CCC1AD730C5D9 /* libPods-ReactNativeSdkExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 56080B9DEED42A97AD1B3D5C /* libPods-ReactNativeSdkExample.a */; }; A3A40C20801B8F02005FA4C0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1FC6B09E65A7BD9F6864C5D8 /* PrivacyInfo.xcprivacy */; }; - CC7C0C660DB585466CC95446 /* libPods-ReactNativeSdkExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D7C71B2515F0E53180477AEC /* libPods-ReactNativeSdkExample.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -29,18 +29,18 @@ 00E356EE1AD99517003FC87E /* ReactNativeSdkExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeSdkExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* ReactNativeSdkExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeSdkExampleTests.m; sourceTree = ""; }; - 054F9627BFE1F378023F2570 /* Pods-ReactNativeSdkExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.debug.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* ReactNativeSdkExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactNativeSdkExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeSdkExample/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeSdkExample/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeSdkExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; 1FC6B09E65A7BD9F6864C5D8 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeSdkExample/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 2BE74655C68E80463F6CD81B /* Pods-ReactNativeSdkExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.release.xcconfig"; sourceTree = ""; }; + 3A95ED4563D4389808EDEA8F /* Pods-ReactNativeSdkExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.debug.xcconfig"; sourceTree = ""; }; + 56080B9DEED42A97AD1B3D5C /* libPods-ReactNativeSdkExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeSdkExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 779227312DFA3FB500D69EC0 /* ReactNativeSdkExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactNativeSdkExample-Bridging-Header.h"; sourceTree = ""; }; 779227322DFA3FB500D69EC0 /* ReactNativeSdkExampleTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactNativeSdkExampleTests-Bridging-Header.h"; sourceTree = ""; }; 779227332DFA3FB500D69EC0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = ReactNativeSdkExample/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeSdkExample/LaunchScreen.storyboard; sourceTree = ""; }; - D7C71B2515F0E53180477AEC /* libPods-ReactNativeSdkExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeSdkExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + EA19B65827A1D757CC5AAC97 /* Pods-ReactNativeSdkExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeSdkExample.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -56,7 +56,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CC7C0C660DB585466CC95446 /* libPods-ReactNativeSdkExample.a in Frameworks */, + 81F6A9EA0E1CCC1AD730C5D9 /* libPods-ReactNativeSdkExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,7 +99,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - D7C71B2515F0E53180477AEC /* libPods-ReactNativeSdkExample.a */, + 56080B9DEED42A97AD1B3D5C /* libPods-ReactNativeSdkExample.a */, ); name = Frameworks; sourceTree = ""; @@ -138,8 +138,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - 054F9627BFE1F378023F2570 /* Pods-ReactNativeSdkExample.debug.xcconfig */, - 2BE74655C68E80463F6CD81B /* Pods-ReactNativeSdkExample.release.xcconfig */, + 3A95ED4563D4389808EDEA8F /* Pods-ReactNativeSdkExample.debug.xcconfig */, + EA19B65827A1D757CC5AAC97 /* Pods-ReactNativeSdkExample.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -169,13 +169,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeSdkExample" */; buildPhases = ( - 787BEB56F90C9C0AEE4C88D5 /* [CP] Check Pods Manifest.lock */, + B07642200E1BCDE7A80934E9 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 152370F00B0C82FBF20ABDA2 /* [CP] Embed Pods Frameworks */, - 6099F4827CE15646F9A0205B /* [CP] Copy Pods Resources */, + 756F1571292F7FB66FB0F625 /* [CP] Embed Pods Frameworks */, + C5D9D662E100C568A4F9922D /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -260,7 +260,7 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 152370F00B0C82FBF20ABDA2 /* [CP] Embed Pods Frameworks */ = { + 756F1571292F7FB66FB0F625 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -277,43 +277,43 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 6099F4827CE15646F9A0205B /* [CP] Copy Pods Resources */ = { + B07642200E1BCDE7A80934E9 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ReactNativeSdkExample-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 787BEB56F90C9C0AEE4C88D5 /* [CP] Check Pods Manifest.lock */ = { + C5D9D662E100C568A4F9922D /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ReactNativeSdkExample-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeSdkExample/Pods-ReactNativeSdkExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -406,7 +406,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 054F9627BFE1F378023F2570 /* Pods-ReactNativeSdkExample.debug.xcconfig */; + baseConfigurationReference = 3A95ED4563D4389808EDEA8F /* Pods-ReactNativeSdkExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -436,7 +436,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2BE74655C68E80463F6CD81B /* Pods-ReactNativeSdkExample.release.xcconfig */; + baseConfigurationReference = EA19B65827A1D757CC5AAC97 /* Pods-ReactNativeSdkExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/ios/RNIterableAPI/Serialization.swift b/ios/RNIterableAPI/Serialization.swift index 478262924..3f837ab2c 100644 --- a/ios/RNIterableAPI/Serialization.swift +++ b/ios/RNIterableAPI/Serialization.swift @@ -94,6 +94,18 @@ extension IterableConfig { } } + if let retryPolicyDict = dict["retryPolicy"] as? [AnyHashable: Any] { + if let maxRetry = retryPolicyDict["maxRetry"] as? Int, + let retryInterval = retryPolicyDict["retryInterval"] as? TimeInterval, + let retryBackoffString = retryPolicyDict["retryBackoff"] as? String + { + let retryBackoffType: RetryPolicy.BackoffType = + retryBackoffString == "EXPONENTIAL" ? .exponential : .linear + config.retryPolicy = RetryPolicy( + maxRetry: maxRetry, retryInterval: retryInterval, retryBackoff: retryBackoffType) + } + } + return config }