diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m b/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m index 28bb3b3b3..3d55a7128 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m @@ -450,7 +450,7 @@ - (void)testSetSafeTrackServiceURLWithUserTrackingDomain { XCTAssertEqualObjects(storedUrl, expectedUrl); storedUrl = [[BNCServerAPI sharedInstance] validationServiceURL]; - expectedUrl = @"https://links.toTestDomain.com/v1/app-link-settings"; + expectedUrl = @"https://api3.branch.io/v1/app-link-settings"; XCTAssertEqualObjects(storedUrl, expectedUrl); [BNCServerAPI sharedInstance].useTrackingDomain = NO; @@ -501,7 +501,7 @@ - (void)testSetSafeTrackServiceURLWithOutUserTrackingDomain { XCTAssertEqualObjects(storedUrl, expectedUrl); storedUrl = [[BNCServerAPI sharedInstance] validationServiceURL]; - expectedUrl = @"https://links.toTestDomain.com/v1/app-link-settings"; + expectedUrl = @"https://api3.branch.io/v1/app-link-settings"; XCTAssertEqualObjects(storedUrl, expectedUrl); [BNCServerAPI sharedInstance].useTrackingDomain = NO; diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m index 38d1b0cc2..d1dccffcc 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m @@ -65,7 +65,7 @@ - (void)testSetODMandSDKRequests { } - (void)testODMTimeOut { - + NSString* requestUUID = [[NSUUID UUID ] UUIDString]; NSNumber* requestCreationTimeStamp = BNCWireFormatFromDate([NSDate date]); NSString *odm = @"testODMString"; @@ -80,21 +80,23 @@ - (void)testODMTimeOut { sleep(10); - NSDictionary *jsonOpen = [factory dataForOpenWithURLString:@"https://branch.io"]; + BNCRequestFactory *factory2 = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:requestUUID TimeStamp:requestCreationTimeStamp]; + NSDictionary *jsonOpen = [factory2 dataForOpenWithURLString:@"https://branch.io"]; XCTAssertTrue(![odm isEqualToString:[jsonOpen objectForKey:@"odm_info"]]); self.prefHelper.odmInfo = nil; self.prefHelper.odmInfoInitDate = nil; - + } - (void) testODMAPIsNotLoaded { + XCTestExpectation *expectation = [self expectationWithDescription:@"Check if ODCManager class is loaded."]; - [[BNCODMInfoCollector instance ] loadODMInfoWithTimeOut:DISPATCH_TIME_FOREVER andCompletionHandler:^(NSString * _Nullable odmInfo, NSError * _Nullable error) { - if (error.code == BNCClassNotFoundError){ - [expectation fulfill]; - } + [[BNCODMInfoCollector instance ] loadODMInfoWithCompletionHandler:^(NSString * _Nullable odmInfo, NSError * _Nullable error) { + if (error.code == BNCClassNotFoundError){ + [expectation fulfill]; + } }]; [self waitForExpectationsWithTimeout:15 handler:nil]; } diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m index 5269501b3..8c9a427b3 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m @@ -414,4 +414,27 @@ - (void)testSaveContentAnalyticsManifest { XCTAssertEqualObjects(retrievedManifest, dummyManifest); } +- (void)testThirdPartyAPIsTimeoutDefaultValue { + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, 0.5, + @"Default third party APIs timeout should be 0.5 seconds"); +} + +- (void)testThirdPartyAPIsTimeoutSetterGetter { + NSTimeInterval testTimeout1 = 1.0; + self.prefHelper.thirdPartyAPIsWaitTime = testTimeout1; + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, testTimeout1, + @"Third party APIs timeout should be settable and retrievable"); + + NSTimeInterval testTimeout2 = 2.5; + self.prefHelper.thirdPartyAPIsWaitTime = testTimeout2; + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, testTimeout2, + @"Third party APIs timeout should be updatable"); + + NSTimeInterval testTimeout3 = 0.1; + self.prefHelper.thirdPartyAPIsWaitTime = testTimeout3; + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, testTimeout3, + @"Third party APIs timeout should support small values"); +} + + @end diff --git a/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m b/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m index cf2bce94c..fd5ee92c3 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m @@ -20,6 +20,7 @@ - (void)writeObjectToDefaults:(NSString *)key value:(NSObject *)value; @interface BranchClassTests : XCTestCase @property (nonatomic, strong) Branch *branch; +@property (nonatomic, strong, readwrite) BNCPreferenceHelper *prefHelper; @end @implementation BranchClassTests @@ -27,6 +28,7 @@ @implementation BranchClassTests - (void)setUp { [super setUp]; self.branch = [Branch getInstance]; + self.prefHelper = [BNCPreferenceHelper sharedInstance]; } - (void)tearDown { @@ -261,6 +263,102 @@ - (void)testSetConsumerProtectionAttributionLevel { } +- (void)testBranchSetSDKWaitTimeForThirdPartyAPIs { + // Test Branch instance method for setting timeout + NSTimeInterval testTimeout = 2.0; + [Branch setSDKWaitTimeForThirdPartyAPIs:testTimeout]; + + // Verify it was set in the preference helper + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, testTimeout, + @"Branch setSDKWaitTimeForThirdPartyAPIs should update preference helper"); +} + +- (void)testBranchSetSDKWaitTimeForThirdPartyAPIsMultipleValues { + // Test setting multiple different values + NSArray *testValues = @[@0.5, @1.0, @1.5, @3.0, @5.0]; + + for (NSNumber *timeoutValue in testValues) { + NSTimeInterval timeout = [timeoutValue doubleValue]; + [Branch setSDKWaitTimeForThirdPartyAPIs:timeout]; + + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, timeout, + @"Branch setSDKWaitTimeForThirdPartyAPIs should handle value %.1f", timeout); + } +} + +- (void)testTimeoutIntegrationWithPreferenceHelper { + // Test that Branch and PreferenceHelper work together correctly + NSTimeInterval branchTimeout = 1.8; + NSTimeInterval directTimeout = 2.3; + + // Set via Branch + [Branch setSDKWaitTimeForThirdPartyAPIs:branchTimeout]; + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, branchTimeout, + @"Wait Time set via Branch should be readable from PreferenceHelper"); + + // Set directly on PreferenceHelper + self.prefHelper.thirdPartyAPIsWaitTime = directTimeout; + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, directTimeout, + @"Wait time set directly should be readable via Branch"); +} + +- (void)testTimeoutValueConsistency { + // Test that the same instance maintains consistent values + NSTimeInterval testTimeout = 1.25; + + [Branch setSDKWaitTimeForThirdPartyAPIs:testTimeout]; + + // Read multiple times to ensure consistency + for (int i = 0; i < 5; i++) { + XCTAssertEqual(self.prefHelper.thirdPartyAPIsWaitTime, testTimeout, + @"Timeout value should remain consistent across multiple reads"); + } +} + +- (void)testBranchSetSDKWaitTimeForThirdPartyAPIsInvalidLowValues { + + NSArray *invalidLowValues = @[@0.0, @-1.0, @-0.5]; + NSTimeInterval originalTimeout = [BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime; + + for (NSNumber *timeoutValue in invalidLowValues) { + NSTimeInterval timeout = [timeoutValue doubleValue]; + [Branch setSDKWaitTimeForThirdPartyAPIs:timeout]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime, originalTimeout, + @"Branch setSDKWaitTimeForThirdPartyAPIs should reject invalid low value %.3f", timeout); + } +} + +- (void)testBranchsetSDKWaitTimeForThirdPartyAPIsInvalidHighValues { + + NSArray *invalidHighValues = @[@10.1, @15.0, @30.0, @60.0]; + NSTimeInterval originalTimeout = [BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime; + + for (NSNumber *timeoutValue in invalidHighValues) { + NSTimeInterval timeout = [timeoutValue doubleValue]; + [Branch setSDKWaitTimeForThirdPartyAPIs:timeout]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime, originalTimeout, + @"Branch setSDKWaitTimeForThirdPartyAPIs should reject invalid high value %.3f", timeout); + } +} + +- (void)testBranchSetSDKWaitTimeForThirdPartyAPIsBoundaryValues { + + // Test exactly 10.0 (should be valid) + [Branch setSDKWaitTimeForThirdPartyAPIs:10.0]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime, 10.0, + @"Timeout of exactly 10.0 seconds should be valid"); + + // Test just over 10.0 (should be invalid) + [Branch setSDKWaitTimeForThirdPartyAPIs:10.0001]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime, 10.0, + @"Timeout of 10.0001 seconds should be rejected"); + + // Test very small positive value (should be valid) + [Branch setSDKWaitTimeForThirdPartyAPIs:0.0001]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime, 0.0001, + @"Very small positive timeout should be valid"); +} + - (void)testSetAnonID { NSString *expectedAnonID = @"static-test-anon-id-12345"; @@ -358,5 +456,4 @@ - (void)testSetAnonID_ThreadSafety { @"One of the anonID values should be set"); } - @end diff --git a/Branch-TestBed/Branch-TestBed-CI.xctestplan b/Branch-TestBed/Branch-TestBed-CI.xctestplan index 260e13c5a..5db9a583a 100644 --- a/Branch-TestBed/Branch-TestBed-CI.xctestplan +++ b/Branch-TestBed/Branch-TestBed-CI.xctestplan @@ -13,6 +13,7 @@ }, "testTargets" : [ { + "enabled" : false, "target" : { "containerPath" : "container:Branch-TestBed.xcodeproj", "identifier" : "E7AC745F2DB064BC002D8C40", diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/Branch-TestBed.xctestplan b/Branch-TestBed/Branch-TestBed.xcodeproj/Branch-TestBed.xctestplan index f4b6ec1ea..cb6dc38fe 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/Branch-TestBed.xctestplan +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/Branch-TestBed.xctestplan @@ -37,6 +37,11 @@ } }, { + "skippedTests" : [ + "Reflection_ODM_Tests", + "Reflection_ODM_Tests\/testODMAPICall", + "Reflection_ODM_Tests\/testODMAPIsLoaded" + ], "target" : { "containerPath" : "container:Branch-TestBed.xcodeproj", "identifier" : "E7AC745F2DB064BC002D8C40", diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj index ee284e032..3668d1f55 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj @@ -234,13 +234,11 @@ E56394312CC7AC9F00E18E65 /* BranchFileLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E563942F2CC7AC9500E18E65 /* BranchFileLogger.m */; }; E71E397B2DD3C14800110F59 /* BNCInAppBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = E71E397A2DD3C14800110F59 /* BNCInAppBrowser.h */; }; E72489D228E40D0200DCD8FD /* PasteControlViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E72489D128E40D0200DCD8FD /* PasteControlViewController.m */; }; + E732827F2E5F7D92005CACC8 /* GoogleAdsOnDeviceConversion in Frameworks */ = {isa = PBXBuildFile; productRef = E732827E2E5F7D92005CACC8 /* GoogleAdsOnDeviceConversion */; }; E7A728BD2AA9A112009343B7 /* BNCAPIServerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E7A728BC2AA9A112009343B7 /* BNCAPIServerTest.m */; }; E7AC74572DB0639E002D8C40 /* BNCODMInfoCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = E7AC74562DB0639E002D8C40 /* BNCODMInfoCollector.h */; }; E7AC74592DB063C6002D8C40 /* BNCODMInfoCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = E7AC74582DB063C6002D8C40 /* BNCODMInfoCollector.m */; }; E7AC745B2DB06407002D8C40 /* BNCODMTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7AC745A2DB06407002D8C40 /* BNCODMTests.m */; }; - E7AC74702DB06992002D8C40 /* GULLogger in Frameworks */ = {isa = PBXBuildFile; productRef = E7AC746F2DB06992002D8C40 /* GULLogger */; }; - E7AC74722DB06992002D8C40 /* GULNetwork in Frameworks */ = {isa = PBXBuildFile; productRef = E7AC74712DB06992002D8C40 /* GULNetwork */; }; - E7AC74752DB069B2002D8C40 /* nanopb in Frameworks */ = {isa = PBXBuildFile; productRef = E7AC74742DB069B2002D8C40 /* nanopb */; }; E7AC747B2DB0700D002D8C40 /* BranchSDK in Frameworks */ = {isa = PBXBuildFile; productRef = E7AC747A2DB0700D002D8C40 /* BranchSDK */; }; E7AC747E2DB0714B002D8C40 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E7AC747D2DB07145002D8C40 /* libc++.tbd */; }; E7AE4A092DFB2C4400696805 /* BranchConfigurationControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7AE4A082DFB2C4400696805 /* BranchConfigurationControllerTests.m */; }; @@ -279,6 +277,13 @@ remoteGlobalIDString = 466B58371B17773000A69EDE; remoteInfo = Branch; }; + E73282812E5F81C9005CACC8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 670016581940F51400A9E103 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6700165F1940F51400A9E103; + remoteInfo = "Branch-TestBed"; + }; E7AC74652DB064BC002D8C40 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 670016581940F51400A9E103 /* Project object */; @@ -631,9 +636,7 @@ files = ( E7AC747E2DB0714B002D8C40 /* libc++.tbd in Frameworks */, E7AC747B2DB0700D002D8C40 /* BranchSDK in Frameworks */, - E7AC74752DB069B2002D8C40 /* nanopb in Frameworks */, - E7AC74722DB06992002D8C40 /* GULNetwork in Frameworks */, - E7AC74702DB06992002D8C40 /* GULLogger in Frameworks */, + E732827F2E5F7D92005CACC8 /* GoogleAdsOnDeviceConversion in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1182,16 +1185,15 @@ ); dependencies = ( E7AC74662DB064BC002D8C40 /* PBXTargetDependency */, + E73282822E5F81C9005CACC8 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( E7AC74612DB064BC002D8C40 /* Reflection_ODM_Tests */, ); name = Reflection_ODM_Tests; packageProductDependencies = ( - E7AC746F2DB06992002D8C40 /* GULLogger */, - E7AC74712DB06992002D8C40 /* GULNetwork */, - E7AC74742DB069B2002D8C40 /* nanopb */, E7AC747A2DB0700D002D8C40 /* BranchSDK */, + E732827E2E5F7D92005CACC8 /* GoogleAdsOnDeviceConversion */, ); productName = Reflection_ODM_Tests; productReference = E7AC74602DB064BC002D8C40 /* Reflection_ODM_Tests.xctest */; @@ -1254,6 +1256,7 @@ }; E7AC745F2DB064BC002D8C40 = { CreatedOnToolsVersion = 16.2; + TestTargetID = 6700165F1940F51400A9E103; }; F1D4F9AB1F323F01002D13FF = { CreatedOnToolsVersion = 8.3.1; @@ -1273,9 +1276,8 @@ ); mainGroup = 670016571940F51400A9E103; packageReferences = ( - E7AC746E2DB06992002D8C40 /* XCRemoteSwiftPackageReference "GoogleUtilities" */, - E7AC74732DB069B2002D8C40 /* XCRemoteSwiftPackageReference "nanopb" */, E7AC74792DB0700D002D8C40 /* XCLocalSwiftPackageReference "../../ios-branch-deep-linking-attribution" */, + E732827D2E5F7D92005CACC8 /* XCRemoteSwiftPackageReference "google-ads-on-device-conversion-ios-sdk" */, ); productRefGroup = 670016611940F51400A9E103 /* Products */; projectDirPath = ""; @@ -1555,6 +1557,11 @@ target = 466B58371B17773000A69EDE /* Branch */; targetProxy = 5F8B7B4121B5F5CD009CE0A6 /* PBXContainerItemProxy */; }; + E73282822E5F81C9005CACC8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6700165F1940F51400A9E103 /* Branch-TestBed */; + targetProxy = E73282812E5F81C9005CACC8 /* PBXContainerItemProxy */; + }; E7AC74662DB064BC002D8C40 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 466B58371B17773000A69EDE /* Branch */; @@ -1891,6 +1898,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1899,7 +1907,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = R63EM248DP; - ENABLE_USER_SCRIPT_SANDBOXING = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -1912,6 +1920,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Branch-TestBed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Branch-TestBed"; }; name = Debug; }; @@ -1919,6 +1928,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1928,7 +1938,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = R63EM248DP; - ENABLE_USER_SCRIPT_SANDBOXING = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -1941,6 +1951,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Branch-TestBed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Branch-TestBed"; }; name = Release; }; @@ -2067,39 +2078,21 @@ /* End XCLocalSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */ - E7AC746E2DB06992002D8C40 /* XCRemoteSwiftPackageReference "GoogleUtilities" */ = { + E732827D2E5F7D92005CACC8 /* XCRemoteSwiftPackageReference "google-ads-on-device-conversion-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/google/GoogleUtilities"; + repositoryURL = "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 8.0.2; - }; - }; - E7AC74732DB069B2002D8C40 /* XCRemoteSwiftPackageReference "nanopb" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/nanopb/nanopb"; - requirement = { - kind = revision; - revision = b7e1104502eca3a213b46303391ca4d3bc8ddec1; + minimumVersion = 2.3.0; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - E7AC746F2DB06992002D8C40 /* GULLogger */ = { - isa = XCSwiftPackageProductDependency; - package = E7AC746E2DB06992002D8C40 /* XCRemoteSwiftPackageReference "GoogleUtilities" */; - productName = GULLogger; - }; - E7AC74712DB06992002D8C40 /* GULNetwork */ = { - isa = XCSwiftPackageProductDependency; - package = E7AC746E2DB06992002D8C40 /* XCRemoteSwiftPackageReference "GoogleUtilities" */; - productName = GULNetwork; - }; - E7AC74742DB069B2002D8C40 /* nanopb */ = { + E732827E2E5F7D92005CACC8 /* GoogleAdsOnDeviceConversion */ = { isa = XCSwiftPackageProductDependency; - package = E7AC74732DB069B2002D8C40 /* XCRemoteSwiftPackageReference "nanopb" */; - productName = nanopb; + package = E732827D2E5F7D92005CACC8 /* XCRemoteSwiftPackageReference "google-ads-on-device-conversion-ios-sdk" */; + productName = GoogleAdsOnDeviceConversion; }; E7AC747A2DB0700D002D8C40 /* BranchSDK */ = { isa = XCSwiftPackageProductDependency; diff --git a/Branch-TestBed/Reflection_ODM_Tests.xctestplan b/Branch-TestBed/Reflection_ODM_Tests.xctestplan index 2130d2322..dd8cbb34e 100644 --- a/Branch-TestBed/Reflection_ODM_Tests.xctestplan +++ b/Branch-TestBed/Reflection_ODM_Tests.xctestplan @@ -14,6 +14,11 @@ "testTargets" : [ { "parallelizable" : true, + "skippedTests" : [ + "Reflection_ODM_Tests", + "Reflection_ODM_Tests\/testODMAPICall", + "Reflection_ODM_Tests\/testODMAPIsLoaded" + ], "target" : { "containerPath" : "container:Branch-TestBed.xcodeproj", "identifier" : "E7AC745F2DB064BC002D8C40", diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/Info.plist b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/Info.plist deleted file mode 100755 index 93a878439..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/Info.plist +++ /dev/null @@ -1,40 +0,0 @@ - - - - - AvailableLibraries - - - LibraryIdentifier - ios-arm64 - LibraryPath - AppAdsOnDeviceConversion.framework - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - - - LibraryIdentifier - ios-arm64_x86_64-simulator - LibraryPath - AppAdsOnDeviceConversion.framework - SupportedArchitectures - - arm64 - x86_64 - - SupportedPlatform - ios - SupportedPlatformVariant - simulator - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/AppAdsOnDeviceConversion b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/AppAdsOnDeviceConversion deleted file mode 100755 index fa2869aa1..000000000 Binary files a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/AppAdsOnDeviceConversion and /dev/null differ diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/AppAdsOnDeviceConversion.h b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/AppAdsOnDeviceConversion.h deleted file mode 100755 index a66e1722c..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/AppAdsOnDeviceConversion.h +++ /dev/null @@ -1,2 +0,0 @@ -#import -#import diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/ODCConversionManager.h b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/ODCConversionManager.h deleted file mode 100755 index 3319bb50e..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/ODCConversionManager.h +++ /dev/null @@ -1,44 +0,0 @@ -#import - -#import "ODCConversionTypes.h" - -NS_ASSUME_NONNULL_BEGIN - -/// The top level on-device conversion manager singleton that provides methods for fetching the -/// aggregate conversion info for conversion reports. -NS_SWIFT_NAME(ConversionManager) -@interface ODCConversionManager : NSObject - -/// Returns the shared ConversionManager instance. -@property(class, nonatomic, readonly) ODCConversionManager *sharedInstance; - -/// The SDK version in the format of three period-separated integers, such as "10.14.1". -@property(nonatomic, readonly) NSString *versionString; - -/// Sets the timestamp when the application was first launched. -/// @param firstLaunchTime The timestamp when the application was first launched. -- (void)setFirstLaunchTime:(NSDate *)firstLaunchTime; - -/// Asynchronously fetches the aggregate conversion info of the current app instance for conversion -/// reports. -/// -/// The aggregate conversion info fetch could fail due to network failures etc. -/// -/// @param interaction The type of interaction to fetch. -/// @param completion The completion handler to call when the fetch is complete. This handler is -/// executed on a system-defined global concurrent queue. -/// This completion handler takes the following parameters: -/// aggregateConversionInfo The aggregate conversion info of the current app instance, or -/// `nil` if it's not available. -/// error An error object that indicates why the request failed, or `nil` if the request -/// was successful. -/// When the aggregate conversion info is expired, both parameters are nil, i.e. the aggregate -/// conversion info is not available and there is no error. -- (void)fetchAggregateConversionInfoForInteraction:(ODCInteractionType)interaction - completion: - (void (^)(NSString *_Nullable aggregateConversionInfo, - NSError *_Nullable error))completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/ODCConversionTypes.h b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/ODCConversionTypes.h deleted file mode 100755 index 9e1bb03d6..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Headers/ODCConversionTypes.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - -/// The type of interaction for fetching conversion info. -typedef NS_ENUM(NSInteger, ODCInteractionType) { - ODCInteractionTypeInstallation, -} NS_SWIFT_NAME(InteractionType); diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Info.plist b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Info.plist deleted file mode 100755 index c1282dfff..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleExecutable - AppAdsOnDeviceConversion - CFBundleIdentifier - com.google.ads.AppAdsOnDeviceConversion - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - AppAdsOnDeviceConversion - CFBundlePackageType - FMWK - CFBundleVersion - 0.8.21 - CFBundleShortVersionString - 0.8.21 - DTSDKName - iphonesimulator11.2 - MinimumOSVersion - 100.0 - - diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Modules/module.modulemap b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Modules/module.modulemap deleted file mode 100755 index 17233f6b8..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64/AppAdsOnDeviceConversion.framework/Modules/module.modulemap +++ /dev/null @@ -1,7 +0,0 @@ -framework module AppAdsOnDeviceConversion { - umbrella header "AppAdsOnDeviceConversion.h" - export * - module * { export * } - link framework "Foundation" - link "c++" -} diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/AppAdsOnDeviceConversion b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/AppAdsOnDeviceConversion deleted file mode 100755 index 873b8adba..000000000 Binary files a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/AppAdsOnDeviceConversion and /dev/null differ diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/AppAdsOnDeviceConversion.h b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/AppAdsOnDeviceConversion.h deleted file mode 100755 index a66e1722c..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/AppAdsOnDeviceConversion.h +++ /dev/null @@ -1,2 +0,0 @@ -#import -#import diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/ODCConversionManager.h b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/ODCConversionManager.h deleted file mode 100755 index 3319bb50e..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/ODCConversionManager.h +++ /dev/null @@ -1,44 +0,0 @@ -#import - -#import "ODCConversionTypes.h" - -NS_ASSUME_NONNULL_BEGIN - -/// The top level on-device conversion manager singleton that provides methods for fetching the -/// aggregate conversion info for conversion reports. -NS_SWIFT_NAME(ConversionManager) -@interface ODCConversionManager : NSObject - -/// Returns the shared ConversionManager instance. -@property(class, nonatomic, readonly) ODCConversionManager *sharedInstance; - -/// The SDK version in the format of three period-separated integers, such as "10.14.1". -@property(nonatomic, readonly) NSString *versionString; - -/// Sets the timestamp when the application was first launched. -/// @param firstLaunchTime The timestamp when the application was first launched. -- (void)setFirstLaunchTime:(NSDate *)firstLaunchTime; - -/// Asynchronously fetches the aggregate conversion info of the current app instance for conversion -/// reports. -/// -/// The aggregate conversion info fetch could fail due to network failures etc. -/// -/// @param interaction The type of interaction to fetch. -/// @param completion The completion handler to call when the fetch is complete. This handler is -/// executed on a system-defined global concurrent queue. -/// This completion handler takes the following parameters: -/// aggregateConversionInfo The aggregate conversion info of the current app instance, or -/// `nil` if it's not available. -/// error An error object that indicates why the request failed, or `nil` if the request -/// was successful. -/// When the aggregate conversion info is expired, both parameters are nil, i.e. the aggregate -/// conversion info is not available and there is no error. -- (void)fetchAggregateConversionInfoForInteraction:(ODCInteractionType)interaction - completion: - (void (^)(NSString *_Nullable aggregateConversionInfo, - NSError *_Nullable error))completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/ODCConversionTypes.h b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/ODCConversionTypes.h deleted file mode 100755 index 9e1bb03d6..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Headers/ODCConversionTypes.h +++ /dev/null @@ -1,6 +0,0 @@ -#import - -/// The type of interaction for fetching conversion info. -typedef NS_ENUM(NSInteger, ODCInteractionType) { - ODCInteractionTypeInstallation, -} NS_SWIFT_NAME(InteractionType); diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Info.plist b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Info.plist deleted file mode 100755 index c1282dfff..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleExecutable - AppAdsOnDeviceConversion - CFBundleIdentifier - com.google.ads.AppAdsOnDeviceConversion - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - AppAdsOnDeviceConversion - CFBundlePackageType - FMWK - CFBundleVersion - 0.8.21 - CFBundleShortVersionString - 0.8.21 - DTSDKName - iphonesimulator11.2 - MinimumOSVersion - 100.0 - - diff --git a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Modules/module.modulemap b/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Modules/module.modulemap deleted file mode 100755 index 17233f6b8..000000000 --- a/Branch-TestBed/Reflection_ODM_Tests/Framework/AppAdsOnDeviceConversion.xcframework/ios-arm64_x86_64-simulator/AppAdsOnDeviceConversion.framework/Modules/module.modulemap +++ /dev/null @@ -1,7 +0,0 @@ -framework module AppAdsOnDeviceConversion { - umbrella header "AppAdsOnDeviceConversion.h" - export * - module * { export * } - link framework "Foundation" - link "c++" -} diff --git a/Branch-TestBed/Reflection_ODM_Tests/Reflection_ODM_Tests.m b/Branch-TestBed/Reflection_ODM_Tests/Reflection_ODM_Tests.m index 1b8b73232..d65973e0f 100644 --- a/Branch-TestBed/Reflection_ODM_Tests/Reflection_ODM_Tests.m +++ b/Branch-TestBed/Reflection_ODM_Tests/Reflection_ODM_Tests.m @@ -22,9 +22,16 @@ - (void) testODMAPIsLoaded { XCTestExpectation *expectation = [self expectationWithDescription:@"Network call"]; - [[BNCODMInfoCollector instance] fetchODMInfoFromDeviceWithInitDate:[NSDate date] andCompletion:^(NSString * _Nonnull odmInfo, NSError * _Nonnull error) { - if ((error.code != BNCClassNotFoundError) && (error.code != BNCMethodNotFoundError)){ + [[BNCODMInfoCollector instance] fetchODMInfoFromDeviceWithInitDate:[NSDate date] andCompletion:^(NSString * _Nullable odmInfo, NSError * _Nullable error) { + if ( !error) { [expectation fulfill]; + } else { + if ((error.code != BNCClassNotFoundError) && (error.code != BNCMethodNotFoundError)) { + [expectation fulfill]; + } else { + XCTFail(@"Unexpected ODM error: %@", error.localizedDescription); + [expectation fulfill]; + } } }]; @@ -32,21 +39,25 @@ - (void) testODMAPIsLoaded { } + - (void) testODMAPICall { - + XCTestExpectation *expectation = [self expectationWithDescription:@"Network call"]; [BNCPreferenceHelper sharedInstance].odmInfo = nil; - [[BNCODMInfoCollector instance ] loadODMInfoWithTimeOut:15 andCompletionHandler:^(NSString * _Nullable odmInfo, NSError * _Nullable error) { - if ((error.code != BNCClassNotFoundError) && (error.code != BNCMethodNotFoundError)){ + [[BNCODMInfoCollector instance ] loadODMInfoWithCompletionHandler:^(NSString * _Nullable odmInfo, NSError * _Nullable error) { + if (!error){ if (odmInfo) { XCTAssertTrue([odmInfo isEqualToString:[BNCPreferenceHelper sharedInstance].odmInfo]); XCTAssertTrue([BNCPreferenceHelper sharedInstance].odmInfoInitDate != nil); } XCTAssertTrue((error == nil), "%s", [[error description] UTF8String]); [expectation fulfill]; + } else { + XCTFail(@"Unexpected ODM error: %@", error.localizedDescription); + [expectation fulfill]; } }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; + [self waitForExpectationsWithTimeout:15 handler:nil]; } @end diff --git a/Sources/BranchSDK/BNCODMInfoCollector.m b/Sources/BranchSDK/BNCODMInfoCollector.m index 09bb519d6..d17f2ad19 100644 --- a/Sources/BranchSDK/BNCODMInfoCollector.m +++ b/Sources/BranchSDK/BNCODMInfoCollector.m @@ -22,8 +22,6 @@ @interface BNCODMInfoCollector() @implementation BNCODMInfoCollector -@synthesize odmInfo = _odmInfo; - + (BNCODMInfoCollector *)instance { static BNCODMInfoCollector *collector = nil; static dispatch_once_t onceToken = 0; @@ -41,51 +39,28 @@ - (instancetype)init { return self; } -- (void) setOdmInfo:(NSString *)odmInfo { - _odmInfo = odmInfo; -} - -- (NSString *) odmInfo { - @synchronized (self) { - // Load ODM info with a time-out of 500 ms. Its must for next call to v1/open. - if (!_odmInfo) { - [self loadODMInfoWithTimeOut:dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_MSEC)) andCompletionHandler:nil]; // Timeout after 500 ms - } - - if (_odmInfo) { - // Check if odmInfo is within validity window - NSDate *initTime = self.preferenceHelper.odmInfoInitDate; - NSTimeInterval validityWindow = self.preferenceHelper.odmInfoValidityWindow; - if ([self isWithinValidityWindow:initTime timeInterval:validityWindow]) { - // fetch ODM info from pref helper - _odmInfo = self.preferenceHelper.odmInfo; - } else { - _odmInfo = nil; - } - } - return _odmInfo; - } -} - -- (void)loadODMInfo { - [self loadODMInfoWithTimeOut: DISPATCH_TIME_FOREVER andCompletionHandler:nil]; -} - -- (void)loadODMInfoWithTimeOut:(dispatch_time_t) timeOut andCompletionHandler:(void (^_Nullable)(NSString * _Nullable odmInfo, NSError * _Nullable error))completion { +- (void)loadODMInfoWithCompletionHandler:(void (^_Nullable)(NSString * _Nullable odmInfo, NSError * _Nullable error))completion { - if (self.preferenceHelper.odmInfo) { - self.odmInfo = self.preferenceHelper.odmInfo; + NSString *odmInfoValidated = self.preferenceHelper.odmInfo; + if (odmInfoValidated) { + // Check if odmInfo is within validity window + NSDate *initTime = self.preferenceHelper.odmInfoInitDate; + NSTimeInterval validityWindow = self.preferenceHelper.odmInfoValidityWindow; + if ([self isWithinValidityWindow:initTime timeInterval:validityWindow]) { + // fetch ODM info from pref helper + odmInfoValidated = self.preferenceHelper.odmInfo; + } else { + odmInfoValidated = nil; + } if (completion) { - completion(_odmInfo, nil); + completion(odmInfoValidated, nil); } } else { // Fetch ODM Info from device NSDate * odmInfofetchingTime = [NSDate date]; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [self fetchODMInfoFromDeviceWithInitDate:odmInfofetchingTime andCompletion:^(NSString *odmInfo, NSError *error) { if (odmInfo) { - self.odmInfo = odmInfo; // Cache ODM info in pref helper self.preferenceHelper.odmInfo = odmInfo; self.preferenceHelper.odmInfoInitDate = odmInfofetchingTime; @@ -93,9 +68,8 @@ - (void)loadODMInfoWithTimeOut:(dispatch_time_t) timeOut andCompletionHandler:(v if (completion) { completion(odmInfo, error); } - dispatch_semaphore_signal(semaphore); + }]; - dispatch_semaphore_wait(semaphore, timeOut); } } @@ -109,90 +83,89 @@ - (BOOL)isWithinValidityWindow:(NSDate *)initTime timeInterval:(NSTimeInterval)t } - (void) fetchODMInfoFromDeviceWithInitDate:(NSDate *) date andCompletion:(void (^)(NSString *odmInfo, NSError *error))completion { - - NSError *error = nil ; - - Class ODMConversionManagerClass = NSClassFromString(@"ODCConversionManager"); - SEL sharedInstanceSelector = NSSelectorFromString(@"sharedInstance"); - - if (ODMConversionManagerClass && [ODMConversionManagerClass respondsToSelector:sharedInstanceSelector]) { + @synchronized (self) { - id sharedInstance = ((id (*)(id, SEL))[ODMConversionManagerClass methodForSelector:sharedInstanceSelector]) - (ODMConversionManagerClass, sharedInstanceSelector); - - // Set the time when the app was first launched by calling setFirstLaunchTime: dynamically - SEL setFirstLaunchTimeSelector = NSSelectorFromString(@"setFirstLaunchTime:"); + NSError *error = nil ; + + Class ODMConversionManagerClass = NSClassFromString(@"ODCConversionManager"); + SEL sharedInstanceSelector = NSSelectorFromString(@"sharedInstance"); - if ([sharedInstance respondsToSelector:setFirstLaunchTimeSelector]) { + if (ODMConversionManagerClass && [ODMConversionManagerClass respondsToSelector:sharedInstanceSelector]) { - void (*setFirstLaunchTimeMethod)(id, SEL, NSDate *) = (void (*)(id, SEL, NSDate *)) - [sharedInstance methodForSelector:setFirstLaunchTimeSelector]; - setFirstLaunchTimeMethod(sharedInstance, setFirstLaunchTimeSelector, date); - [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"setFirstLaunchTimeSelector: invoked successfully."] error:nil]; + id sharedInstance = ((id (*)(id, SEL))[ODMConversionManagerClass methodForSelector:sharedInstanceSelector]) + (ODMConversionManagerClass, sharedInstanceSelector); - // Fetch the conversion info. Call fetchAggregateConversionInfoForInteraction:completion dynamically - SEL fetchAggregateConversionInfoSelector = NSSelectorFromString(@"fetchAggregateConversionInfoForInteraction:completion:"); - if ([sharedInstance respondsToSelector:fetchAggregateConversionInfoSelector]) { - NSMethodSignature *signature = [sharedInstance methodSignatureForSelector:fetchAggregateConversionInfoSelector]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; - [invocation setTarget:sharedInstance]; - [invocation setSelector:fetchAggregateConversionInfoSelector]; - - // Since ODCInteractionType is an enum defined in AppAdsOnDeviceConversion.framework and its not accessible via reflection. And since enums in Objective-C are just symbolic constants that get replaced by their underlying integer values at compile time, so defining similar enum here - - typedef NS_ENUM(NSInteger, ODCInteractionType) { - ODCInteractionTypeInstallation, - } ; + // Set the time when the app was first launched by calling setFirstLaunchTime: dynamically + SEL setFirstLaunchTimeSelector = NSSelectorFromString(@"setFirstLaunchTime:"); + + if ([sharedInstance respondsToSelector:setFirstLaunchTimeSelector]) { - ODCInteractionType arg1 = ODCInteractionTypeInstallation; - [invocation setArgument:&arg1 atIndex:2]; + void (*setFirstLaunchTimeMethod)(id, SEL, NSDate *) = (void (*)(id, SEL, NSDate *)) + [sharedInstance methodForSelector:setFirstLaunchTimeSelector]; + setFirstLaunchTimeMethod(sharedInstance, setFirstLaunchTimeSelector, date); + [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"setFirstLaunchTimeSelector: invoked successfully."] error:nil]; - __weak typeof(self) weakSelf = self; - self.odmFetchCompletion = ^(NSString *info, NSError *error) { + // Fetch the conversion info. Call fetchAggregateConversionInfoForInteraction:completion dynamically + SEL fetchAggregateConversionInfoSelector = NSSelectorFromString(@"fetchAggregateConversionInfoForInteraction:completion:"); + if ([sharedInstance respondsToSelector:fetchAggregateConversionInfoSelector]) { + NSMethodSignature *signature = [sharedInstance methodSignatureForSelector:fetchAggregateConversionInfoSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setTarget:sharedInstance]; + [invocation setSelector:fetchAggregateConversionInfoSelector]; + // Since ODCInteractionType is an enum defined in AppAdsOnDeviceConversion.framework and its not accessible via reflection. And since enums in Objective-C are just symbolic constants that get replaced by their underlying integer values at compile time, so defining similar enum here - + typedef NS_ENUM(NSInteger, ODCInteractionType) { + ODCInteractionTypeInstallation, + } ; - if (error) { - [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"ODMConversionManager:fetchInfo Error : %@", error.localizedDescription ] error:error]; - } + ODCInteractionType arg1 = ODCInteractionTypeInstallation; + [invocation setArgument:&arg1 atIndex:2]; - __strong typeof(self) self = weakSelf; - if (info) { - self->_odmInfo = info; // Save new value even if its new. - } + __weak typeof(self) weakSelf = self; + self.odmFetchCompletion = ^(NSString *info, NSError *error) { + + + if (error) { + [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"ODMConversionManager:fetchInfo Error : %@", error.localizedDescription ] error:error]; + } + + __strong typeof(self) self = weakSelf; + + if (completion) { + completion( info, error); + } + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Received Info: %@", info] error:nil]; + }; + [invocation setArgument:&_odmFetchCompletion atIndex:3]; + [invocation invoke]; + [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"fetchInfo:completion: invoked successfully."] error:nil]; + + + } else { + NSString *message = [NSString stringWithFormat:@"Method fetchInfo:completion: not found."] ; + error = [NSError branchErrorWithCode:BNCMethodNotFoundError localizedMessage:message]; + [[BranchLogger shared] logDebug:message error:error ]; if (completion) { - completion( self->_odmInfo, error); + completion( nil, error); } - [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Received Info: %@", info] error:nil]; - }; - - [invocation setArgument:&_odmFetchCompletion atIndex:3]; - [invocation invoke]; - [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"fetchInfo:completion: invoked successfully."] error:nil]; - - + } } else { - NSString *message = [NSString stringWithFormat:@"Method fetchInfo:completion: not found."] ; + NSString *message = [NSString stringWithFormat:@"Method setFirstLaunchTimeSelector: not found."] ; error = [NSError branchErrorWithCode:BNCMethodNotFoundError localizedMessage:message]; - [[BranchLogger shared] logDebug:message error:error ]; + [[BranchLogger shared] logDebug:message error:error]; if (completion) { completion( nil, error); } } } else { - NSString *message = [NSString stringWithFormat:@"Method setFirstLaunchTimeSelector: not found."] ; - error = [NSError branchErrorWithCode:BNCMethodNotFoundError localizedMessage:message]; + NSString *message = [NSString stringWithFormat:@"ODCConversionManager class or sharedInstance method not found. Ignore this error if not using ODM."] ; + error = [NSError branchErrorWithCode:BNCClassNotFoundError localizedMessage:message]; [[BranchLogger shared] logDebug:message error:error]; if (completion) { completion( nil, error); } } - } else { - NSString *message = [NSString stringWithFormat:@"ODCConversionManager class or sharedInstance method not found."] ; - error = [NSError branchErrorWithCode:BNCClassNotFoundError localizedMessage:message]; - [[BranchLogger shared] logDebug:message error:error]; - if (completion) { - completion( nil, error); - } } } diff --git a/Sources/BranchSDK/BNCPreferenceHelper.m b/Sources/BranchSDK/BNCPreferenceHelper.m index ca7001583..05c4d84e9 100644 --- a/Sources/BranchSDK/BNCPreferenceHelper.m +++ b/Sources/BranchSDK/BNCPreferenceHelper.m @@ -16,6 +16,7 @@ #import "BNCSKAdNetwork.h" static const NSTimeInterval DEFAULT_TIMEOUT = 5.5; +static const NSTimeInterval DEFAULT_THIRD_PARTY_APIS_TIMEOUT = 0.5; // 500ms default static const NSTimeInterval DEFAULT_RETRY_INTERVAL = 0; static const NSInteger DEFAULT_RETRY_COUNT = 3; static const NSTimeInterval DEFAULT_REFERRER_GBRAID_WINDOW = 2592000; // 30 days = 2,592,000 seconds @@ -57,6 +58,7 @@ static NSString * const BRANCH_PREFS_KEY_FIRST_APP_LAUNCH_TIME = @"bnc_first_app_launch_time"; static NSString * const BRANCH_PREFS_KEY_SKAN_HIGHEST_CONV_VALUE_SENT = @"bnc_skan_send_highest_conv_value"; static NSString * const BRANCH_PREFS_KEY_SKAN_INVOKE_REGISTER_APP = @"bnc_invoke_register_app"; +static NSString * const BRANCH_PREFS_KEY_THIRD_PARTY_APIS_TIMEOUT = @"bnc_third_party_apis_timeout"; static NSString * const BRANCH_PREFS_KEY_USE_EU_SERVERS = @"bnc_use_EU_servers"; @@ -114,6 +116,7 @@ @implementation BNCPreferenceHelper retryCount = _retryCount, retryInterval = _retryInterval, timeout = _timeout, + thirdPartyAPIsWaitTime = _thirdPartyAPIsWaitTime, lastStrongMatchDate = _lastStrongMatchDate, requestMetadataDictionary = _requestMetadataDictionary, instrumentationDictionary = _instrumentationDictionary, @@ -156,6 +159,7 @@ - (instancetype)init { _retryCount = DEFAULT_RETRY_COUNT; _retryInterval = DEFAULT_RETRY_INTERVAL; _odmInfoValidityWindow = DEFAULT_ODM_INFO_VALIDITY_WINDOW; + _thirdPartyAPIsWaitTime = DEFAULT_THIRD_PARTY_APIS_TIMEOUT; _isDebug = NO; _persistPrefsQueue = [[NSOperationQueue alloc] init]; _persistPrefsQueue.maxConcurrentOperationCount = 1; @@ -759,6 +763,23 @@ - (void) setOdmInfoInitDate:(NSDate *)initDate { } } +- (NSTimeInterval) thirdPartyAPIsWaitTime { + @synchronized (self) { + _thirdPartyAPIsWaitTime = [self readDoubleFromDefaults:BRANCH_PREFS_KEY_THIRD_PARTY_APIS_TIMEOUT]; + if (_thirdPartyAPIsWaitTime == NSNotFound) { + _thirdPartyAPIsWaitTime = DEFAULT_THIRD_PARTY_APIS_TIMEOUT; + } + return _thirdPartyAPIsWaitTime; + } +} + +- (void) setThirdPartyAPIsWaitTime:(NSTimeInterval)waitTime { + @synchronized (self) { + _thirdPartyAPIsWaitTime = waitTime; + [self writeObjectToDefaults:BRANCH_PREFS_KEY_THIRD_PARTY_APIS_TIMEOUT value:@(waitTime)]; + } +} + - (NSInteger) skanCurrentWindow { @synchronized (self) { _skanCurrentWindow = [self readIntegerFromDefaults:BRANCH_PREFS_KEY_SKAN_CURRENT_WINDOW]; diff --git a/Sources/BranchSDK/BNCRequestFactory.m b/Sources/BranchSDK/BNCRequestFactory.m index d6aae49e4..a89b6ed51 100644 --- a/Sources/BranchSDK/BNCRequestFactory.m +++ b/Sources/BranchSDK/BNCRequestFactory.m @@ -49,6 +49,8 @@ @interface BNCRequestFactory() @property (nonatomic, strong, readwrite) BNCPasteboard *pasteboard; @property (nonatomic, strong, readwrite) NSNumber *requestCreationTimeStamp; @property (nonatomic, strong, readwrite) NSString *requestUUID; +@property (nonatomic, strong, readwrite) NSString *odmInfo; +@property (nonatomic, strong, readwrite) NSString *appleAttributionToken; @end @@ -72,6 +74,40 @@ - (instancetype)initWithBranchKey:(NSString *)key UUID:(NSString *)requestUUID T return self; } +- (void) loadDataFromThirdPartyAPIs { +#if !TARGET_OS_TV + dispatch_group_t apiGroup = dispatch_group_create(); + dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + if ([[self.preferenceHelper attributionLevel] isEqualToString:BranchAttributionLevelFull]) + { + dispatch_group_enter(apiGroup); + dispatch_async(concurrentQueue, ^{ + [[BNCODMInfoCollector instance] loadODMInfoWithCompletionHandler:^(NSString * _Nullable odmInfo, NSError * _Nullable error) { + self.odmInfo = odmInfo; + dispatch_group_leave(apiGroup); + }]; + }); + } + + if (!self.preferenceHelper.appleAttributionTokenChecked) { + dispatch_group_enter(apiGroup); + dispatch_async(concurrentQueue, ^{ + self.appleAttributionToken = [BNCSystemObserver appleAttributionToken]; + if (self.appleAttributionToken) { + self.preferenceHelper.appleAttributionTokenChecked = YES; + } + dispatch_group_leave(apiGroup); + }); + } + + NSTimeInterval timeoutSeconds = [BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime; + dispatch_time_t timeOut = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutSeconds * NSEC_PER_SEC)); + dispatch_group_wait(apiGroup, timeOut); +#endif + +} + // SDK level tracking control // When set to YES, only link creation and resolution calls are allowed. // NO by default. @@ -82,6 +118,8 @@ - (BOOL)isTrackingDisabled { - (NSDictionary *)dataForInstallWithURLString:(NSString *)urlString { NSMutableDictionary *json = [NSMutableDictionary new]; + [self loadDataFromThirdPartyAPIs]; + // All requests [self addDefaultRequestDataToJSON:json]; @@ -141,6 +179,8 @@ - (NSDictionary *)dataForInstallWithURLString:(NSString *)urlString { - (NSDictionary *)dataForOpenWithURLString:(NSString *)urlString { NSMutableDictionary *json = [NSMutableDictionary new]; + [self loadDataFromThirdPartyAPIs]; + // All requests [self addDefaultRequestDataToJSON:json]; @@ -335,10 +375,9 @@ - (void)addSystemObserverDataToJSON:(NSMutableDictionary *)json { - (void)addAppleAttributionTokenToJSON:(NSMutableDictionary *)json { // This value is only sent once usually on install if (!self.preferenceHelper.appleAttributionTokenChecked) { - NSString *appleAttributionToken = [BNCSystemObserver appleAttributionToken]; - if (appleAttributionToken) { + if (self.appleAttributionToken) { self.preferenceHelper.appleAttributionTokenChecked = YES; - [self safeSetValue:appleAttributionToken forKey:BRANCH_REQUEST_KEY_APPLE_ATTRIBUTION_TOKEN onDict:json]; + [self safeSetValue:self.appleAttributionToken forKey:BRANCH_REQUEST_KEY_APPLE_ATTRIBUTION_TOKEN onDict:json]; } } } @@ -347,9 +386,8 @@ - (void)addAppleAttributionTokenToJSON:(NSMutableDictionary *)json { - (void)addODMInfoToJSON:(NSMutableDictionary *)json { #if !TARGET_OS_TV if ([[self.preferenceHelper attributionLevel] isEqualToString:BranchAttributionLevelFull]) { - NSString *odmInfo = [BNCODMInfoCollector instance].odmInfo ; - if (odmInfo) { - [self safeSetValue:odmInfo forKey:BRANCH_REQUEST_KEY_ODM_INFO onDict:json]; + if (self.odmInfo) { + [self safeSetValue:self.odmInfo forKey:BRANCH_REQUEST_KEY_ODM_INFO onDict:json]; NSNumber* odmInitDateInNumberFormat = BNCWireFormatFromDate(self.preferenceHelper.odmInfoInitDate); [self safeSetValue:odmInitDateInNumberFormat forKey:BRANCH_REQUEST_KEY_ODM_FIRST_OPEN_TIMESTAMP onDict:json]; } diff --git a/Sources/BranchSDK/BNCSystemObserver.m b/Sources/BranchSDK/BNCSystemObserver.m index 2bc4b9b83..238ffcb77 100644 --- a/Sources/BranchSDK/BNCSystemObserver.m +++ b/Sources/BranchSDK/BNCSystemObserver.m @@ -8,6 +8,8 @@ #import "BNCSystemObserver.h" #import "BranchLogger.h" +#import "BNCPreferenceHelper.h" + #if __has_feature(modules) @import UIKit; @import SystemConfiguration; @@ -35,32 +37,20 @@ + (NSString *)appleAttributionToken { return nil; } - __block NSString *token = nil; - #if !TARGET_OS_TV if (@available(iOS 14.3, macCatalyst 14.3, *)) { - - // We are getting reports on iOS 14.5 that this API can hang, adding a short timeout for now. - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error; - NSString *appleAttributionToken = [AAAttribution attributionTokenWithError:&error]; - if (!error) { - token = appleAttributionToken; - } - dispatch_semaphore_signal(semaphore); - }); - - // Apple said this API should respond within 50ms, lets give up after 500 ms - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_MSEC))); - if (token == nil) { - [[BranchLogger shared] logError:[NSString stringWithFormat:@"AppleAttributionToken request timed out"] error:nil]; + NSError *error; + NSString *appleAttributionToken = [AAAttribution attributionTokenWithError:&error]; + if (error) { + [[BranchLogger shared] logError:[NSString stringWithFormat:@"AppleAttributionToken API failed, error: %@", [error description]] error:error]; + } + if (appleAttributionToken == nil) { + [[BranchLogger shared] logError:[NSString stringWithFormat:@"AppleAttributionToken returned nil token"] error:nil]; } + return appleAttributionToken; } #endif - - return token; + return nil; } + (NSString *)advertiserIdentifier { diff --git a/Sources/BranchSDK/Branch.m b/Sources/BranchSDK/Branch.m index 32ee69691..434c9c22d 100644 --- a/Sources/BranchSDK/Branch.m +++ b/Sources/BranchSDK/Branch.m @@ -244,7 +244,6 @@ - (id)initWithInterface:(BNCServerInterface *)interface // queue up async data loading [self loadApplicationData]; [self loadUserAgent]; - [self startLoadingOfODMInfo]; BranchJsonConfig *config = BranchJsonConfig.instance; self.deferInitForPluginRuntime = config.deferInitForPluginRuntime; @@ -539,6 +538,19 @@ - (void)setRetryInterval:(NSTimeInterval)retryInterval { self.preferenceHelper.retryInterval = retryInterval; } ++ (void)setSDKWaitTimeForThirdPartyAPIs:(NSTimeInterval)waitTime { + @synchronized(self) { + if (waitTime <= 0) { + [[BranchLogger shared] logWarning:@"Invalid waitTime value. It must be greater than 0. Using default value." error:nil]; + return; + } + if (waitTime > 10) { + [[BranchLogger shared] logWarning:@"Invalid waitTime value. It must not exceed 10 seconds. Using default value." error:nil]; + return; + } + [BNCPreferenceHelper sharedInstance].thirdPartyAPIsWaitTime = waitTime; + } +} - (void)setRequestMetadataKey:(NSString *)key value:(NSString *)value { [self.preferenceHelper setRequestMetadataKey:key value:value]; @@ -597,7 +609,6 @@ + (void)setODMInfo:(NSString *)odmInfo andFirstOpenTimestamp:(NSDate *) firstOpe @synchronized (self) { [[BNCPreferenceHelper sharedInstance] setOdmInfo:odmInfo]; [BNCPreferenceHelper sharedInstance].odmInfoInitDate = firstOpenTimestamp; - [[BNCODMInfoCollector instance] loadODMInfo]; } #else [[BranchLogger shared] logWarning:@"setODMInfo not supported on tvOS." error:nil]; @@ -1004,15 +1015,6 @@ - (void)loadApplicationData { }); } -- (void)startLoadingOfODMInfo { - #if !TARGET_OS_TV - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [[BranchLogger shared] logVerbose:@"Loading ODM info ..." error:nil]; - [[BNCODMInfoCollector instance] loadODMInfo]; - }); - #endif -} - #pragma mark - Apple Search Ad Check diff --git a/Sources/BranchSDK/Private/BNCODMInfoCollector.h b/Sources/BranchSDK/Private/BNCODMInfoCollector.h index a2e064bd8..18b2ae8d1 100644 --- a/Sources/BranchSDK/Private/BNCODMInfoCollector.h +++ b/Sources/BranchSDK/Private/BNCODMInfoCollector.h @@ -19,8 +19,6 @@ NS_ASSUME_NONNULL_BEGIN @interface BNCODMInfoCollector : NSObject + (BNCODMInfoCollector *_Nullable) instance; - -@property (nonatomic, copy, readwrite) NSString * _Nullable odmInfo; /** * Checks if the given date is within the specified validity window from the current time. * @param initTime The reference date to check against. @@ -29,17 +27,11 @@ NS_ASSUME_NONNULL_BEGIN */ - (BOOL)isWithinValidityWindow:(NSDate *)initTime timeInterval:(NSTimeInterval)timeInterval; -/** - * Loads ODM information either from cache or from the device. - */ -- (void)loadODMInfo; - /** * Loads ODM information with a specified timeout. - * @param timeOut The maximum time to wait for ODM information. * @param completion Optional completion handler called when ODM info is loaded. */ -- (void)loadODMInfoWithTimeOut:(dispatch_time_t) timeOut andCompletionHandler:(void (^_Nullable)(NSString * _Nullable odmInfo, NSError * _Nullable error))completion; // Added completion handler for unit tests +- (void)loadODMInfoWithCompletionHandler:(void (^_Nullable)(NSString * _Nullable odmInfo, NSError * _Nullable error))completion; - (void)fetchODMInfoFromDeviceWithInitDate:(NSDate *) date andCompletion:(void (^)(NSString *odmInfo, NSError *error))completion; diff --git a/Sources/BranchSDK/Public/BNCPreferenceHelper.h b/Sources/BranchSDK/Public/BNCPreferenceHelper.h index f664b7f72..d3253bd5a 100644 --- a/Sources/BranchSDK/Public/BNCPreferenceHelper.h +++ b/Sources/BranchSDK/Public/BNCPreferenceHelper.h @@ -46,6 +46,7 @@ NSURL* /* _Nonnull */ BNCURLForBranchDirectory(void); @property (assign, nonatomic) NSInteger retryCount; @property (assign, nonatomic) NSTimeInterval retryInterval; @property (assign, nonatomic) NSTimeInterval timeout; +@property (assign, nonatomic) NSTimeInterval thirdPartyAPIsWaitTime; @property (copy, nonatomic) NSString *externalIntentURI; @property (strong, nonatomic) NSMutableDictionary *savedAnalyticsData; @property (copy, nonatomic) NSString *lastSystemBuildVersion; diff --git a/Sources/BranchSDK/Public/Branch.h b/Sources/BranchSDK/Public/Branch.h index c75d92216..0983a5168 100644 --- a/Sources/BranchSDK/Public/Branch.h +++ b/Sources/BranchSDK/Public/Branch.h @@ -752,6 +752,14 @@ Sets a custom base safetrack URL for non-linking calls to the Branch API. */ - (void)setNetworkTimeout:(NSTimeInterval)timeout; +/** + Set the SDK wait time for third party APIs (for fetching ODM info and Apple Attribution Token) to finish + This timeout should be > 0 and <= 10 seconds. + + @param waitTime Number of seconds before third party API calls are considered timed out. Default is 0.5 seconds (500ms). + */ ++ (void)setSDKWaitTimeForThirdPartyAPIs:(NSTimeInterval)waitTime; + /** Disable callouts to ad networks for all events for a user; by default Branch sends callouts to ad networks.