From 742b9507247e6d3c0df775e2605ad11a574d5c00 Mon Sep 17 00:00:00 2001 From: Sumeru Chatterjee Date: Mon, 13 Jan 2025 15:57:20 +0000 Subject: [PATCH] [MOB-10951] Add mobile framework info --- swift-sdk/Internal/InternalIterableAPI.swift | 71 ++----------------- .../IterableAPIMobileFrameworkDetector.swift | 69 ++++++++++++++++++ .../api-client/Request/RequestCreator.swift | 11 ++- .../Request/RequestProcessorProtocol.swift | 2 +- 4 files changed, 79 insertions(+), 74 deletions(-) create mode 100644 swift-sdk/Internal/IterableAPIMobileFrameworkDetector.swift diff --git a/swift-sdk/Internal/InternalIterableAPI.swift b/swift-sdk/Internal/InternalIterableAPI.swift index df754f63..6647ad7d 100644 --- a/swift-sdk/Internal/InternalIterableAPI.swift +++ b/swift-sdk/Internal/InternalIterableAPI.swift @@ -189,7 +189,10 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { hexToken = token.hexString() - let mobileFrameworkType = IterableAPIMobileFrameworkType.detect() + let mobileFrameworkInfo = config.mobileFrameworkInfo ?? IterableAPIMobileFrameworkInfo( + frameworkType: IterableAPIMobileFrameworkDetector.frameworkType(), + iterableSdkVersion: nil + ) let registerTokenInfo = RegisterTokenInfo(hexToken: token.hexString(), appName: appName, pushServicePlatform: config.pushPlatform, @@ -197,7 +200,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { deviceId: deviceId, deviceAttributes: deviceAttributes, sdkVersion: localStorage.sdkVersion, - mobileFrameworkInfo: IterableAPIMobileFrameworkInfo(frameworkType: mobileFrameworkType, iterableSdkVersion: nil)) + mobileFrameworkInfo: mobileFrameworkInfo) requestHandler.register(registerTokenInfo: registerTokenInfo, notificationStateProvider: notificationStateProvider, onSuccess: { (_ data: [AnyHashable: Any]?) in @@ -779,67 +782,3 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider { } } -extension IterableAPIMobileFrameworkType { - private struct FrameworkClasses { - static let flutter = [ - "FlutterViewController", - "GeneratedPluginRegistrant", - "FlutterEngine", - "FlutterPluginRegistry" - ] - - static let reactNative = [ - "RCTBridge", - "RCTRootView", - "RCTBundleURLProvider", - "RCTEventEmitter" - ] - } - - static func detect() -> IterableAPIMobileFrameworkType { - let bundle = Bundle.main - - // Helper function to check for framework classes - func hasFrameworkClasses(_ classNames: [String]) -> Bool { - // Consider framework present if ANY of its core classes are found - return classNames.contains { className in - bundle.classNamed(className) != nil - } - } - - // Check for multiple framework classes to increase confidence - let hasFlutter = hasFrameworkClasses(FrameworkClasses.flutter) - let hasReactNative = hasFrameworkClasses(FrameworkClasses.reactNative) - - switch (hasFlutter, hasReactNative) { - case (true, true): - ITBError("Both Flutter and React Native frameworks detected. This is unexpected.") - // In case of ambiguity, we could try to determine the primary framework - // by checking for more framework-specific indicators - if let mainBundle = Bundle.main.infoDictionary, - mainBundle["CFBundleExecutable"] as? String == "Runner" { - return .flutter // Flutter apps typically use "Runner" as executable name - } - return .reactNative - - case (true, false): - return .flutter - - case (false, true): - return .reactNative - - case (false, false): - // Additional check for framework-specific build settings or Info.plist entries - if let mainBundle = Bundle.main.infoDictionary { - if mainBundle["FlutterDeploymentTarget"] != nil { - return .flutter - } - if mainBundle["RNBundleURLProvider"] != nil { - return .reactNative - } - } - - return .native - } - } -} diff --git a/swift-sdk/Internal/IterableAPIMobileFrameworkDetector.swift b/swift-sdk/Internal/IterableAPIMobileFrameworkDetector.swift new file mode 100644 index 00000000..84ed8b92 --- /dev/null +++ b/swift-sdk/Internal/IterableAPIMobileFrameworkDetector.swift @@ -0,0 +1,69 @@ +import Foundation + +public final class IterableAPIMobileFrameworkDetector { + private struct FrameworkClasses { + static let flutter = [ + "FlutterViewController", + "GeneratedPluginRegistrant", + "FlutterEngine", + "FlutterPluginRegistry" + ] + + static let reactNative = [ + "RCTBridge", + "RCTRootView", + "RCTBundleURLProvider", + "RCTEventEmitter" + ] + } + + private static var cachedFrameworkType: IterableAPIMobileFrameworkType = { + detectFramework() + }() + + static func detectFramework() -> IterableAPIMobileFrameworkType { + let bundle = Bundle.main + + // Helper function to check for framework classes + func hasFrameworkClasses(_ classNames: [String]) -> Bool { + classNames.contains { className in + bundle.classNamed(className) != nil + } + } + + let hasFlutter = hasFrameworkClasses(FrameworkClasses.flutter) + let hasReactNative = hasFrameworkClasses(FrameworkClasses.reactNative) + + switch (hasFlutter, hasReactNative) { + case (true, true): + ITBError("Both Flutter and React Native frameworks detected. This is unexpected.") + if let mainBundle = Bundle.main.infoDictionary, + mainBundle["CFBundleExecutable"] as? String == "Runner" { + return .flutter + } + return .reactNative + + case (true, false): + return .flutter + + case (false, true): + return .reactNative + + case (false, false): + if let mainBundle = Bundle.main.infoDictionary { + if mainBundle["FlutterDeploymentTarget"] != nil { + return .flutter + } + if mainBundle["RNBundleURLProvider"] != nil { + return .reactNative + } + } + + return .native + } + } + + public static func frameworkType() -> IterableAPIMobileFrameworkType { + return cachedFrameworkType + } +} diff --git a/swift-sdk/Internal/api-client/Request/RequestCreator.swift b/swift-sdk/Internal/api-client/Request/RequestCreator.swift index 4fa0931d..a25d4191 100644 --- a/swift-sdk/Internal/api-client/Request/RequestCreator.swift +++ b/swift-sdk/Internal/api-client/Request/RequestCreator.swift @@ -53,15 +53,12 @@ struct RequestCreator { apnsType: registerTokenInfo.apnsType), JsonKey.applicationName: registerTokenInfo.appName, JsonKey.dataFields: dataFields, + JsonKey.mobileFramework: [ + JsonKey.frameworkType: registerTokenInfo.mobileFrameworkInfo.frameworkType, + JsonKey.iterableSdkVersion: registerTokenInfo.mobileFrameworkInfo] + ] - // Add mobile framework info if available - if let frameworkInfo = registerTokenInfo.mobileFrameworkInfo { - deviceDictionary[JsonKey.mobileFramework] = [ - JsonKey.frameworkType: frameworkInfo.frameworkType, - JsonKey.iterableSdkVersion: frameworkInfo.iterableSdkVersion - ] - } var body = [AnyHashable: Any]() diff --git a/swift-sdk/Internal/api-client/Request/RequestProcessorProtocol.swift b/swift-sdk/Internal/api-client/Request/RequestProcessorProtocol.swift index b80db3ae..1243605b 100644 --- a/swift-sdk/Internal/api-client/Request/RequestProcessorProtocol.swift +++ b/swift-sdk/Internal/api-client/Request/RequestProcessorProtocol.swift @@ -13,7 +13,7 @@ struct RegisterTokenInfo { let deviceId: String let deviceAttributes: [String: String] let sdkVersion: String? - let mobileFrameworkInfo: IterableAPIMobileFrameworkInfo? + let mobileFrameworkInfo: IterableAPIMobileFrameworkInfo } struct UpdateSubscriptionsInfo {