diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m b/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m index 28bb3b3b3..d79235760 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCAPIServerTest.m @@ -11,7 +11,7 @@ #import "BNCSystemObserver.h" #import "BNCConfig.h" #import "BranchConstants.h" -#import "Branch.h" +@import BranchSDK; @interface BNCAPIServerTest : XCTestCase diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m index 38d1b0cc2..555449d9a 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCODMTests.m @@ -7,7 +7,7 @@ // #import -#import "Branch.h" +@import BranchSDK; #import "BNCPreferenceHelper.h" #import "BNCRequestFactory.h" #import "BNCEncodingUtils.h" diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCPasteboardTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCPasteboardTests.m index b850ef0cd..0e2cd06bf 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCPasteboardTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCPasteboardTests.m @@ -8,7 +8,7 @@ #import #import "BNCPasteboard.h" -#import "Branch.h" +@import BranchSDK; @interface BNCPasteboardTests : XCTestCase diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m index 5269501b3..48645e03a 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCPreferenceHelperTests.m @@ -9,7 +9,7 @@ #import #import "BNCPreferenceHelper.h" #import "BNCEncodingUtils.h" -#import "Branch.h" +@import BranchSDK; #import "BNCConfig.h" @interface BNCPreferenceHelper() diff --git a/Branch-TestBed/Branch-SDK-Tests/Branch-SDK-Tests-Bridging-Header.h b/Branch-TestBed/Branch-SDK-Tests/Branch-SDK-Tests-Bridging-Header.h index 169bd50f3..e1b9477b6 100644 --- a/Branch-TestBed/Branch-SDK-Tests/Branch-SDK-Tests-Bridging-Header.h +++ b/Branch-TestBed/Branch-SDK-Tests/Branch-SDK-Tests-Bridging-Header.h @@ -2,4 +2,4 @@ // Module headers for Branch SDK unit testing. // -#import "Branch.h" +@import BranchSDK; diff --git a/Branch-TestBed/Branch-SDK-Tests/BranchActivityItemTests.m b/Branch-TestBed/Branch-SDK-Tests/BranchActivityItemTests.m index f117859f7..0dbd515f6 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BranchActivityItemTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BranchActivityItemTests.m @@ -7,7 +7,7 @@ // #import -#import "Branch.h" +@import BranchSDK; @interface BranchActivityItemTests: XCTestCase @end diff --git a/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m b/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m index d1e7713f1..a0727327b 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BranchClassTests.m @@ -7,7 +7,7 @@ // #import -#import "Branch.h" +@import BranchSDK; #import "BranchConstants.h" #import "BNCPasteboard.h" #import "BNCAppGroupsData.h" diff --git a/Branch-TestBed/Branch-SDK-Tests/BranchLoggerTests.m b/Branch-TestBed/Branch-SDK-Tests/BranchLoggerTests.m index 1a4be3490..a28b775d9 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BranchLoggerTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BranchLoggerTests.m @@ -8,7 +8,7 @@ #import #import "BranchLogger.h" -#import "Branch.h" +@import BranchSDK; @interface BranchLoggerTests : XCTestCase diff --git a/Branch-TestBed/Branch-SDK-Tests/BranchQRCodeTests.m b/Branch-TestBed/Branch-SDK-Tests/BranchQRCodeTests.m index 3d1e1ffc6..9208b9052 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BranchQRCodeTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BranchQRCodeTests.m @@ -7,7 +7,7 @@ // #import -#import "Branch.h" +@import BranchSDK; #import "BranchQRCode.h" #import "BNCQRCodeCache.h" diff --git a/Branch-TestBed/Branch-SDK-Tests/BranchShareLinkTests.m b/Branch-TestBed/Branch-SDK-Tests/BranchShareLinkTests.m index c41fdb618..6306a2792 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BranchShareLinkTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BranchShareLinkTests.m @@ -9,7 +9,7 @@ #import #import "BranchShareLink.h" #import "BranchLinkProperties.h" -#import "Branch.h" +@import BranchSDK; @interface BranchShareLinkTests : XCTestCase diff --git a/Branch-TestBed/Branch-SDK-Tests/DispatchToIsolationQueueTests.m b/Branch-TestBed/Branch-SDK-Tests/DispatchToIsolationQueueTests.m index 89043aa94..e51f4c7ef 100644 --- a/Branch-TestBed/Branch-SDK-Tests/DispatchToIsolationQueueTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/DispatchToIsolationQueueTests.m @@ -7,7 +7,7 @@ // #import -// #import "Branch.h" +// @import BranchSDK; @interface DispatchToIsolationQueueTests : XCTestCase // @property (nonatomic, strong, readwrite) Branch *branch; diff --git a/Branch-TestBed/Branch-SDK-Unhosted-Tests/Branch_setBranchKeyTests.m b/Branch-TestBed/Branch-SDK-Unhosted-Tests/Branch_setBranchKeyTests.m index 3792b3933..6e92febdc 100644 --- a/Branch-TestBed/Branch-SDK-Unhosted-Tests/Branch_setBranchKeyTests.m +++ b/Branch-TestBed/Branch-SDK-Unhosted-Tests/Branch_setBranchKeyTests.m @@ -7,7 +7,7 @@ // #import -#import "Branch.h" +@import BranchSDK; #import "NSError+Branch.h" // expose private methods used by tests diff --git a/Branch-TestBed/Branch-TestBed-UITests/UITestCaseMisc.m b/Branch-TestBed/Branch-TestBed-UITests/UITestCaseMisc.m index 3c992a5b6..aaf34d73e 100644 --- a/Branch-TestBed/Branch-TestBed-UITests/UITestCaseMisc.m +++ b/Branch-TestBed/Branch-TestBed-UITests/UITestCaseMisc.m @@ -7,7 +7,7 @@ // #import "UITestCaseTestBed.h" -#import "Branch.h" +@import BranchSDK; #import "BranchEvent.h" @interface UITestCaseMisc : UITestCaseTestBed diff --git a/Branch-TestBed/Branch-TestBed-UITests/UITestCaseTracking.m b/Branch-TestBed/Branch-TestBed-UITests/UITestCaseTracking.m index 95eb6f594..89e8ebbb6 100644 --- a/Branch-TestBed/Branch-TestBed-UITests/UITestCaseTracking.m +++ b/Branch-TestBed/Branch-TestBed-UITests/UITestCaseTracking.m @@ -7,7 +7,7 @@ // #import "UITestCaseTestBed.h" -#import "Branch.h" +@import BranchSDK; #import "BranchEvent.h" @interface UITestCaseTracking : UITestCaseTestBed diff --git a/Branch-TestBed/Branch-TestBed-UITests/UITestSendV2Event.m b/Branch-TestBed/Branch-TestBed-UITests/UITestSendV2Event.m index e919669e4..41491c7ae 100644 --- a/Branch-TestBed/Branch-TestBed-UITests/UITestSendV2Event.m +++ b/Branch-TestBed/Branch-TestBed-UITests/UITestSendV2Event.m @@ -7,7 +7,7 @@ // #import -#import "Branch.h" +@import BranchSDK; #import "BranchEvent.h" #import "UITestCaseTestBed.h" diff --git a/Branch-TestBed/Branch-TestBed-UITests/UITestSetIdentity.m b/Branch-TestBed/Branch-TestBed-UITests/UITestSetIdentity.m index 60e1b6f66..9e2982319 100644 --- a/Branch-TestBed/Branch-TestBed-UITests/UITestSetIdentity.m +++ b/Branch-TestBed/Branch-TestBed-UITests/UITestSetIdentity.m @@ -8,7 +8,7 @@ #import -#import "Branch.h" +@import BranchSDK; #import "BranchEvent.h" #import "UITestCaseTestBed.h" diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj index c55c7fb00..802a31f30 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj @@ -11,14 +11,13 @@ 032DAF232607B59300891641 /* UITestCaseTracking.m in Sources */ = {isa = PBXBuildFile; fileRef = 032DAF222607B59300891641 /* UITestCaseTracking.m */; }; 033E096325F459C300F39CB3 /* UITestSetIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 033E096225F459C200F39CB3 /* UITestSetIdentity.m */; }; 033E097225F541D400F39CB3 /* UITestCaseTestBed.m in Sources */ = {isa = PBXBuildFile; fileRef = 033E097125F541D400F39CB3 /* UITestCaseTestBed.m */; }; - 0372076425E8418F00F29C30 /* libBranch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 466B58381B17773000A69EDE /* libBranch.a */; }; 0372078825E9F81100F29C30 /* UITestCaseMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = 0372078725E9F81000F29C30 /* UITestCaseMisc.m */; }; 0399DD122599BF8A00CDB36E /* UITestSendV2Event.m in Sources */ = {isa = PBXBuildFile; fileRef = 0399DD112599BF8A00CDB36E /* UITestSendV2Event.m */; }; 03B49EEB25F9F315000BF105 /* UITestCase0OpenNInstall.m in Sources */ = {isa = PBXBuildFile; fileRef = 03B49EEA25F9F315000BF105 /* UITestCase0OpenNInstall.m */; }; + 40EA3FC7C54A6B3CCEBDB9F8 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; 466B584F1B17775900A69EDE /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67BBCF271A69E49A009C7DAE /* AdSupport.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 466B58521B17776500A69EDE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 670016631940F51400A9E103 /* Foundation.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 466B58531B17776A00A69EDE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 670016671940F51400A9E103 /* UIKit.framework */; }; - 466B58811B1778DB00A69EDE /* libBranch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 466B58381B17773000A69EDE /* libBranch.a */; }; 4683F0761B20A73F00A432E7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 670016731940F51400A9E103 /* AppDelegate.m */; }; 46DC406E1B2A328900D2D203 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67BBCF271A69E49A009C7DAE /* AdSupport.framework */; }; 4AB16368239E3A2700D42931 /* DispatchToIsolationQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AB16367239E3A2700D42931 /* DispatchToIsolationQueueTests.m */; }; @@ -36,6 +35,7 @@ 4D93D8622098D43C00CFABA6 /* UITestSafari.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D93D8602098D43C00CFABA6 /* UITestSafari.m */; }; 4DBEFFF61FB114F900F7C41B /* ArrayPickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBEFFF51FB114F900F7C41B /* ArrayPickerView.m */; }; 4DE235641FB12C2700D4E5A9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4DBEFFFB1FB12A1000F7C41B /* Main.storyboard */; }; + 5AFDEF31D95DB3252A8F146D /* (null) in Frameworks */ = {isa = PBXBuildFile; }; 5F205D05231864E800C776D1 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F205D04231864E800C776D1 /* WebKit.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 5F205D062318659500C776D1 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F205D04231864E800C776D1 /* WebKit.framework */; }; 5F205D0823186AF700C776D1 /* BNCUserAgentCollectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F205D022318641700C776D1 /* BNCUserAgentCollectorTests.m */; }; @@ -43,51 +43,8 @@ 5F3D671C233062FD00454FF1 /* BNCJsonLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F3D671A233062FD00454FF1 /* BNCJsonLoader.m */; }; 5F42763325DB3694005B9BBC /* AdServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F42763225DB3694005B9BBC /* AdServices.framework */; }; 5F437E40237E1A560052064B /* BNCDeviceSystemTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F437E3F237E1A560052064B /* BNCDeviceSystemTests.m */; }; - 5F5FDA102B7DE20800F14A43 /* BranchLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5FDA0F2B7DE20800F14A43 /* BranchLogger.m */; }; 5F5FDA122B7DE22A00F14A43 /* BranchLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F5FDA112B7DE22A00F14A43 /* BranchLogger.h */; }; 5F5FDA142B7DE27D00F14A43 /* BranchLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5FDA132B7DE27D00F14A43 /* BranchLoggerTests.m */; }; - 5F644BB92B7AA811000DCD78 /* NSError+Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B262B7AA810000DCD78 /* NSError+Branch.m */; }; - 5F644BBA2B7AA811000DCD78 /* BNCUserAgentCollector.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B272B7AA810000DCD78 /* BNCUserAgentCollector.m */; }; - 5F644BBB2B7AA811000DCD78 /* BNCEncodingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B282B7AA810000DCD78 /* BNCEncodingUtils.m */; }; - 5F644BBC2B7AA811000DCD78 /* BranchUniversalObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B292B7AA810000DCD78 /* BranchUniversalObject.m */; }; - 5F644BBD2B7AA811000DCD78 /* BranchPasteControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B2A2B7AA810000DCD78 /* BranchPasteControl.m */; }; - 5F644BBE2B7AA811000DCD78 /* BNCApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B2B2B7AA810000DCD78 /* BNCApplication.m */; }; - 5F644BBF2B7AA811000DCD78 /* BNCDeviceSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B2C2B7AA810000DCD78 /* BNCDeviceSystem.m */; }; - 5F644BC02B7AA811000DCD78 /* NSMutableDictionary+Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B2D2B7AA810000DCD78 /* NSMutableDictionary+Branch.m */; }; - 5F644BC12B7AA811000DCD78 /* BNCServerRequestQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B2E2B7AA810000DCD78 /* BNCServerRequestQueue.m */; }; - 5F644BC22B7AA811000DCD78 /* BNCUrlQueryParameter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B2F2B7AA810000DCD78 /* BNCUrlQueryParameter.m */; }; - 5F644BC32B7AA811000DCD78 /* BNCDeepLinkViewControllerInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B302B7AA810000DCD78 /* BNCDeepLinkViewControllerInstance.m */; }; - 5F644BC42B7AA811000DCD78 /* BranchScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B312B7AA810000DCD78 /* BranchScene.m */; }; - 5F644BC52B7AA811000DCD78 /* BNCContentDiscoveryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B322B7AA810000DCD78 /* BNCContentDiscoveryManager.m */; }; - 5F644BC62B7AA811000DCD78 /* Branch+Validator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B332B7AA810000DCD78 /* Branch+Validator.m */; }; - 5F644BC72B7AA811000DCD78 /* BranchInstallRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B342B7AA810000DCD78 /* BranchInstallRequest.m */; }; - 5F644BC82B7AA811000DCD78 /* BranchPluginSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B352B7AA810000DCD78 /* BranchPluginSupport.m */; }; - 5F644BC92B7AA811000DCD78 /* NSString+Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B362B7AA810000DCD78 /* NSString+Branch.m */; }; - 5F644BCA2B7AA811000DCD78 /* BNCSystemObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B372B7AA810000DCD78 /* BNCSystemObserver.m */; }; - 5F644BCC2B7AA811000DCD78 /* BNCURLFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B392B7AA810000DCD78 /* BNCURLFilter.m */; }; - 5F644BCD2B7AA811000DCD78 /* BNCServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B3A2B7AA810000DCD78 /* BNCServerRequest.m */; }; - 5F644BCE2B7AA811000DCD78 /* BNCNetworkInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B3B2B7AA810000DCD78 /* BNCNetworkInterface.m */; }; - 5F644BCF2B7AA811000DCD78 /* BNCPreferenceHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B3C2B7AA810000DCD78 /* BNCPreferenceHelper.m */; }; - 5F644BD02B7AA811000DCD78 /* BNCConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B3D2B7AA810000DCD78 /* BNCConfig.m */; }; - 5F644BD12B7AA811000DCD78 /* BranchConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B3E2B7AA810000DCD78 /* BranchConstants.m */; }; - 5F644BD22B7AA811000DCD78 /* BranchContentDiscoveryManifest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B3F2B7AA810000DCD78 /* BranchContentDiscoveryManifest.m */; }; - 5F644BD32B7AA811000DCD78 /* BranchSpotlightUrlRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B402B7AA810000DCD78 /* BranchSpotlightUrlRequest.m */; }; - 5F644BD52B7AA811000DCD78 /* BranchContentDiscoverer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B422B7AA810000DCD78 /* BranchContentDiscoverer.m */; }; - 5F644BD62B7AA811000DCD78 /* BNCDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B432B7AA810000DCD78 /* BNCDeviceInfo.m */; }; - 5F644BD72B7AA811000DCD78 /* BNCNetworkService.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B442B7AA810000DCD78 /* BNCNetworkService.m */; }; - 5F644BD82B7AA811000DCD78 /* BNCReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B452B7AA810000DCD78 /* BNCReachability.m */; }; - 5F644BD92B7AA811000DCD78 /* BNCRequestFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B462B7AA810000DCD78 /* BNCRequestFactory.m */; }; - 5F644BDA2B7AA811000DCD78 /* BNCReferringURLUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B472B7AA810000DCD78 /* BNCReferringURLUtility.m */; }; - 5F644BDB2B7AA811000DCD78 /* BNCProductCategory.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B482B7AA810000DCD78 /* BNCProductCategory.m */; }; - 5F644BDC2B7AA811000DCD78 /* BNCCrashlyticsWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B492B7AA810000DCD78 /* BNCCrashlyticsWrapper.m */; }; - 5F644BDD2B7AA811000DCD78 /* BNCServerInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B4A2B7AA810000DCD78 /* BNCServerInterface.m */; }; - 5F644BDE2B7AA811000DCD78 /* BranchJsonConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B4B2B7AA810000DCD78 /* BranchJsonConfig.m */; }; - 5F644BDF2B7AA811000DCD78 /* BranchLATDRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B4C2B7AA810000DCD78 /* BranchLATDRequest.m */; }; - 5F644BE02B7AA811000DCD78 /* BNCPartnerParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B4D2B7AA810000DCD78 /* BNCPartnerParameters.m */; }; - 5F644BE12B7AA811000DCD78 /* BranchCSSearchableItemAttributeSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B4E2B7AA810000DCD78 /* BranchCSSearchableItemAttributeSet.m */; }; - 5F644BE22B7AA811000DCD78 /* BranchActivityItemProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B4F2B7AA810000DCD78 /* BranchActivityItemProvider.m */; }; - 5F644BE32B7AA811000DCD78 /* BranchQRCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B502B7AA810000DCD78 /* BranchQRCode.m */; }; - 5F644BE42B7AA811000DCD78 /* BNCKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B512B7AA810000DCD78 /* BNCKeyChain.m */; }; 5F644BE52B7AA811000DCD78 /* BNCLinkData.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B532B7AA810000DCD78 /* BNCLinkData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5F644BE62B7AA811000DCD78 /* BranchDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B542B7AA810000DCD78 /* BranchDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5F644BE72B7AA811000DCD78 /* BranchQRCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B552B7AA810000DCD78 /* BranchQRCode.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -151,7 +108,6 @@ 5F644C242B7AA811000DCD78 /* BranchSpotlightUrlRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B932B7AA811000DCD78 /* BranchSpotlightUrlRequest.h */; }; 5F644C252B7AA811000DCD78 /* BranchContentDiscoveryManifest.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B942B7AA811000DCD78 /* BranchContentDiscoveryManifest.h */; }; 5F644C262B7AA811000DCD78 /* BNCConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B952B7AA811000DCD78 /* BNCConfig.h */; }; - 5F644C272B7AA811000DCD78 /* BranchConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B962B7AA811000DCD78 /* BranchConstants.h */; }; 5F644C282B7AA811000DCD78 /* BNCPartnerParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B972B7AA811000DCD78 /* BNCPartnerParameters.h */; }; 5F644C292B7AA811000DCD78 /* BranchJsonConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B982B7AA811000DCD78 /* BranchJsonConfig.h */; }; 5F644C2A2B7AA811000DCD78 /* BranchLATDRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B992B7AA811000DCD78 /* BranchLATDRequest.h */; }; @@ -160,37 +116,10 @@ 5F644C2D2B7AA811000DCD78 /* BNCReferringURLUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B9C2B7AA811000DCD78 /* BNCReferringURLUtility.h */; }; 5F644C2E2B7AA811000DCD78 /* BNCNetworkService.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B9D2B7AA811000DCD78 /* BNCNetworkService.h */; }; 5F644C2F2B7AA811000DCD78 /* BNCReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F644B9E2B7AA811000DCD78 /* BNCReachability.h */; }; - 5F644C302B7AA811000DCD78 /* BranchDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644B9F2B7AA811000DCD78 /* BranchDelegate.m */; }; - 5F644C312B7AA811000DCD78 /* BNCLinkData.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA02B7AA811000DCD78 /* BNCLinkData.m */; }; - 5F644C322B7AA811000DCD78 /* Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA12B7AA811000DCD78 /* Branch.m */; }; - 5F644C332B7AA811000DCD78 /* BNCJSONUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA22B7AA811000DCD78 /* BNCJSONUtility.m */; }; - 5F644C342B7AA811000DCD78 /* BNCCurrency.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA32B7AA811000DCD78 /* BNCCurrency.m */; }; - 5F644C352B7AA811000DCD78 /* BranchLastAttributedTouchData.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA42B7AA811000DCD78 /* BranchLastAttributedTouchData.m */; }; - 5F644C362B7AA811000DCD78 /* BNCSKAdNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA52B7AA811000DCD78 /* BNCSKAdNetwork.m */; }; - 5F644C372B7AA811000DCD78 /* BranchContentPathProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA62B7AA811000DCD78 /* BranchContentPathProperties.m */; }; - 5F644C382B7AA811000DCD78 /* BNCPasteboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA72B7AA811000DCD78 /* BNCPasteboard.m */; }; - 5F644C392B7AA811000DCD78 /* BNCAppleReceipt.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA82B7AA811000DCD78 /* BNCAppleReceipt.m */; }; - 5F644C3A2B7AA811000DCD78 /* BranchLinkProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BA92B7AA811000DCD78 /* BranchLinkProperties.m */; }; - 5F644C3B2B7AA811000DCD78 /* BNCInitSessionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BAA2B7AA811000DCD78 /* BNCInitSessionResponse.m */; }; - 5F644C3C2B7AA811000DCD78 /* BNCLinkCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BAB2B7AA811000DCD78 /* BNCLinkCache.m */; }; - 5F644C3D2B7AA811000DCD78 /* BNCSpotlightService.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BAC2B7AA811000DCD78 /* BNCSpotlightService.m */; }; - 5F644C3E2B7AA811000DCD78 /* BNCServerAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BAD2B7AA811000DCD78 /* BNCServerAPI.m */; }; - 5F644C3F2B7AA811000DCD78 /* BranchShareLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BAE2B7AA811000DCD78 /* BranchShareLink.m */; }; - 5F644C402B7AA811000DCD78 /* UIViewController+Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BAF2B7AA811000DCD78 /* UIViewController+Branch.m */; }; - 5F644C412B7AA811000DCD78 /* BNCAppGroupsData.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB02B7AA811000DCD78 /* BNCAppGroupsData.m */; }; - 5F644C422B7AA811000DCD78 /* BNCServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB12B7AA811000DCD78 /* BNCServerResponse.m */; }; - 5F644C432B7AA811000DCD78 /* BranchEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB22B7AA811000DCD78 /* BranchEvent.m */; }; - 5F644C442B7AA811000DCD78 /* BranchShortUrlRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB32B7AA811000DCD78 /* BranchShortUrlRequest.m */; }; - 5F644C452B7AA811000DCD78 /* BranchOpenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB42B7AA811000DCD78 /* BranchOpenRequest.m */; }; - 5F644C462B7AA811000DCD78 /* BNCQRCodeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB52B7AA811000DCD78 /* BNCQRCodeCache.m */; }; - 5F644C472B7AA811000DCD78 /* BranchShortUrlSyncRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB62B7AA811000DCD78 /* BranchShortUrlSyncRequest.m */; }; - 5F644C482B7AA811000DCD78 /* BNCCallbackMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB72B7AA811000DCD78 /* BNCCallbackMap.m */; }; - 5F644C492B7AA811000DCD78 /* BNCEventUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F644BB82B7AA811000DCD78 /* BNCEventUtils.m */; }; 5F67F48E228F535500067429 /* BNCEncodingUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F67F48D228F535500067429 /* BNCEncodingUtilsTests.m */; }; 5F6D86D92BB5E9650068B536 /* BNCClassSerializationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F6D86D82BB5E9650068B536 /* BNCClassSerializationTests.m */; }; 5F86501A2B76DA3200364BDE /* NSMutableDictionaryBranchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F8650192B76DA3200364BDE /* NSMutableDictionaryBranchTests.m */; }; 5F892EC5236116CD0023AEC1 /* NSErrorBranchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F892EC4236116CC0023AEC1 /* NSErrorBranchTests.m */; }; - 5F8B7B4021B5F5CD009CE0A6 /* libBranch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 466B58381B17773000A69EDE /* libBranch.a */; }; 5F8B7B4721B5F5F0009CE0A6 /* Branch_setBranchKeyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F8B7B4621B5F5F0009CE0A6 /* Branch_setBranchKeyTests.m */; }; 5F8BB66E278771890055D2DC /* BNCKeyChainTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F8BB66D278771890055D2DC /* BNCKeyChainTests.m */; }; 5F909B5E23314CE900A774D2 /* BNCJSONUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F73FC8023314697000EBD32 /* BNCJSONUtilityTests.m */; }; @@ -225,6 +154,7 @@ 670016701940F51400A9E103 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6700166F1940F51400A9E103 /* main.m */; }; 6700167A1940F51400A9E103 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 670016791940F51400A9E103 /* ViewController.m */; }; 67F270891BA9FCFF002546A7 /* CoreSpotlight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67F270881BA9FCFF002546A7 /* CoreSpotlight.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + B77CB6C72E8EE36300FDF144 /* BranchSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B77CB6C62E8EE36300FDF144 /* BranchSDK */; }; C10A6DE629A995590061A851 /* StoreKitTestCertificate.cer in Resources */ = {isa = PBXBuildFile; fileRef = C10A6DE529A995590061A851 /* StoreKitTestCertificate.cer */; }; C10C61AA282481FB00761D7E /* BranchShareLinkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C10C61A9282481FB00761D7E /* BranchShareLinkTests.m */; }; C12320B52808DB90007771C0 /* BranchQRCodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C12320B42808DB90007771C0 /* BranchQRCodeTests.m */; }; @@ -234,70 +164,17 @@ C17DAF7B2AC20C2000B16B1A /* BranchClassTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C17DAF7A2AC20C2000B16B1A /* BranchClassTests.m */; }; C1CC888229BAAFC000BDD2B5 /* BNCReferringURLUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C1CC888129BAAFC000BDD2B5 /* BNCReferringURLUtilityTests.m */; }; E51F642A2CF46899000858D2 /* BranchFileLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = E51F64292CF46899000858D2 /* BranchFileLogger.h */; }; - 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 */; }; 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 */; }; - E7AE4A0C2DFB2D0100696805 /* BranchConfigurationController.h in Headers */ = {isa = PBXBuildFile; fileRef = E7AE4A0B2DFB2D0100696805 /* BranchConfigurationController.h */; }; - E7E28ECA2DD2424C00F75D0D /* BNCInAppBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = E7E28EC82DD2424C00F75D0D /* BNCInAppBrowser.m */; }; - E7FC47732DFC7B020072B3ED /* BranchConfigurationController.m in Sources */ = {isa = PBXBuildFile; fileRef = E7FC47722DFC7B020072B3ED /* BranchConfigurationController.m */; }; + E7CA74B22E1B4890002EFB40 /* BranchConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = E7CA74B12E1B4890002EFB40 /* BranchConstants.h */; }; F1CF14111F4CC79F00BB2694 /* CoreSpotlight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67F270881BA9FCFF002546A7 /* CoreSpotlight.framework */; settings = {ATTRIBUTES = (Required, ); }; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 466B586F1B1777DE00A69EDE /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 670016581940F51400A9E103 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 466B58371B17773000A69EDE; - remoteInfo = Branch; - }; - 4683F07B1B20AC6300A432E7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 670016581940F51400A9E103 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 466B58371B17773000A69EDE; - remoteInfo = Branch; - }; - 4D337DDC201019B8009A5774 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 670016581940F51400A9E103 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 6700165F1940F51400A9E103; - remoteInfo = "Branch-TestBed"; - }; - 5F8B7B4121B5F5CD009CE0A6 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 670016581940F51400A9E103 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 466B58371B17773000A69EDE; - remoteInfo = Branch; - }; - E7AC74652DB064BC002D8C40 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 670016581940F51400A9E103 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 466B58371B17773000A69EDE; - remoteInfo = Branch; - }; - F1D4F9B11F323F01002D13FF /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 670016581940F51400A9E103 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 6700165F1940F51400A9E103; - remoteInfo = "Branch-TestBed"; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 4DF79403209B90B6003597E8 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; @@ -320,7 +197,6 @@ 0372078725E9F81000F29C30 /* UITestCaseMisc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UITestCaseMisc.m; sourceTree = ""; }; 0399DD112599BF8A00CDB36E /* UITestSendV2Event.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UITestSendV2Event.m; sourceTree = ""; }; 03B49EEA25F9F315000BF105 /* UITestCase0OpenNInstall.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UITestCase0OpenNInstall.m; sourceTree = ""; }; - 466B58381B17773000A69EDE /* libBranch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBranch.a; sourceTree = BUILT_PRODUCTS_DIR; }; 4AB16367239E3A2700D42931 /* DispatchToIsolationQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DispatchToIsolationQueueTests.m; sourceTree = ""; }; 4D1683812098C901008819E3 /* Branch-SDK-Tests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Branch-SDK-Tests-Bridging-Header.h"; sourceTree = ""; }; 4D1683842098C901008819E3 /* BNCLinkDataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCLinkDataTests.m; sourceTree = ""; }; @@ -379,7 +255,7 @@ 5F644B3B2B7AA810000DCD78 /* BNCNetworkInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCNetworkInterface.m; sourceTree = ""; }; 5F644B3C2B7AA810000DCD78 /* BNCPreferenceHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCPreferenceHelper.m; sourceTree = ""; }; 5F644B3D2B7AA810000DCD78 /* BNCConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCConfig.m; sourceTree = ""; }; - 5F644B3E2B7AA810000DCD78 /* BranchConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchConstants.m; sourceTree = ""; }; + 5F644B3E2B7AA810000DCD78 /* BranchConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BranchConstants.m; path = ../BranchSDK_ObjC/BranchConstants.m; sourceTree = ""; }; 5F644B3F2B7AA810000DCD78 /* BranchContentDiscoveryManifest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchContentDiscoveryManifest.m; sourceTree = ""; }; 5F644B402B7AA810000DCD78 /* BranchSpotlightUrlRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchSpotlightUrlRequest.m; sourceTree = ""; }; 5F644B422B7AA810000DCD78 /* BranchContentDiscoverer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchContentDiscoverer.m; sourceTree = ""; }; @@ -461,7 +337,6 @@ 5F644B932B7AA811000DCD78 /* BranchSpotlightUrlRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchSpotlightUrlRequest.h; sourceTree = ""; }; 5F644B942B7AA811000DCD78 /* BranchContentDiscoveryManifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchContentDiscoveryManifest.h; sourceTree = ""; }; 5F644B952B7AA811000DCD78 /* BNCConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCConfig.h; sourceTree = ""; }; - 5F644B962B7AA811000DCD78 /* BranchConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchConstants.h; sourceTree = ""; }; 5F644B972B7AA811000DCD78 /* BNCPartnerParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCPartnerParameters.h; sourceTree = ""; }; 5F644B982B7AA811000DCD78 /* BranchJsonConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchJsonConfig.h; sourceTree = ""; }; 5F644B992B7AA811000DCD78 /* BranchLATDRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchLATDRequest.h; sourceTree = ""; }; @@ -548,6 +423,161 @@ 67BBCF271A69E49A009C7DAE /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; 67F270881BA9FCFF002546A7 /* CoreSpotlight.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSpotlight.framework; path = System/Library/Frameworks/CoreSpotlight.framework; sourceTree = SDKROOT; }; 7E6B3B511AA42D0E005F45BF /* Branch-SDK-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Branch-SDK-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8CED1FA4F572F144DA1055F3 /* BranchRequestOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BranchRequestOperation.swift; path = ../Sources/BranchSwiftSDK/BranchRequestOperation.swift; sourceTree = SOURCE_ROOT; }; + B7A21E2B2E8EDD5F00BB1E14 /* BNCAppGroupsData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCAppGroupsData.h; sourceTree = ""; }; + B7A21E2C2E8EDD5F00BB1E14 /* BNCAppleReceipt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCAppleReceipt.h; sourceTree = ""; }; + B7A21E2D2E8EDD5F00BB1E14 /* BNCApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCApplication.h; sourceTree = ""; }; + B7A21E2E2E8EDD5F00BB1E14 /* BNCCallbackMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCCallbackMap.h; sourceTree = ""; }; + B7A21E2F2E8EDD5F00BB1E14 /* BNCConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCConfig.h; sourceTree = ""; }; + B7A21E302E8EDD5F00BB1E14 /* BNCContentDiscoveryManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCContentDiscoveryManager.h; sourceTree = ""; }; + B7A21E312E8EDD5F00BB1E14 /* BNCCrashlyticsWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCCrashlyticsWrapper.h; sourceTree = ""; }; + B7A21E322E8EDD5F00BB1E14 /* BNCDeepLinkViewControllerInstance.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCDeepLinkViewControllerInstance.h; sourceTree = ""; }; + B7A21E332E8EDD5F00BB1E14 /* BNCDeviceInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCDeviceInfo.h; sourceTree = ""; }; + B7A21E342E8EDD5F00BB1E14 /* BNCDeviceSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCDeviceSystem.h; sourceTree = ""; }; + B7A21E352E8EDD5F00BB1E14 /* BNCEncodingUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCEncodingUtils.h; sourceTree = ""; }; + B7A21E362E8EDD5F00BB1E14 /* BNCEventUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCEventUtils.h; sourceTree = ""; }; + B7A21E372E8EDD5F00BB1E14 /* BNCInAppBrowser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCInAppBrowser.h; sourceTree = ""; }; + B7A21E382E8EDD5F00BB1E14 /* BNCJSONUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCJSONUtility.h; sourceTree = ""; }; + B7A21E392E8EDD5F00BB1E14 /* BNCKeyChain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCKeyChain.h; sourceTree = ""; }; + B7A21E3A2E8EDD5F00BB1E14 /* BNCNetworkInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCNetworkInterface.h; sourceTree = ""; }; + B7A21E3B2E8EDD5F00BB1E14 /* BNCNetworkService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCNetworkService.h; sourceTree = ""; }; + B7A21E3C2E8EDD5F00BB1E14 /* BNCODMInfoCollector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCODMInfoCollector.h; sourceTree = ""; }; + B7A21E3D2E8EDD5F00BB1E14 /* BNCPartnerParameters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCPartnerParameters.h; sourceTree = ""; }; + B7A21E3E2E8EDD5F00BB1E14 /* BNCPasteboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCPasteboard.h; sourceTree = ""; }; + B7A21E3F2E8EDD5F00BB1E14 /* BNCQRCodeCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCQRCodeCache.h; sourceTree = ""; }; + B7A21E402E8EDD5F00BB1E14 /* BNCReachability.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCReachability.h; sourceTree = ""; }; + B7A21E412E8EDD5F00BB1E14 /* BNCReferringURLUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCReferringURLUtility.h; sourceTree = ""; }; + B7A21E422E8EDD5F00BB1E14 /* BNCRequestFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCRequestFactory.h; sourceTree = ""; }; + B7A21E432E8EDD5F00BB1E14 /* BNCServerAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCServerAPI.h; sourceTree = ""; }; + B7A21E442E8EDD5F00BB1E14 /* BNCServerRequestOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCServerRequestOperation.h; sourceTree = ""; }; + B7A21E452E8EDD5F00BB1E14 /* BNCSKAdNetwork.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCSKAdNetwork.h; sourceTree = ""; }; + B7A21E462E8EDD5F00BB1E14 /* BNCSpotlightService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCSpotlightService.h; sourceTree = ""; }; + B7A21E472E8EDD5F00BB1E14 /* BNCSystemObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCSystemObserver.h; sourceTree = ""; }; + B7A21E482E8EDD5F00BB1E14 /* BNCURLFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCURLFilter.h; sourceTree = ""; }; + B7A21E492E8EDD5F00BB1E14 /* BNCUrlQueryParameter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCUrlQueryParameter.h; sourceTree = ""; }; + B7A21E4A2E8EDD5F00BB1E14 /* BNCUserAgentCollector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCUserAgentCollector.h; sourceTree = ""; }; + B7A21E4B2E8EDD5F00BB1E14 /* Branch+Validator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Branch+Validator.h"; sourceTree = ""; }; + B7A21E4C2E8EDD5F00BB1E14 /* BranchConfigurationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchConfigurationController.h; sourceTree = ""; }; + B7A21E4D2E8EDD5F00BB1E14 /* BranchContentDiscoverer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchContentDiscoverer.h; sourceTree = ""; }; + B7A21E4E2E8EDD5F00BB1E14 /* BranchContentDiscoveryManifest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchContentDiscoveryManifest.h; sourceTree = ""; }; + B7A21E4F2E8EDD5F00BB1E14 /* BranchContentPathProperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchContentPathProperties.h; sourceTree = ""; }; + B7A21E502E8EDD5F00BB1E14 /* BranchFileLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchFileLogger.h; sourceTree = ""; }; + B7A21E512E8EDD5F00BB1E14 /* BranchInstallRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchInstallRequest.h; sourceTree = ""; }; + B7A21E522E8EDD5F00BB1E14 /* BranchJsonConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchJsonConfig.h; sourceTree = ""; }; + B7A21E532E8EDD5F00BB1E14 /* BranchLATDRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchLATDRequest.h; sourceTree = ""; }; + B7A21E542E8EDD5F00BB1E14 /* BranchOpenRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchOpenRequest.h; sourceTree = ""; }; + B7A21E552E8EDD5F00BB1E14 /* BranchShortUrlRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchShortUrlRequest.h; sourceTree = ""; }; + B7A21E562E8EDD5F00BB1E14 /* BranchShortUrlSyncRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchShortUrlSyncRequest.h; sourceTree = ""; }; + B7A21E572E8EDD5F00BB1E14 /* BranchSpotlightUrlRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchSpotlightUrlRequest.h; sourceTree = ""; }; + B7A21E582E8EDD5F00BB1E14 /* NSError+Branch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSError+Branch.h"; sourceTree = ""; }; + B7A21E592E8EDD5F00BB1E14 /* NSMutableDictionary+Branch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableDictionary+Branch.h"; sourceTree = ""; }; + B7A21E5A2E8EDD5F00BB1E14 /* NSString+Branch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+Branch.h"; sourceTree = ""; }; + B7A21E5B2E8EDD5F00BB1E14 /* UIViewController+Branch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Branch.h"; sourceTree = ""; }; + B7A21E5D2E8EDD5F00BB1E14 /* BNCCallbacks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCCallbacks.h; sourceTree = ""; }; + B7A21E5E2E8EDD5F00BB1E14 /* BNCCurrency.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCCurrency.h; sourceTree = ""; }; + B7A21E5F2E8EDD5F00BB1E14 /* BNCInitSessionResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCInitSessionResponse.h; sourceTree = ""; }; + B7A21E602E8EDD5F00BB1E14 /* BNCLinkCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCLinkCache.h; sourceTree = ""; }; + B7A21E612E8EDD5F00BB1E14 /* BNCLinkData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCLinkData.h; sourceTree = ""; }; + B7A21E622E8EDD5F00BB1E14 /* BNCNetworkServiceProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCNetworkServiceProtocol.h; sourceTree = ""; }; + B7A21E632E8EDD5F00BB1E14 /* BNCPreferenceHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCPreferenceHelper.h; sourceTree = ""; }; + B7A21E642E8EDD5F00BB1E14 /* BNCProductCategory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCProductCategory.h; sourceTree = ""; }; + B7A21E652E8EDD5F00BB1E14 /* BNCServerInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCServerInterface.h; sourceTree = ""; }; + B7A21E662E8EDD5F00BB1E14 /* BNCServerRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCServerRequest.h; sourceTree = ""; }; + B7A21E672E8EDD5F00BB1E14 /* BNCServerRequestQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCServerRequestQueue.h; sourceTree = ""; }; + B7A21E682E8EDD5F00BB1E14 /* BNCServerResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCServerResponse.h; sourceTree = ""; }; + B7A21E692E8EDD5F00BB1E14 /* Branch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Branch.h; sourceTree = ""; }; + B7A21E6A2E8EDD5F00BB1E14 /* BranchActivityItemProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchActivityItemProvider.h; sourceTree = ""; }; + B7A21E6B2E8EDD5F00BB1E14 /* BranchConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchConstants.h; sourceTree = ""; }; + B7A21E6C2E8EDD5F00BB1E14 /* BranchCSSearchableItemAttributeSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchCSSearchableItemAttributeSet.h; sourceTree = ""; }; + B7A21E6D2E8EDD5F00BB1E14 /* BranchDeepLinkingController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchDeepLinkingController.h; sourceTree = ""; }; + B7A21E6E2E8EDD5F00BB1E14 /* BranchDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchDelegate.h; sourceTree = ""; }; + B7A21E6F2E8EDD5F00BB1E14 /* BranchEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchEvent.h; sourceTree = ""; }; + B7A21E702E8EDD5F00BB1E14 /* BranchLastAttributedTouchData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchLastAttributedTouchData.h; sourceTree = ""; }; + B7A21E712E8EDD5F00BB1E14 /* BranchLinkProperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchLinkProperties.h; sourceTree = ""; }; + B7A21E722E8EDD5F00BB1E14 /* BranchLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchLogger.h; sourceTree = ""; }; + B7A21E732E8EDD5F00BB1E14 /* BranchPasteControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchPasteControl.h; sourceTree = ""; }; + B7A21E742E8EDD5F00BB1E14 /* BranchPluginSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchPluginSupport.h; sourceTree = ""; }; + B7A21E752E8EDD5F00BB1E14 /* BranchQRCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchQRCode.h; sourceTree = ""; }; + B7A21E762E8EDD5F00BB1E14 /* BranchScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchScene.h; sourceTree = ""; }; + B7A21E772E8EDD5F00BB1E14 /* BranchSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchSDK.h; sourceTree = ""; }; + B7A21E782E8EDD5F00BB1E14 /* BranchShareLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchShareLink.h; sourceTree = ""; }; + B7A21E792E8EDD5F00BB1E14 /* BranchUniversalObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchUniversalObject.h; sourceTree = ""; }; + B7A21E7B2E8EDD5F00BB1E14 /* BNCAppGroupsData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCAppGroupsData.m; sourceTree = ""; }; + B7A21E7C2E8EDD5F00BB1E14 /* BNCAppleReceipt.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCAppleReceipt.m; sourceTree = ""; }; + B7A21E7D2E8EDD5F00BB1E14 /* BNCApplication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCApplication.m; sourceTree = ""; }; + B7A21E7E2E8EDD5F00BB1E14 /* BNCCallbackMap.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCCallbackMap.m; sourceTree = ""; }; + B7A21E7F2E8EDD5F00BB1E14 /* BNCConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCConfig.m; sourceTree = ""; }; + B7A21E802E8EDD5F00BB1E14 /* BNCContentDiscoveryManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCContentDiscoveryManager.m; sourceTree = ""; }; + B7A21E812E8EDD5F00BB1E14 /* BNCCrashlyticsWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCCrashlyticsWrapper.m; sourceTree = ""; }; + B7A21E822E8EDD5F00BB1E14 /* BNCCurrency.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCCurrency.m; sourceTree = ""; }; + B7A21E832E8EDD5F00BB1E14 /* BNCDeepLinkViewControllerInstance.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCDeepLinkViewControllerInstance.m; sourceTree = ""; }; + B7A21E842E8EDD5F00BB1E14 /* BNCDeviceInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCDeviceInfo.m; sourceTree = ""; }; + B7A21E852E8EDD5F00BB1E14 /* BNCDeviceSystem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCDeviceSystem.m; sourceTree = ""; }; + B7A21E862E8EDD5F00BB1E14 /* BNCEncodingUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCEncodingUtils.m; sourceTree = ""; }; + B7A21E872E8EDD5F00BB1E14 /* BNCEventUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCEventUtils.m; sourceTree = ""; }; + B7A21E882E8EDD5F00BB1E14 /* BNCInAppBrowser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCInAppBrowser.m; sourceTree = ""; }; + B7A21E892E8EDD5F00BB1E14 /* BNCInitSessionResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCInitSessionResponse.m; sourceTree = ""; }; + B7A21E8A2E8EDD5F00BB1E14 /* BNCJSONUtility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCJSONUtility.m; sourceTree = ""; }; + B7A21E8B2E8EDD5F00BB1E14 /* BNCKeyChain.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCKeyChain.m; sourceTree = ""; }; + B7A21E8C2E8EDD5F00BB1E14 /* BNCLinkCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCLinkCache.m; sourceTree = ""; }; + B7A21E8D2E8EDD5F00BB1E14 /* BNCLinkData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCLinkData.m; sourceTree = ""; }; + B7A21E8E2E8EDD5F00BB1E14 /* BNCNetworkInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCNetworkInterface.m; sourceTree = ""; }; + B7A21E8F2E8EDD5F00BB1E14 /* BNCNetworkService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCNetworkService.m; sourceTree = ""; }; + B7A21E902E8EDD5F00BB1E14 /* BNCODMInfoCollector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCODMInfoCollector.m; sourceTree = ""; }; + B7A21E912E8EDD5F00BB1E14 /* BNCPartnerParameters.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCPartnerParameters.m; sourceTree = ""; }; + B7A21E922E8EDD5F00BB1E14 /* BNCPasteboard.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCPasteboard.m; sourceTree = ""; }; + B7A21E932E8EDD5F00BB1E14 /* BNCPreferenceHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCPreferenceHelper.m; sourceTree = ""; }; + B7A21E942E8EDD5F00BB1E14 /* BNCProductCategory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCProductCategory.m; sourceTree = ""; }; + B7A21E952E8EDD5F00BB1E14 /* BNCQRCodeCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCQRCodeCache.m; sourceTree = ""; }; + B7A21E962E8EDD5F00BB1E14 /* BNCReachability.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCReachability.m; sourceTree = ""; }; + B7A21E972E8EDD5F00BB1E14 /* BNCReferringURLUtility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCReferringURLUtility.m; sourceTree = ""; }; + B7A21E982E8EDD5F00BB1E14 /* BNCRequestFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCRequestFactory.m; sourceTree = ""; }; + B7A21E992E8EDD5F00BB1E14 /* BNCServerAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCServerAPI.m; sourceTree = ""; }; + B7A21E9A2E8EDD5F00BB1E14 /* BNCServerInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCServerInterface.m; sourceTree = ""; }; + B7A21E9B2E8EDD5F00BB1E14 /* BNCServerRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCServerRequest.m; sourceTree = ""; }; + B7A21E9C2E8EDD5F00BB1E14 /* BNCServerRequestOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCServerRequestOperation.m; sourceTree = ""; }; + B7A21E9D2E8EDD5F00BB1E14 /* BNCServerRequestQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCServerRequestQueue.m; sourceTree = ""; }; + B7A21E9E2E8EDD5F00BB1E14 /* BNCServerResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCServerResponse.m; sourceTree = ""; }; + B7A21E9F2E8EDD5F00BB1E14 /* BNCSKAdNetwork.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCSKAdNetwork.m; sourceTree = ""; }; + B7A21EA02E8EDD5F00BB1E14 /* BNCSpotlightService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCSpotlightService.m; sourceTree = ""; }; + B7A21EA12E8EDD5F00BB1E14 /* BNCSystemObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCSystemObserver.m; sourceTree = ""; }; + B7A21EA22E8EDD5F00BB1E14 /* BNCURLFilter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCURLFilter.m; sourceTree = ""; }; + B7A21EA32E8EDD5F00BB1E14 /* BNCUrlQueryParameter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCUrlQueryParameter.m; sourceTree = ""; }; + B7A21EA42E8EDD5F00BB1E14 /* BNCUserAgentCollector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCUserAgentCollector.m; sourceTree = ""; }; + B7A21EA52E8EDD5F00BB1E14 /* Branch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Branch.m; sourceTree = ""; }; + B7A21EA62E8EDD5F00BB1E14 /* Branch-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Branch-Bridging-Header.h"; sourceTree = ""; }; + B7A21EA72E8EDD5F00BB1E14 /* Branch+Validator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Branch+Validator.m"; sourceTree = ""; }; + B7A21EA82E8EDD5F00BB1E14 /* BranchActivityItemProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchActivityItemProvider.m; sourceTree = ""; }; + B7A21EA92E8EDD5F00BB1E14 /* BranchConfigurationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchConfigurationController.m; sourceTree = ""; }; + B7A21EAA2E8EDD5F00BB1E14 /* BranchConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchConstants.m; sourceTree = ""; }; + B7A21EAB2E8EDD5F00BB1E14 /* BranchContentDiscoverer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchContentDiscoverer.m; sourceTree = ""; }; + B7A21EAC2E8EDD5F00BB1E14 /* BranchContentDiscoveryManifest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchContentDiscoveryManifest.m; sourceTree = ""; }; + B7A21EAD2E8EDD5F00BB1E14 /* BranchContentPathProperties.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchContentPathProperties.m; sourceTree = ""; }; + B7A21EAE2E8EDD5F00BB1E14 /* BranchCSSearchableItemAttributeSet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchCSSearchableItemAttributeSet.m; sourceTree = ""; }; + B7A21EAF2E8EDD5F00BB1E14 /* BranchDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchDelegate.m; sourceTree = ""; }; + B7A21EB02E8EDD5F00BB1E14 /* BranchEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchEvent.m; sourceTree = ""; }; + B7A21EB12E8EDD5F00BB1E14 /* BranchFileLogger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchFileLogger.m; sourceTree = ""; }; + B7A21EB22E8EDD5F00BB1E14 /* BranchInstallRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchInstallRequest.m; sourceTree = ""; }; + B7A21EB32E8EDD5F00BB1E14 /* BranchJsonConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchJsonConfig.m; sourceTree = ""; }; + B7A21EB42E8EDD5F00BB1E14 /* BranchLastAttributedTouchData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchLastAttributedTouchData.m; sourceTree = ""; }; + B7A21EB52E8EDD5F00BB1E14 /* BranchLATDRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchLATDRequest.m; sourceTree = ""; }; + B7A21EB62E8EDD5F00BB1E14 /* BranchLinkProperties.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchLinkProperties.m; sourceTree = ""; }; + B7A21EB72E8EDD5F00BB1E14 /* BranchLogger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchLogger.m; sourceTree = ""; }; + B7A21EB82E8EDD5F00BB1E14 /* BranchOpenRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchOpenRequest.m; sourceTree = ""; }; + B7A21EB92E8EDD5F00BB1E14 /* BranchPasteControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchPasteControl.m; sourceTree = ""; }; + B7A21EBA2E8EDD5F00BB1E14 /* BranchPluginSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchPluginSupport.m; sourceTree = ""; }; + B7A21EBB2E8EDD5F00BB1E14 /* BranchQRCode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchQRCode.m; sourceTree = ""; }; + B7A21EBC2E8EDD5F00BB1E14 /* BranchScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchScene.m; sourceTree = ""; }; + B7A21EBD2E8EDD5F00BB1E14 /* BranchShareLink.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchShareLink.m; sourceTree = ""; }; + B7A21EBE2E8EDD5F00BB1E14 /* BranchShortUrlRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchShortUrlRequest.m; sourceTree = ""; }; + B7A21EBF2E8EDD5F00BB1E14 /* BranchShortUrlSyncRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchShortUrlSyncRequest.m; sourceTree = ""; }; + B7A21EC02E8EDD5F00BB1E14 /* BranchSpotlightUrlRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchSpotlightUrlRequest.m; sourceTree = ""; }; + B7A21EC12E8EDD5F00BB1E14 /* BranchUniversalObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchUniversalObject.m; sourceTree = ""; }; + B7A21EC22E8EDD5F00BB1E14 /* NSError+Branch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSError+Branch.m"; sourceTree = ""; }; + B7A21EC32E8EDD5F00BB1E14 /* NSMutableDictionary+Branch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableDictionary+Branch.m"; sourceTree = ""; }; + B7A21EC42E8EDD5F00BB1E14 /* NSString+Branch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+Branch.m"; sourceTree = ""; }; + B7A21EC52E8EDD5F00BB1E14 /* UIViewController+Branch.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Branch.m"; sourceTree = ""; }; + B9BB25109C12DC8BD2D2F665 /* BranchRequestQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BranchRequestQueue.swift; path = ../Sources/BranchSwiftSDK/BranchRequestQueue.swift; sourceTree = SOURCE_ROOT; }; C10A6DE029A97E440061A851 /* TestStoreKitConfig.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = TestStoreKitConfig.storekit; sourceTree = ""; }; C10A6DE529A995590061A851 /* StoreKitTestCertificate.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = StoreKitTestCertificate.cer; sourceTree = ""; }; C10C61A9282481FB00761D7E /* BranchShareLinkTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchShareLinkTests.m; sourceTree = ""; }; @@ -558,6 +588,7 @@ C1614D55285BC8A00098946B /* LinkPresentation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LinkPresentation.framework; path = System/Library/Frameworks/LinkPresentation.framework; sourceTree = SDKROOT; }; C17DAF7A2AC20C2000B16B1A /* BranchClassTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchClassTests.m; sourceTree = ""; }; C1CC888129BAAFC000BDD2B5 /* BNCReferringURLUtilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCReferringURLUtilityTests.m; sourceTree = ""; }; + D8B1EA48C972289E75C4943A /* ConfigurationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConfigurationController.swift; path = ../Sources/BranchSwiftSDK/ConfigurationController.swift; sourceTree = SOURCE_ROOT; }; E51F64292CF46899000858D2 /* BranchFileLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchFileLogger.h; sourceTree = ""; }; E563942F2CC7AC9500E18E65 /* BranchFileLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchFileLogger.m; sourceTree = ""; }; E71E397A2DD3C14800110F59 /* BNCInAppBrowser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCInAppBrowser.h; sourceTree = ""; }; @@ -573,9 +604,7 @@ E7AC74782DB06D47002D8C40 /* NSError+Branch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSError+Branch.h"; sourceTree = ""; }; E7AC747D2DB07145002D8C40 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk/usr/lib/libc++.tbd"; sourceTree = DEVELOPER_DIR; }; E7AE4A082DFB2C4400696805 /* BranchConfigurationControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchConfigurationControllerTests.m; sourceTree = ""; }; - E7AE4A0B2DFB2D0100696805 /* BranchConfigurationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchConfigurationController.h; sourceTree = ""; }; E7E28EC82DD2424C00F75D0D /* BNCInAppBrowser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCInAppBrowser.m; sourceTree = ""; }; - E7FC47722DFC7B020072B3ED /* BranchConfigurationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BranchConfigurationController.m; path = ../Sources/BranchSDK/BranchConfigurationController.m; sourceTree = ""; }; F1D4F9AC1F323F01002D13FF /* Branch-TestBed-UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Branch-TestBed-UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -588,6 +617,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 40EA3FC7C54A6B3CCEBDB9F8 /* (null) in Frameworks */, + 5AFDEF31D95DB3252A8F146D /* (null) in Frameworks */, 5F92B2362383644C00CA909B /* SystemConfiguration.framework in Frameworks */, 5F205D05231864E800C776D1 /* WebKit.framework in Frameworks */, 5FF7D2862A9549B40049158D /* AdServices.framework in Frameworks */, @@ -604,7 +635,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5F8B7B4021B5F5CD009CE0A6 /* libBranch.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -618,7 +648,6 @@ 670016661940F51400A9E103 /* CoreGraphics.framework in Frameworks */, 67F270891BA9FCFF002546A7 /* CoreSpotlight.framework in Frameworks */, 670016641940F51400A9E103 /* Foundation.framework in Frameworks */, - 466B58811B1778DB00A69EDE /* libBranch.a in Frameworks */, 670016681940F51400A9E103 /* UIKit.framework in Frameworks */, 5F42763325DB3694005B9BBC /* AdServices.framework in Frameworks */, ); @@ -636,10 +665,6 @@ buildActionMask = 2147483647; files = ( E7AC747E2DB0714B002D8C40 /* libc++.tbd in Frameworks */, - E7AC747B2DB0700D002D8C40 /* BranchSDK in Frameworks */, - E7AC74752DB069B2002D8C40 /* nanopb in Frameworks */, - E7AC74722DB06992002D8C40 /* GULNetwork in Frameworks */, - E7AC74702DB06992002D8C40 /* GULLogger in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -647,7 +672,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0372076425E8418F00F29C30 /* libBranch.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -724,92 +748,10 @@ path = "Branch-TestBed-UITests"; sourceTree = ""; }; - 5F644B252B7AA810000DCD78 /* BranchSDK */ = { - isa = PBXGroup; - children = ( - E563942F2CC7AC9500E18E65 /* BranchFileLogger.m */, - 5F644BB02B7AA811000DCD78 /* BNCAppGroupsData.m */, - 5F644BA82B7AA811000DCD78 /* BNCAppleReceipt.m */, - 5F644B2B2B7AA810000DCD78 /* BNCApplication.m */, - 5F644BB72B7AA811000DCD78 /* BNCCallbackMap.m */, - 5F644B3D2B7AA810000DCD78 /* BNCConfig.m */, - 5F644B322B7AA810000DCD78 /* BNCContentDiscoveryManager.m */, - 5F644B492B7AA810000DCD78 /* BNCCrashlyticsWrapper.m */, - 5F644BA32B7AA811000DCD78 /* BNCCurrency.m */, - 5F644B302B7AA810000DCD78 /* BNCDeepLinkViewControllerInstance.m */, - 5F644B432B7AA810000DCD78 /* BNCDeviceInfo.m */, - 5F644B2C2B7AA810000DCD78 /* BNCDeviceSystem.m */, - 5F644B282B7AA810000DCD78 /* BNCEncodingUtils.m */, - 5F644BB82B7AA811000DCD78 /* BNCEventUtils.m */, - 5F644BAA2B7AA811000DCD78 /* BNCInitSessionResponse.m */, - E7E28EC82DD2424C00F75D0D /* BNCInAppBrowser.m */, - 5F644BA22B7AA811000DCD78 /* BNCJSONUtility.m */, - 5F644B512B7AA810000DCD78 /* BNCKeyChain.m */, - 5F644BAB2B7AA811000DCD78 /* BNCLinkCache.m */, - E7AC74772DB06D05002D8C40 /* NSError+Branch.m */, - 5F644BA02B7AA811000DCD78 /* BNCLinkData.m */, - 5F644B3B2B7AA810000DCD78 /* BNCNetworkInterface.m */, - 5F644B442B7AA810000DCD78 /* BNCNetworkService.m */, - 5F644B4D2B7AA810000DCD78 /* BNCPartnerParameters.m */, - 5F644BA72B7AA811000DCD78 /* BNCPasteboard.m */, - 5F644B3C2B7AA810000DCD78 /* BNCPreferenceHelper.m */, - 5F644B482B7AA810000DCD78 /* BNCProductCategory.m */, - 5F644BB52B7AA811000DCD78 /* BNCQRCodeCache.m */, - 5F644B452B7AA810000DCD78 /* BNCReachability.m */, - 5F644B472B7AA810000DCD78 /* BNCReferringURLUtility.m */, - 5F644B462B7AA810000DCD78 /* BNCRequestFactory.m */, - 5F644BAD2B7AA811000DCD78 /* BNCServerAPI.m */, - 5F644B4A2B7AA810000DCD78 /* BNCServerInterface.m */, - 5F644B3A2B7AA810000DCD78 /* BNCServerRequest.m */, - 5F644B2E2B7AA810000DCD78 /* BNCServerRequestQueue.m */, - 5F644BB12B7AA811000DCD78 /* BNCServerResponse.m */, - 5F644BA52B7AA811000DCD78 /* BNCSKAdNetwork.m */, - 5F644BAC2B7AA811000DCD78 /* BNCSpotlightService.m */, - 5F644B372B7AA810000DCD78 /* BNCSystemObserver.m */, - 5F644B392B7AA810000DCD78 /* BNCURLFilter.m */, - 5F644B2F2B7AA810000DCD78 /* BNCUrlQueryParameter.m */, - 5F644B272B7AA810000DCD78 /* BNCUserAgentCollector.m */, - E7AC74582DB063C6002D8C40 /* BNCODMInfoCollector.m */, - 5F644BA12B7AA811000DCD78 /* Branch.m */, - 5F644B332B7AA810000DCD78 /* Branch+Validator.m */, - 5F644B4F2B7AA810000DCD78 /* BranchActivityItemProvider.m */, - 5F644B3E2B7AA810000DCD78 /* BranchConstants.m */, - 5F644B422B7AA810000DCD78 /* BranchContentDiscoverer.m */, - 5F644B3F2B7AA810000DCD78 /* BranchContentDiscoveryManifest.m */, - 5F644BA62B7AA811000DCD78 /* BranchContentPathProperties.m */, - 5F644B4E2B7AA810000DCD78 /* BranchCSSearchableItemAttributeSet.m */, - 5F644B9F2B7AA811000DCD78 /* BranchDelegate.m */, - 5F644BB22B7AA811000DCD78 /* BranchEvent.m */, - 5F644B342B7AA810000DCD78 /* BranchInstallRequest.m */, - 5F644B4B2B7AA810000DCD78 /* BranchJsonConfig.m */, - 5F644BA42B7AA811000DCD78 /* BranchLastAttributedTouchData.m */, - 5F644B4C2B7AA810000DCD78 /* BranchLATDRequest.m */, - 5F644BA92B7AA811000DCD78 /* BranchLinkProperties.m */, - 5F5FDA0F2B7DE20800F14A43 /* BranchLogger.m */, - 5F644BB42B7AA811000DCD78 /* BranchOpenRequest.m */, - 5F644B2A2B7AA810000DCD78 /* BranchPasteControl.m */, - 5F644B352B7AA810000DCD78 /* BranchPluginSupport.m */, - 5F644B502B7AA810000DCD78 /* BranchQRCode.m */, - 5F644B312B7AA810000DCD78 /* BranchScene.m */, - 5F644BAE2B7AA811000DCD78 /* BranchShareLink.m */, - 5F644BB32B7AA811000DCD78 /* BranchShortUrlRequest.m */, - 5F644BB62B7AA811000DCD78 /* BranchShortUrlSyncRequest.m */, - 5F644B402B7AA810000DCD78 /* BranchSpotlightUrlRequest.m */, - 5F644B292B7AA810000DCD78 /* BranchUniversalObject.m */, - 5F644B262B7AA810000DCD78 /* NSError+Branch.m */, - 5F644B2D2B7AA810000DCD78 /* NSMutableDictionary+Branch.m */, - 5F644B362B7AA810000DCD78 /* NSString+Branch.m */, - 5F644BAF2B7AA811000DCD78 /* UIViewController+Branch.m */, - 5F644B6E2B7AA810000DCD78 /* Private */, - 5F644B522B7AA810000DCD78 /* Public */, - ); - name = BranchSDK; - path = ../Sources/BranchSDK; - sourceTree = ""; - }; 5F644B522B7AA810000DCD78 /* Public */ = { isa = PBXGroup; children = ( + E7CA74B12E1B4890002EFB40 /* BranchConstants.h */, 5F644B5E2B7AA810000DCD78 /* BNCCallbacks.h */, 5F644B5A2B7AA810000DCD78 /* BNCCurrency.h */, 5F644B612B7AA810000DCD78 /* BNCInitSessionResponse.h */, @@ -835,7 +777,6 @@ 5F644B692B7AA810000DCD78 /* BranchPluginSupport.h */, 5F644B552B7AA810000DCD78 /* BranchQRCode.h */, 5F644B6A2B7AA810000DCD78 /* BranchScene.h */, - 5F644B592B7AA810000DCD78 /* BranchSDK.h */, 5F644B5F2B7AA810000DCD78 /* BranchShareLink.h */, 5F644B662B7AA810000DCD78 /* BranchUniversalObject.h */, ); @@ -845,7 +786,6 @@ 5F644B6E2B7AA810000DCD78 /* Private */ = { isa = PBXGroup; children = ( - E7AE4A0B2DFB2D0100696805 /* BranchConfigurationController.h */, E71E397A2DD3C14800110F59 /* BNCInAppBrowser.h */, 5F644B762B7AA810000DCD78 /* BNCAppGroupsData.h */, 5F644B702B7AA810000DCD78 /* BNCAppleReceipt.h */, @@ -878,7 +818,6 @@ 5F644B842B7AA811000DCD78 /* BNCUserAgentCollector.h */, E7AC74562DB0639E002D8C40 /* BNCODMInfoCollector.h */, 5F644B8D2B7AA811000DCD78 /* Branch+Validator.h */, - 5F644B962B7AA811000DCD78 /* BranchConstants.h */, 5F644B922B7AA811000DCD78 /* BranchContentDiscoverer.h */, 5F644B942B7AA811000DCD78 /* BranchContentDiscoveryManifest.h */, 5F644B722B7AA810000DCD78 /* BranchContentPathProperties.h */, @@ -924,12 +863,10 @@ 670016571940F51400A9E103 = { isa = PBXGroup; children = ( - E7FC47722DFC7B020072B3ED /* BranchConfigurationController.m */, E7AC74762DB06B42002D8C40 /* Reflection_ODM_Tests.xctestplan */, 6589EBA52674270100F2E28B /* Branch-TestBed-CI.xctestplan */, 033FC71025AC1E5800D8319E /* Branch-TestBed.xctestplan */, 4D93D8592098CC4400CFABA6 /* README.md */, - 5F644B252B7AA810000DCD78 /* BranchSDK */, 4D16837A2098C901008819E3 /* Branch-SDK-Tests */, 5F8B7B3C21B5F5CD009CE0A6 /* Branch-SDK-Unhosted-Tests */, 670016691940F51400A9E103 /* Branch-TestBed */, @@ -946,7 +883,6 @@ children = ( 670016601940F51400A9E103 /* Branch-TestBed.app */, 7E6B3B511AA42D0E005F45BF /* Branch-SDK-Tests.xctest */, - 466B58381B17773000A69EDE /* libBranch.a */, F1D4F9AC1F323F01002D13FF /* Branch-TestBed-UITests.xctest */, 5F8B7B3B21B5F5CD009CE0A6 /* Branch-SDK-Unhosted-Tests.xctest */, E7AC74602DB064BC002D8C40 /* Reflection_ODM_Tests.xctest */, @@ -1016,6 +952,97 @@ name = "Supporting Files"; sourceTree = ""; }; + B7A21E5C2E8EDD5F00BB1E14 /* Private */ = { + isa = PBXGroup; + children = ( + B7A21E2B2E8EDD5F00BB1E14 /* BNCAppGroupsData.h */, + B7A21E2C2E8EDD5F00BB1E14 /* BNCAppleReceipt.h */, + B7A21E2D2E8EDD5F00BB1E14 /* BNCApplication.h */, + B7A21E2E2E8EDD5F00BB1E14 /* BNCCallbackMap.h */, + B7A21E2F2E8EDD5F00BB1E14 /* BNCConfig.h */, + B7A21E302E8EDD5F00BB1E14 /* BNCContentDiscoveryManager.h */, + B7A21E312E8EDD5F00BB1E14 /* BNCCrashlyticsWrapper.h */, + B7A21E322E8EDD5F00BB1E14 /* BNCDeepLinkViewControllerInstance.h */, + B7A21E332E8EDD5F00BB1E14 /* BNCDeviceInfo.h */, + B7A21E342E8EDD5F00BB1E14 /* BNCDeviceSystem.h */, + B7A21E352E8EDD5F00BB1E14 /* BNCEncodingUtils.h */, + B7A21E362E8EDD5F00BB1E14 /* BNCEventUtils.h */, + B7A21E372E8EDD5F00BB1E14 /* BNCInAppBrowser.h */, + B7A21E382E8EDD5F00BB1E14 /* BNCJSONUtility.h */, + B7A21E392E8EDD5F00BB1E14 /* BNCKeyChain.h */, + B7A21E3A2E8EDD5F00BB1E14 /* BNCNetworkInterface.h */, + B7A21E3B2E8EDD5F00BB1E14 /* BNCNetworkService.h */, + B7A21E3C2E8EDD5F00BB1E14 /* BNCODMInfoCollector.h */, + B7A21E3D2E8EDD5F00BB1E14 /* BNCPartnerParameters.h */, + B7A21E3E2E8EDD5F00BB1E14 /* BNCPasteboard.h */, + B7A21E3F2E8EDD5F00BB1E14 /* BNCQRCodeCache.h */, + B7A21E402E8EDD5F00BB1E14 /* BNCReachability.h */, + B7A21E412E8EDD5F00BB1E14 /* BNCReferringURLUtility.h */, + B7A21E422E8EDD5F00BB1E14 /* BNCRequestFactory.h */, + B7A21E432E8EDD5F00BB1E14 /* BNCServerAPI.h */, + B7A21E442E8EDD5F00BB1E14 /* BNCServerRequestOperation.h */, + B7A21E452E8EDD5F00BB1E14 /* BNCSKAdNetwork.h */, + B7A21E462E8EDD5F00BB1E14 /* BNCSpotlightService.h */, + B7A21E472E8EDD5F00BB1E14 /* BNCSystemObserver.h */, + B7A21E482E8EDD5F00BB1E14 /* BNCURLFilter.h */, + B7A21E492E8EDD5F00BB1E14 /* BNCUrlQueryParameter.h */, + B7A21E4A2E8EDD5F00BB1E14 /* BNCUserAgentCollector.h */, + B7A21E4B2E8EDD5F00BB1E14 /* Branch+Validator.h */, + B7A21E4C2E8EDD5F00BB1E14 /* BranchConfigurationController.h */, + B7A21E4D2E8EDD5F00BB1E14 /* BranchContentDiscoverer.h */, + B7A21E4E2E8EDD5F00BB1E14 /* BranchContentDiscoveryManifest.h */, + B7A21E4F2E8EDD5F00BB1E14 /* BranchContentPathProperties.h */, + B7A21E502E8EDD5F00BB1E14 /* BranchFileLogger.h */, + B7A21E512E8EDD5F00BB1E14 /* BranchInstallRequest.h */, + B7A21E522E8EDD5F00BB1E14 /* BranchJsonConfig.h */, + B7A21E532E8EDD5F00BB1E14 /* BranchLATDRequest.h */, + B7A21E542E8EDD5F00BB1E14 /* BranchOpenRequest.h */, + B7A21E552E8EDD5F00BB1E14 /* BranchShortUrlRequest.h */, + B7A21E562E8EDD5F00BB1E14 /* BranchShortUrlSyncRequest.h */, + B7A21E572E8EDD5F00BB1E14 /* BranchSpotlightUrlRequest.h */, + B7A21E582E8EDD5F00BB1E14 /* NSError+Branch.h */, + B7A21E592E8EDD5F00BB1E14 /* NSMutableDictionary+Branch.h */, + B7A21E5A2E8EDD5F00BB1E14 /* NSString+Branch.h */, + B7A21E5B2E8EDD5F00BB1E14 /* UIViewController+Branch.h */, + ); + path = Private; + sourceTree = ""; + }; + B7A21E7A2E8EDD5F00BB1E14 /* Public */ = { + isa = PBXGroup; + children = ( + B7A21E5D2E8EDD5F00BB1E14 /* BNCCallbacks.h */, + B7A21E5E2E8EDD5F00BB1E14 /* BNCCurrency.h */, + B7A21E5F2E8EDD5F00BB1E14 /* BNCInitSessionResponse.h */, + B7A21E602E8EDD5F00BB1E14 /* BNCLinkCache.h */, + B7A21E612E8EDD5F00BB1E14 /* BNCLinkData.h */, + B7A21E622E8EDD5F00BB1E14 /* BNCNetworkServiceProtocol.h */, + B7A21E632E8EDD5F00BB1E14 /* BNCPreferenceHelper.h */, + B7A21E642E8EDD5F00BB1E14 /* BNCProductCategory.h */, + B7A21E652E8EDD5F00BB1E14 /* BNCServerInterface.h */, + B7A21E662E8EDD5F00BB1E14 /* BNCServerRequest.h */, + B7A21E672E8EDD5F00BB1E14 /* BNCServerRequestQueue.h */, + B7A21E682E8EDD5F00BB1E14 /* BNCServerResponse.h */, + B7A21E692E8EDD5F00BB1E14 /* Branch.h */, + B7A21E6A2E8EDD5F00BB1E14 /* BranchActivityItemProvider.h */, + B7A21E6B2E8EDD5F00BB1E14 /* BranchConstants.h */, + B7A21E6C2E8EDD5F00BB1E14 /* BranchCSSearchableItemAttributeSet.h */, + B7A21E6D2E8EDD5F00BB1E14 /* BranchDeepLinkingController.h */, + B7A21E6E2E8EDD5F00BB1E14 /* BranchDelegate.h */, + B7A21E6F2E8EDD5F00BB1E14 /* BranchEvent.h */, + B7A21E702E8EDD5F00BB1E14 /* BranchLastAttributedTouchData.h */, + B7A21E712E8EDD5F00BB1E14 /* BranchLinkProperties.h */, + B7A21E722E8EDD5F00BB1E14 /* BranchLogger.h */, + B7A21E732E8EDD5F00BB1E14 /* BranchPasteControl.h */, + B7A21E742E8EDD5F00BB1E14 /* BranchPluginSupport.h */, + B7A21E752E8EDD5F00BB1E14 /* BranchQRCode.h */, + B7A21E762E8EDD5F00BB1E14 /* BranchScene.h */, + B7A21E782E8EDD5F00BB1E14 /* BranchShareLink.h */, + B7A21E792E8EDD5F00BB1E14 /* BranchUniversalObject.h */, + ); + path = Public; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1030,7 +1057,6 @@ 5F644BE82B7AA811000DCD78 /* BranchActivityItemProvider.h in Headers */, 5F644BE92B7AA811000DCD78 /* BranchCSSearchableItemAttributeSet.h in Headers */, 5F644BEA2B7AA811000DCD78 /* BranchLinkProperties.h in Headers */, - 5F644BEB2B7AA811000DCD78 /* BranchSDK.h in Headers */, 5F644BEC2B7AA811000DCD78 /* BNCCurrency.h in Headers */, 5F644BED2B7AA811000DCD78 /* BranchLastAttributedTouchData.h in Headers */, 5F644BEE2B7AA811000DCD78 /* Branch.h in Headers */, @@ -1062,6 +1088,7 @@ 5F644C032B7AA811000DCD78 /* BranchContentPathProperties.h in Headers */, 5F644C042B7AA811000DCD78 /* BNCSKAdNetwork.h in Headers */, 5F644C0E2B7AA811000DCD78 /* BNCQRCodeCache.h in Headers */, + E7CA74B22E1B4890002EFB40 /* BranchConstants.h in Headers */, 5F644C2E2B7AA811000DCD78 /* BNCNetworkService.h in Headers */, 5F644C1B2B7AA811000DCD78 /* BNCSystemObserver.h in Headers */, 5F644C292B7AA811000DCD78 /* BranchJsonConfig.h in Headers */, @@ -1074,7 +1101,6 @@ 5F644C052B7AA811000DCD78 /* BNCJSONUtility.h in Headers */, 5F644C2D2B7AA811000DCD78 /* BNCReferringURLUtility.h in Headers */, 5F644C0B2B7AA811000DCD78 /* BNCEventUtils.h in Headers */, - 5F644C272B7AA811000DCD78 /* BranchConstants.h in Headers */, 5F644C002B7AA811000DCD78 /* BNCKeyChain.h in Headers */, 5F644C1D2B7AA811000DCD78 /* BranchInstallRequest.h in Headers */, 5F644C0D2B7AA811000DCD78 /* BranchShortUrlSyncRequest.h in Headers */, @@ -1097,7 +1123,6 @@ 5F644C062B7AA811000DCD78 /* UIViewController+Branch.h in Headers */, 5F644C022B7AA811000DCD78 /* BNCPasteboard.h in Headers */, 5F644C0F2B7AA811000DCD78 /* BranchOpenRequest.h in Headers */, - E7AE4A0C2DFB2D0100696805 /* BranchConfigurationController.h in Headers */, 5F644C2C2B7AA811000DCD78 /* BNCRequestFactory.h in Headers */, 5F644C142B7AA811000DCD78 /* BNCDeviceSystem.h in Headers */, ); @@ -1119,8 +1144,9 @@ dependencies = ( ); name = Branch; + packageProductDependencies = ( + ); productName = Branch; - productReference = 466B58381B17773000A69EDE /* libBranch.a */; productType = "com.apple.product-type.library.static"; }; 5F8B7B3A21B5F5CD009CE0A6 /* Branch-SDK-Unhosted-Tests */ = { @@ -1134,7 +1160,6 @@ buildRules = ( ); dependencies = ( - 5F8B7B4221B5F5CD009CE0A6 /* PBXTargetDependency */, ); name = "Branch-SDK-Unhosted-Tests"; productName = "Branch-SDK-Unhosted-Tests"; @@ -1153,9 +1178,11 @@ buildRules = ( ); dependencies = ( - 466B58701B1777DE00A69EDE /* PBXTargetDependency */, ); name = "Branch-TestBed"; + packageProductDependencies = ( + B77CB6C62E8EE36300FDF144 /* BranchSDK */, + ); productName = "Branch-TestBed"; productReference = 670016601940F51400A9E103 /* Branch-TestBed.app */; productType = "com.apple.product-type.application"; @@ -1172,8 +1199,6 @@ buildRules = ( ); dependencies = ( - 4683F07C1B20AC6300A432E7 /* PBXTargetDependency */, - 4D337DDD201019B8009A5774 /* PBXTargetDependency */, ); name = "Branch-SDK-Tests"; productName = "Branch-SDK Functionality Tests"; @@ -1191,18 +1216,11 @@ buildRules = ( ); dependencies = ( - E7AC74662DB064BC002D8C40 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( E7AC74612DB064BC002D8C40 /* Reflection_ODM_Tests */, ); name = Reflection_ODM_Tests; - packageProductDependencies = ( - E7AC746F2DB06992002D8C40 /* GULLogger */, - E7AC74712DB06992002D8C40 /* GULNetwork */, - E7AC74742DB069B2002D8C40 /* nanopb */, - E7AC747A2DB0700D002D8C40 /* BranchSDK */, - ); productName = Reflection_ODM_Tests; productReference = E7AC74602DB064BC002D8C40 /* Reflection_ODM_Tests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; @@ -1218,7 +1236,6 @@ buildRules = ( ); dependencies = ( - F1D4F9B21F323F01002D13FF /* PBXTargetDependency */, ); name = "Branch-TestBed-UITests"; productName = "Branch-TestBedUITests"; @@ -1238,6 +1255,7 @@ TargetAttributes = { 466B58371B17773000A69EDE = { CreatedOnToolsVersion = 6.3.2; + LastSwiftMigration = 1640; }; 5F8B7B3A21B5F5CD009CE0A6 = { CreatedOnToolsVersion = 10.1; @@ -1283,9 +1301,7 @@ ); mainGroup = 670016571940F51400A9E103; packageReferences = ( - E7AC746E2DB06992002D8C40 /* XCRemoteSwiftPackageReference "GoogleUtilities" */, - E7AC74732DB069B2002D8C40 /* XCRemoteSwiftPackageReference "nanopb" */, - E7AC74792DB0700D002D8C40 /* XCLocalSwiftPackageReference "../../ios-branch-deep-linking-attribution" */, + B77CB6C52E8EE36300FDF144 /* XCLocalSwiftPackageReference "../../ios-branch-deep-linking-attribution" */, ); productRefGroup = 670016611940F51400A9E103 /* Products */; projectDirPath = ""; @@ -1374,79 +1390,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E7FC47732DFC7B020072B3ED /* BranchConfigurationController.m in Sources */, - 5F644BB92B7AA811000DCD78 /* NSError+Branch.m in Sources */, - 5F644C482B7AA811000DCD78 /* BNCCallbackMap.m in Sources */, - 5F644BBE2B7AA811000DCD78 /* BNCApplication.m in Sources */, - 5F644C302B7AA811000DCD78 /* BranchDelegate.m in Sources */, - 5F5FDA102B7DE20800F14A43 /* BranchLogger.m in Sources */, - 5F644C3D2B7AA811000DCD78 /* BNCSpotlightService.m in Sources */, - 5F644BC02B7AA811000DCD78 /* NSMutableDictionary+Branch.m in Sources */, - 5F644BBC2B7AA811000DCD78 /* BranchUniversalObject.m in Sources */, - 5F644BE42B7AA811000DCD78 /* BNCKeyChain.m in Sources */, - 5F644BDE2B7AA811000DCD78 /* BranchJsonConfig.m in Sources */, - 5F644BE02B7AA811000DCD78 /* BNCPartnerParameters.m in Sources */, - 5F644BDF2B7AA811000DCD78 /* BranchLATDRequest.m in Sources */, - 5F644C382B7AA811000DCD78 /* BNCPasteboard.m in Sources */, - 5F644BE22B7AA811000DCD78 /* BranchActivityItemProvider.m in Sources */, - E7AC74592DB063C6002D8C40 /* BNCODMInfoCollector.m in Sources */, - 5F644BE12B7AA811000DCD78 /* BranchCSSearchableItemAttributeSet.m in Sources */, - 5F644BD92B7AA811000DCD78 /* BNCRequestFactory.m in Sources */, - 5F644BC32B7AA811000DCD78 /* BNCDeepLinkViewControllerInstance.m in Sources */, - 5F644C3C2B7AA811000DCD78 /* BNCLinkCache.m in Sources */, - 5F644C332B7AA811000DCD78 /* BNCJSONUtility.m in Sources */, - 5F644BDB2B7AA811000DCD78 /* BNCProductCategory.m in Sources */, - 5F644C322B7AA811000DCD78 /* Branch.m in Sources */, - 5F644BC72B7AA811000DCD78 /* BranchInstallRequest.m in Sources */, - 5F644BC62B7AA811000DCD78 /* Branch+Validator.m in Sources */, - 5F644BBA2B7AA811000DCD78 /* BNCUserAgentCollector.m in Sources */, - 5F644C362B7AA811000DCD78 /* BNCSKAdNetwork.m in Sources */, - 5F644BD02B7AA811000DCD78 /* BNCConfig.m in Sources */, - 5F644BD52B7AA811000DCD78 /* BranchContentDiscoverer.m in Sources */, - 5F644BBF2B7AA811000DCD78 /* BNCDeviceSystem.m in Sources */, - 5F644C412B7AA811000DCD78 /* BNCAppGroupsData.m in Sources */, - 5F644BBD2B7AA811000DCD78 /* BranchPasteControl.m in Sources */, - 5F644C492B7AA811000DCD78 /* BNCEventUtils.m in Sources */, - 5F644BD32B7AA811000DCD78 /* BranchSpotlightUrlRequest.m in Sources */, - 5F644BE32B7AA811000DCD78 /* BranchQRCode.m in Sources */, - 5F644BCA2B7AA811000DCD78 /* BNCSystemObserver.m in Sources */, - 5F644BC82B7AA811000DCD78 /* BranchPluginSupport.m in Sources */, - 5F644C462B7AA811000DCD78 /* BNCQRCodeCache.m in Sources */, - 5F644BD82B7AA811000DCD78 /* BNCReachability.m in Sources */, - 5F644BC92B7AA811000DCD78 /* NSString+Branch.m in Sources */, - 5F644C442B7AA811000DCD78 /* BranchShortUrlRequest.m in Sources */, - 5F644BDD2B7AA811000DCD78 /* BNCServerInterface.m in Sources */, - 5F644BC42B7AA811000DCD78 /* BranchScene.m in Sources */, - 5F644BBB2B7AA811000DCD78 /* BNCEncodingUtils.m in Sources */, - 5F644BD62B7AA811000DCD78 /* BNCDeviceInfo.m in Sources */, - 5F644BC22B7AA811000DCD78 /* BNCUrlQueryParameter.m in Sources */, - 5F644C3E2B7AA811000DCD78 /* BNCServerAPI.m in Sources */, - 5F644BD12B7AA811000DCD78 /* BranchConstants.m in Sources */, - 5F644C342B7AA811000DCD78 /* BNCCurrency.m in Sources */, - 5F644C472B7AA811000DCD78 /* BranchShortUrlSyncRequest.m in Sources */, - 5F644BC52B7AA811000DCD78 /* BNCContentDiscoveryManager.m in Sources */, - 5F644BCC2B7AA811000DCD78 /* BNCURLFilter.m in Sources */, - 5F644C352B7AA811000DCD78 /* BranchLastAttributedTouchData.m in Sources */, - 5F644BCE2B7AA811000DCD78 /* BNCNetworkInterface.m in Sources */, - 5F644BD72B7AA811000DCD78 /* BNCNetworkService.m in Sources */, - 5F644C402B7AA811000DCD78 /* UIViewController+Branch.m in Sources */, - 5F644BC12B7AA811000DCD78 /* BNCServerRequestQueue.m in Sources */, - E56394312CC7AC9F00E18E65 /* BranchFileLogger.m in Sources */, - 5F644C452B7AA811000DCD78 /* BranchOpenRequest.m in Sources */, - 5F644C312B7AA811000DCD78 /* BNCLinkData.m in Sources */, - E7E28ECA2DD2424C00F75D0D /* BNCInAppBrowser.m in Sources */, - 5F644BD22B7AA811000DCD78 /* BranchContentDiscoveryManifest.m in Sources */, - 5F644BDC2B7AA811000DCD78 /* BNCCrashlyticsWrapper.m in Sources */, - 5F644C3F2B7AA811000DCD78 /* BranchShareLink.m in Sources */, - 5F644BCD2B7AA811000DCD78 /* BNCServerRequest.m in Sources */, - 5F644C3A2B7AA811000DCD78 /* BranchLinkProperties.m in Sources */, - 5F644C432B7AA811000DCD78 /* BranchEvent.m in Sources */, - 5F644C392B7AA811000DCD78 /* BNCAppleReceipt.m in Sources */, - 5F644BDA2B7AA811000DCD78 /* BNCReferringURLUtility.m in Sources */, - 5F644C422B7AA811000DCD78 /* BNCServerResponse.m in Sources */, - 5F644C3B2B7AA811000DCD78 /* BNCInitSessionResponse.m in Sources */, - 5F644BCF2B7AA811000DCD78 /* BNCPreferenceHelper.m in Sources */, - 5F644C372B7AA811000DCD78 /* BranchContentPathProperties.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1546,39 +1489,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 466B58701B1777DE00A69EDE /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 466B58371B17773000A69EDE /* Branch */; - targetProxy = 466B586F1B1777DE00A69EDE /* PBXContainerItemProxy */; - }; - 4683F07C1B20AC6300A432E7 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 466B58371B17773000A69EDE /* Branch */; - targetProxy = 4683F07B1B20AC6300A432E7 /* PBXContainerItemProxy */; - }; - 4D337DDD201019B8009A5774 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 6700165F1940F51400A9E103 /* Branch-TestBed */; - targetProxy = 4D337DDC201019B8009A5774 /* PBXContainerItemProxy */; - }; - 5F8B7B4221B5F5CD009CE0A6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 466B58371B17773000A69EDE /* Branch */; - targetProxy = 5F8B7B4121B5F5CD009CE0A6 /* PBXContainerItemProxy */; - }; - E7AC74662DB064BC002D8C40 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 466B58371B17773000A69EDE /* Branch */; - targetProxy = E7AC74652DB064BC002D8C40 /* PBXContainerItemProxy */; - }; - F1D4F9B21F323F01002D13FF /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 6700165F1940F51400A9E103 /* Branch-TestBed */; - targetProxy = F1D4F9B11F323F01002D13FF /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 6700166C1940F51400A9E103 /* InfoPlist.strings */ = { isa = PBXVariantGroup; @@ -1603,6 +1513,9 @@ "DEBUG=1", "$(inherited)", ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ); IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; OTHER_LDFLAGS = "-ObjC"; @@ -1610,6 +1523,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PUBLIC_HEADERS_FOLDER_PATH = /headers; SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1622,6 +1536,9 @@ DEFINES_MODULE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ); IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = "-ObjC"; @@ -1629,6 +1546,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PUBLIC_HEADERS_FOLDER_PATH = /headers; SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -2072,49 +1990,16 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - E7AC74792DB0700D002D8C40 /* XCLocalSwiftPackageReference "../../ios-branch-deep-linking-attribution" */ = { + B77CB6C52E8EE36300FDF144 /* XCLocalSwiftPackageReference "../../ios-branch-deep-linking-attribution" */ = { isa = XCLocalSwiftPackageReference; relativePath = "../../ios-branch-deep-linking-attribution"; }; /* End XCLocalSwiftPackageReference section */ -/* Begin XCRemoteSwiftPackageReference section */ - E7AC746E2DB06992002D8C40 /* XCRemoteSwiftPackageReference "GoogleUtilities" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/google/GoogleUtilities"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 8.0.2; - }; - }; - E7AC74732DB069B2002D8C40 /* XCRemoteSwiftPackageReference "nanopb" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/nanopb/nanopb"; - requirement = { - kind = revision; - revision = b7e1104502eca3a213b46303391ca4d3bc8ddec1; - }; - }; -/* 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 */ = { - isa = XCSwiftPackageProductDependency; - package = E7AC74732DB069B2002D8C40 /* XCRemoteSwiftPackageReference "nanopb" */; - productName = nanopb; - }; - E7AC747A2DB0700D002D8C40 /* BranchSDK */ = { + B77CB6C62E8EE36300FDF144 /* BranchSDK */ = { isa = XCSwiftPackageProductDependency; + package = B77CB6C52E8EE36300FDF144 /* XCLocalSwiftPackageReference "../" */; productName = BranchSDK; }; /* End XCSwiftPackageProductDependency section */ diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Tests.xcscheme b/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Tests.xcscheme index 1f130cd47..461595a04 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Tests.xcscheme +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Tests.xcscheme @@ -1,6 +1,6 @@ + + + + diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Unhosted-Tests.xcscheme b/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Unhosted-Tests.xcscheme index 40a19b5ba..8cd35e0b8 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Unhosted-Tests.xcscheme +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Branch-SDK-Unhosted-Tests.xcscheme @@ -1,6 +1,6 @@ @@ -34,7 +34,7 @@ @@ -83,7 +83,7 @@ @@ -99,7 +99,7 @@ diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Reflection_ODM_Tests.xcscheme b/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Reflection_ODM_Tests.xcscheme index 84c83c2f5..ea6854043 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Reflection_ODM_Tests.xcscheme +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/xcshareddata/xcschemes/Reflection_ODM_Tests.xcscheme @@ -1,6 +1,6 @@ @end diff --git a/Branch-TestBed/Branch-TestBed/PasteControlViewController.m b/Branch-TestBed/Branch-TestBed/PasteControlViewController.m index aa702c70b..4349ac9e1 100644 --- a/Branch-TestBed/Branch-TestBed/PasteControlViewController.m +++ b/Branch-TestBed/Branch-TestBed/PasteControlViewController.m @@ -7,9 +7,7 @@ // #import "PasteControlViewController.h" -#import "Branch.h" -#import "BranchOpenRequest.h" -#import "BranchPasteControl.h" +@import BranchSDK; #import "LogOutputViewController.h" #import "AppDelegate.h" diff --git a/Branch-TestBed/Branch-TestBed/ViewController.m b/Branch-TestBed/Branch-TestBed/ViewController.m index 994b689d4..b094206df 100644 --- a/Branch-TestBed/Branch-TestBed/ViewController.m +++ b/Branch-TestBed/Branch-TestBed/ViewController.m @@ -6,16 +6,11 @@ // Copyright (c) 2014 Branch Metrics. All rights reserved. // -#import "Branch.h" -#import "BranchEvent.h" -#import "BranchQRCode.h" -#import "BranchConstants.h" -#import "BNCConfig.h" +@import BranchSDK; +#import "BNCConfig.h" // For BNC_SDK_VERSION #import "ViewController.h" #import "LogOutputViewController.h" #import "ArrayPickerView.h" -#import "BranchUniversalObject.h" -#import "BranchLinkProperties.h" #import "LogOutputViewController.h" #import "AppDelegate.h" #import diff --git a/BranchSDK.xcodeproj/project.pbxproj b/BranchSDK.xcodeproj/project.pbxproj index 74de0d1d8..039a3aa35 100644 --- a/BranchSDK.xcodeproj/project.pbxproj +++ b/BranchSDK.xcodeproj/project.pbxproj @@ -510,7 +510,8 @@ E7F311B12DACB54100F824A7 /* BNCODMInfoCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = E7F311B02DACB54100F824A7 /* BNCODMInfoCollector.h */; }; E7F311B22DACB54100F824A7 /* BNCODMInfoCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = E7F311B02DACB54100F824A7 /* BNCODMInfoCollector.h */; }; E7F311B32DACB54100F824A7 /* BNCODMInfoCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = E7F311B02DACB54100F824A7 /* BNCODMInfoCollector.h */; }; -/* End PBXBuildFile section */ + 06A04628F6DB4F44A9BB2AE2 /* BNCServerRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D8DF63E386294A42819E6992 /* BNCServerRequestOperation.m */; }; + /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 5F2211852894A9C100C5B190 /* PBXContainerItemProxy */ = { @@ -727,7 +728,9 @@ E73D02802DEE8AE90076C3F1 /* BranchConfigurationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BranchConfigurationController.m; sourceTree = ""; }; E7F311AD2DACB4D400F824A7 /* BNCODMInfoCollector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCODMInfoCollector.m; sourceTree = ""; }; E7F311B02DACB54100F824A7 /* BNCODMInfoCollector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCODMInfoCollector.h; sourceTree = ""; }; -/* End PBXFileReference section */ + D8DF63E386294A42819E6992 /* BNCServerRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCServerRequestOperation.m; sourceTree = ""; }; + 61FEEBBCF8A744929B7C96A1 /* BNCServerRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCServerRequestOperation.h; sourceTree = ""; }; + /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 5F22101A2894A0DB00C5B190 /* Frameworks */ = { @@ -874,6 +877,7 @@ 5FCDD3902B7AC6A100EAF29F /* BNCServerInterface.m */, 5FCDD3802B7AC6A100EAF29F /* BNCServerRequest.m */, 5FCDD3742B7AC6A100EAF29F /* BNCServerRequestQueue.m */, + D8DF63E386294A42819E6992 /* BNCServerRequestOperation.m */, 5FCDD3F72B7AC6A100EAF29F /* BNCServerResponse.m */, 5FCDD3EB2B7AC6A100EAF29F /* BNCSKAdNetwork.m */, 5FCDD3F22B7AC6A100EAF29F /* BNCSpotlightService.m */, @@ -1007,7 +1011,8 @@ 5FCDD3BB2B7AC6A100EAF29F /* UIViewController+Branch.h */, E71E396D2DD3A92900110F59 /* BNCInAppBrowser.h */, E73D027F2DEE8AE90076C3F1 /* BranchConfigurationController.h */, - ); + + 61FEEBBCF8A744929B7C96A1 /* BNCServerRequestOperation.h */,); path = Private; sourceTree = ""; }; @@ -1675,7 +1680,8 @@ 5FCDD5852B7AC6A400EAF29F /* BNCInitSessionResponse.m in Sources */, 5FCDD4412B7AC6A100EAF29F /* BNCPreferenceHelper.m in Sources */, 5FCDD5792B7AC6A400EAF29F /* BranchContentPathProperties.m in Sources */, - ); + + 06A04628F6DB4F44A9BB2AE2 /* BNCServerRequestOperation.m in Sources */,); runOnlyForDeploymentPostprocessing = 0; }; 5F22116B2894A9C000C5B190 /* Sources */ = { @@ -1686,7 +1692,8 @@ 5F6DD24C2894AF5E00AE9FB0 /* NSURLSession+Branch.m in Sources */, 5F2211722894A9C000C5B190 /* AppDelegate.swift in Sources */, 5F2211742894A9C000C5B190 /* SceneDelegate.swift in Sources */, - ); + + 06A04628F6DB4F44A9BB2AE2 /* BNCServerRequestOperation.m in Sources */,); runOnlyForDeploymentPostprocessing = 0; }; 5F2211802894A9C100C5B190 /* Sources */ = { @@ -1694,7 +1701,8 @@ buildActionMask = 2147483647; files = ( 5F2211892894A9C100C5B190 /* TestHostTests.swift in Sources */, - ); + + 06A04628F6DB4F44A9BB2AE2 /* BNCServerRequestOperation.m in Sources */,); runOnlyForDeploymentPostprocessing = 0; }; 5F22118A2894A9C100C5B190 /* Sources */ = { @@ -1703,7 +1711,8 @@ files = ( 5F2211952894A9C100C5B190 /* TestHostUITestsLaunchTests.swift in Sources */, 5F2211932894A9C100C5B190 /* TestHostUITests.swift in Sources */, - ); + + 06A04628F6DB4F44A9BB2AE2 /* BNCServerRequestOperation.m in Sources */,); runOnlyForDeploymentPostprocessing = 0; }; 5F73EBF028ECE65400608601 /* Sources */ = { @@ -1783,7 +1792,8 @@ 5FCDD5862B7AC6A400EAF29F /* BNCInitSessionResponse.m in Sources */, 5FCDD4422B7AC6A100EAF29F /* BNCPreferenceHelper.m in Sources */, 5FCDD57A2B7AC6A400EAF29F /* BranchContentPathProperties.m in Sources */, - ); + + 06A04628F6DB4F44A9BB2AE2 /* BNCServerRequestOperation.m in Sources */,); runOnlyForDeploymentPostprocessing = 0; }; 5F79038828B5765D003144CD /* Sources */ = { @@ -1853,7 +1863,8 @@ 5FCDD5872B7AC6A400EAF29F /* BNCInitSessionResponse.m in Sources */, 5FCDD4432B7AC6A100EAF29F /* BNCPreferenceHelper.m in Sources */, 5FCDD57B2B7AC6A400EAF29F /* BranchContentPathProperties.m in Sources */, - ); + + 06A04628F6DB4F44A9BB2AE2 /* BNCServerRequestOperation.m in Sources */,); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ diff --git a/BranchSDKTests/BNCAPIServerTest.m b/BranchSDKTests/BNCAPIServerTest.m new file mode 100644 index 000000000..eb9165001 --- /dev/null +++ b/BranchSDKTests/BNCAPIServerTest.m @@ -0,0 +1,508 @@ +// +// BNCAPIServerTest.m +// Branch-SDK-Tests +// +// Created by Nidhi Dixit on 9/6/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import "BNCServerAPI.h" +#import "BNCSystemObserver.h" +#import "BNCConfig.h" +#import "BranchConstants.h" +#import "Branch.h" + +@interface BNCAPIServerTest : XCTestCase + +@end + +@implementation BNCAPIServerTest + +- (void)testInstallServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI installServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/install"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testOpenServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI openServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/open"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testStandardEventServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI standardEventServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v2/event/standard"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testCustomEventServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI customEventServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v2/event/custom"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLinkServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI linkServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/url"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testQRCodeServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI qrcodeServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/qr-code"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLATDServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI latdServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/cpid/latd"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testValidationServiceURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + + NSString *url = [serverAPI validationServiceURL]; + NSString *expectedUrlPrefix= @"https://api3.branch.io/v1/app-link-settings"; + + XCTAssertTrue([url hasPrefix:expectedUrlPrefix]); +} + +- (void)testInstallServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI installServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack.branch.io/v1/install"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testOpenServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI openServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack.branch.io/v1/open"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testStandardEventServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI standardEventServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack.branch.io/v2/event/standard"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testCustomEventServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI customEventServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack.branch.io/v2/event/custom"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLinkServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI linkServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/url"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testQRCodeServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI qrcodeServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/qr-code"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLATDServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI latdServiceURL]; + NSString *expectedUrlStr = @"https://api3.branch.io/v1/cpid/latd"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testValidationServiceURL_Tracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI validationServiceURL]; + NSString *expectedUrlPrefix= @"https://api3.branch.io/v1/app-link-settings"; + + XCTAssertTrue([url hasPrefix:expectedUrlPrefix]); +} + +- (void)testInstallServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI installServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/install"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testOpenServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI openServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/open"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testStandardEventServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI standardEventServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v2/event/standard"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testCustomEventServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI customEventServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v2/event/custom"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLinkServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI linkServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/url"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testQRCodeServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI qrcodeServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/qr-code"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLATDServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI latdServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/cpid/latd"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testValidationServiceURL_EU { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + + NSString *url = [serverAPI validationServiceURL]; + NSString *expectedUrlPrefix= @"https://api3-eu.branch.io/v1/app-link-settings"; + + XCTAssertTrue([url hasPrefix:expectedUrlPrefix]); +} + +- (void)testInstallServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI installServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack-eu.branch.io/v1/install"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testOpenServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI openServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack-eu.branch.io/v1/open"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testStandardEventServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI standardEventServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack-eu.branch.io/v2/event/standard"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testCustomEventServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI customEventServiceURL]; + NSString *expectedUrlStr = @"https://api-safetrack-eu.branch.io/v2/event/custom"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLinkServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI linkServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/url"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testQRCodeServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI qrcodeServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/qr-code"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testLATDServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI latdServiceURL]; + NSString *expectedUrlStr = @"https://api3-eu.branch.io/v1/cpid/latd"; + + XCTAssertTrue([url isEqualToString:expectedUrlStr]); +} + +- (void)testValidationServiceURL_EUTracking { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useEUServers = YES; + serverAPI.useTrackingDomain = YES; + + NSString *url = [serverAPI validationServiceURL]; + NSString *expectedUrlPrefix= @"https://api3-eu.branch.io/v1/app-link-settings"; + + XCTAssertTrue([url hasPrefix:expectedUrlPrefix]); +} + +- (void)testDefaultAPIURL { + BNCServerAPI *serverAPI = [BNCServerAPI new]; + XCTAssertNil(serverAPI.customAPIURL); + + NSString *storedUrl = [[BNCServerAPI sharedInstance] installServiceURL]; + NSString *expectedUrl = [BNC_API_URL stringByAppendingString: @"/v1/install"]; + XCTAssertEqualObjects(storedUrl, expectedUrl); +} + +- (void)testSetAPIURL_Example { + NSString *url = @"https://www.example.com"; + [Branch setAPIUrl:url]; + + NSString *storedUrl = [[BNCServerAPI sharedInstance] installServiceURL]; + NSString *expectedUrl = [url stringByAppendingString: @"/v1/install"]; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + [Branch setAPIUrl:BNC_API_URL]; +} + +- (void)testSetAPIURL_InvalidHttp { + NSString *url = @"Invalid://www.example.com"; + [Branch setAPIUrl:url]; + + NSString *storedUrl = [[BNCServerAPI sharedInstance] installServiceURL]; + NSString *expectedUrl = [BNC_API_URL stringByAppendingString: @"/v1/install"]; + XCTAssertEqualObjects(storedUrl, expectedUrl); +} + +- (void)testSetAPIURL_InvalidEmpty { + NSString *url = @""; + [Branch setAPIUrl:url]; + + NSString *storedUrl = [[BNCServerAPI sharedInstance] installServiceURL]; + NSString *expectedUrl = [BNC_API_URL stringByAppendingString: @"/v1/install"]; + XCTAssertEqualObjects(storedUrl, expectedUrl); +} + + +- (void)testSetSafeTrackServiceURLWithUserTrackingDomain { + NSString *url = @"https://links.toTestDomain.com"; + NSString *safeTrackUrl = @"https://links.toTestDomain-safeTrack.com"; + + [Branch setAPIUrl:url]; + [Branch setSafetrackAPIURL:safeTrackUrl]; + + BNCServerAPI *serverAPI = [BNCServerAPI sharedInstance]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = YES; + + NSString *storedUrl = [[BNCServerAPI sharedInstance] installServiceURL]; + NSString *expectedUrl = @"https://links.toTestDomain-safeTrack.com/v1/install"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] openServiceURL]; + expectedUrl = @"https://links.toTestDomain-safeTrack.com/v1/open"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] standardEventServiceURL]; + expectedUrl = @"https://links.toTestDomain-safeTrack.com/v2/event/standard"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] customEventServiceURL]; + expectedUrl = @"https://links.toTestDomain-safeTrack.com/v2/event/custom"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] linkServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v1/url"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] qrcodeServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v1/qr-code"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] latdServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v1/cpid/latd"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + + [BNCServerAPI sharedInstance].useTrackingDomain = NO; + [BNCServerAPI sharedInstance].useEUServers = NO; + [BNCServerAPI sharedInstance].automaticallyEnableTrackingDomain = YES; + [BNCServerAPI sharedInstance].customAPIURL = nil; + [BNCServerAPI sharedInstance].customSafeTrackAPIURL = nil; + +} + +- (void)testSetSafeTrackServiceURLWithOutUserTrackingDomain { + NSString *url = @"https://links.toTestDomain.com"; + NSString *safeTrackUrl = @"https://links.toTestDomain-safeTrack.com"; + + [Branch setAPIUrl:url]; + [Branch setSafetrackAPIURL:safeTrackUrl]; + + BNCServerAPI *serverAPI = [BNCServerAPI sharedInstance]; + serverAPI.automaticallyEnableTrackingDomain = NO; + serverAPI.useTrackingDomain = NO; + + NSString *storedUrl = [[BNCServerAPI sharedInstance] installServiceURL]; + NSString *expectedUrl = @"https://links.toTestDomain.com/v1/install"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] openServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v1/open"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] standardEventServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v2/event/standard"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] customEventServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v2/event/custom"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] linkServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v1/url"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] qrcodeServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v1/qr-code"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + storedUrl = [[BNCServerAPI sharedInstance] latdServiceURL]; + expectedUrl = @"https://links.toTestDomain.com/v1/cpid/latd"; + XCTAssertEqualObjects(storedUrl, expectedUrl); + + [BNCServerAPI sharedInstance].useTrackingDomain = NO; + [BNCServerAPI sharedInstance].useEUServers = NO; + [BNCServerAPI sharedInstance].automaticallyEnableTrackingDomain = YES; + [BNCServerAPI sharedInstance].customAPIURL = nil; + [BNCServerAPI sharedInstance].customSafeTrackAPIURL = nil; + +} + +@end diff --git a/BranchSDKTests/BNCAppleReceiptTests.m b/BranchSDKTests/BNCAppleReceiptTests.m new file mode 100644 index 000000000..faff0ef96 --- /dev/null +++ b/BranchSDKTests/BNCAppleReceiptTests.m @@ -0,0 +1,33 @@ +// +// BNCAppleReceiptTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 7/15/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +#import "BNCAppleReceipt.h" + +@interface BNCAppleReceiptTests : XCTestCase + +@end + +@implementation BNCAppleReceiptTests + +- (void)setUp { + +} + +- (void)tearDown { + +} + +- (void)testReceiptOnSimulator { + BNCAppleReceipt *receipt = [[BNCAppleReceipt alloc] init]; + // Appears the simulator can have a receipt + //XCTAssertNil([receipt installReceipt]); + XCTAssertFalse([receipt isTestFlight]); +} + +@end diff --git a/BranchSDKTests/BNCApplicationTests.m b/BranchSDKTests/BNCApplicationTests.m new file mode 100644 index 000000000..147a2a665 --- /dev/null +++ b/BranchSDKTests/BNCApplicationTests.m @@ -0,0 +1,75 @@ +// +// BNCApplication.Test.m +// Branch-SDK-Tests +// +// Created by Edward on 1/10/18. +// Copyright © 2018 Branch, Inc. All rights reserved. +// + +#import +#import "BNCApplication.h" +#import "BNCKeyChain.h" + +@interface BNCApplicationTests : XCTestCase +@end + +@implementation BNCApplicationTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testApplication { + // Test general info: + + if ([UIApplication sharedApplication] == nil) { + NSLog(@"No host application for BNCApplication testing!"); + return; + } + + BNCApplication *application = [BNCApplication currentApplication]; + XCTAssertEqualObjects(application.bundleID, @"branch.BranchSDKTestsHostApp"); + XCTAssertEqualObjects(application.displayName, @"BranchSDKTestsHostApp"); + XCTAssertEqualObjects(application.shortDisplayName, @"BranchSDKTestsHostApp"); + XCTAssertEqualObjects(application.displayVersionString, @"1.0"); + XCTAssertEqualObjects(application.versionString, @"1"); +} + +- (void) testAppDates { + // App dates. Not a great test but tests basic function: + + if ([UIApplication sharedApplication] == nil) { + NSLog(@"No host application for BNCApplication testing!"); + return; + } + + NSTimeInterval const kOneYearAgo = -365.0 * 24.0 * 60.0 * 60.0; + + BNCApplication *application = [BNCApplication currentApplication]; + XCTAssertTrue(application.firstInstallDate && [application.firstInstallDate timeIntervalSinceNow] > kOneYearAgo); + XCTAssertTrue(application.firstInstallBuildDate && [application.firstInstallBuildDate timeIntervalSinceNow] > kOneYearAgo); + XCTAssertTrue(application.currentInstallDate && [application.currentInstallDate timeIntervalSinceNow] > kOneYearAgo); + XCTAssertTrue(application.currentBuildDate && [application.currentBuildDate timeIntervalSinceNow] > kOneYearAgo); + + NSString*const kBranchKeychainService = @"BranchKeychainService"; + NSString*const kBranchKeychainFirstBuildKey = @"BranchKeychainFirstBuild"; + NSString*const kBranchKeychainFirstInstalldKey = @"BranchKeychainFirstInstall"; + + NSDate * firstBuildDate = + [BNCKeyChain retrieveDateForService:kBranchKeychainService + key:kBranchKeychainFirstBuildKey + error:nil]; + XCTAssertEqualObjects(application.firstInstallBuildDate, firstBuildDate); + + NSDate * firstInstallDate = + [BNCKeyChain retrieveDateForService:kBranchKeychainService + key:kBranchKeychainFirstInstalldKey + error:nil]; + XCTAssertEqualObjects(application.firstInstallDate, firstInstallDate); +} + +@end diff --git a/BranchSDKTests/BNCCallbackMapTests.m b/BranchSDKTests/BNCCallbackMapTests.m new file mode 100644 index 000000000..4d5482499 --- /dev/null +++ b/BranchSDKTests/BNCCallbackMapTests.m @@ -0,0 +1,134 @@ +// +// BNCCallbackMapTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 2/25/20. +// Copyright © 2020 Branch, Inc. All rights reserved. +// + +#import +#import "BNCCallbackMap.h" +#import "NSError+Branch.h" + +// expose private storage object for state checks +@interface BNCCallbackMap() +@property (nonatomic, strong, readwrite) NSMapTable *callbacks; +@end + +@interface BNCCallbackMapTests : XCTestCase + +@end + +@implementation BNCCallbackMapTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testRequestSaveAndCallback { + BNCCallbackMap *map = [BNCCallbackMap new]; + + __block BOOL successResult = NO; + __block NSError *errorResult = nil; + + // store a request and callback block + BNCServerRequest *request = [BNCServerRequest new]; + [map storeRequest:request withCompletion:^(BOOL success, NSError * _Nullable error) { + successResult = success; + errorResult = error; + }]; + + // confirm there's one entry + XCTAssert([map containsRequest:request] != NO); + XCTAssert(map.callbacks.count == 1); + + // call callback + [map callCompletionForRequest:request withSuccessStatus:YES error:nil]; + + // check if variable was updated + XCTAssertTrue(successResult); + XCTAssertNil(errorResult); +} + +- (void)testRequestSaveAndCallbackWithError { + BNCCallbackMap *map = [BNCCallbackMap new]; + + __block BOOL successResult = YES; + __block NSError *errorResult = nil; + + // store a request and callback block + BNCServerRequest *request = [BNCServerRequest new]; + [map storeRequest:request withCompletion:^(BOOL success, NSError * _Nullable error) { + successResult = success; + errorResult = error; + }]; + + // confirm there's one entry + XCTAssert([map containsRequest:request] != NO); + XCTAssert(map.callbacks.count == 1); + + // call callback + [map callCompletionForRequest:request withSuccessStatus:NO error:[NSError branchErrorWithCode:BNCGeneralError localizedMessage:@"Test Error"]]; + + // check if variable was updated + XCTAssertFalse(successResult); + XCTAssert([@"Test Error" isEqualToString:errorResult.localizedFailureReason]); +} + +- (void)testAttemptCallbackWithUnsavedRequest { + BNCCallbackMap *map = [BNCCallbackMap new]; + + __block BOOL successResult = NO; + __block NSError *errorResult = nil; + + // store a request and callback block + BNCServerRequest *request = [BNCServerRequest new]; + [map storeRequest:request withCompletion:^(BOOL success, NSError * _Nullable error) { + successResult = success; + errorResult = error; + }]; + + // confirm there's one entry + XCTAssert([map containsRequest:request] != NO); + XCTAssert(map.callbacks.count == 1); + + // confirm a new request results in no callback + request = [BNCServerRequest new]; + XCTAssert([map containsRequest:request] == NO); + [map callCompletionForRequest:request withSuccessStatus:YES error:nil]; + + // check if variable was updated + XCTAssertFalse(successResult); + XCTAssertNil(errorResult); +} + +- (void)testRequestsGetReleasedAutomatically { + BNCCallbackMap *map = [BNCCallbackMap new]; + + __block int count = 0; + + BNCServerRequest *request = [BNCServerRequest new]; + [map storeRequest:request withCompletion:^(BOOL success, NSError * _Nullable error) { + count++; + }]; + + for (int i=0; i<100; i++) { + BNCServerRequest *tmp = [BNCServerRequest new]; + [map storeRequest:tmp withCompletion:^(BOOL success, NSError * _Nullable error) { + count++; + }]; + } + + // confirm there's less than 100 entries. By not retaining the tmp request, they should be getting ARC'd + XCTAssert(map.callbacks.count < 100); + + // confirm the one request we held does get a callback + [map callCompletionForRequest:request withSuccessStatus:YES error:nil]; + XCTAssert(count == 1); +} + +@end diff --git a/BranchSDKTests/BNCClassSerializationTests.m b/BranchSDKTests/BNCClassSerializationTests.m new file mode 100644 index 000000000..f1af287d9 --- /dev/null +++ b/BranchSDKTests/BNCClassSerializationTests.m @@ -0,0 +1,120 @@ +// +// BNCClassSerializationTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 3/28/24. +// Copyright © 2024 Branch, Inc. All rights reserved. +// + +#import +#import "BranchEvent.h" +#import "BranchOpenRequest.h" +#import "BranchInstallRequest.h" + +@interface BranchEvent() +// private BranchEvent methods used to build a BranchEventRequest +- (NSDictionary *)buildEventDictionary; +@end + +@interface BranchOpenRequest() +- (NSString *)getActionName; +@end + +@interface BNCClassSerializationTests : XCTestCase + +@end + +// Test serialization of replayable requests +@implementation BNCClassSerializationTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +// BranchEventRequest is creation is tightly coupled with the BranchEvent class +// In order to test building it, we need to expose some private methods. :( +- (BranchEventRequest *)buildBranchEventRequest { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventPurchase]; + NSURL *url = [NSURL URLWithString:@"https://api3.branch.io/v2/event/standard"]; + NSDictionary *eventDictionary = [event buildEventDictionary]; + + BranchEventRequest *request = [[BranchEventRequest alloc] initWithServerURL:url eventDictionary:eventDictionary completion:nil]; + return request; +} + +- (void)testBranchEventRequestArchive { + BranchEventRequest *request = [self buildBranchEventRequest]; + + // archive the event + NSError *error = nil; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request requiringSecureCoding:YES error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(data); + + // unarchive the event + id object = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithArray:@[BranchEventRequest.class]] fromData:data error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(object); + + // check object + XCTAssertTrue([object isKindOfClass:BranchEventRequest.class]); + BranchEventRequest *unarchivedRequest = (BranchEventRequest *)object; + + XCTAssertTrue([request.serverURL.absoluteString isEqualToString:unarchivedRequest.serverURL.absoluteString]); + XCTAssertTrue(request.eventDictionary.count == unarchivedRequest.eventDictionary.count); + XCTAssertNil(unarchivedRequest.completion); +} + +- (void)testBranchOpenRequestArchive { + BranchOpenRequest *request = [[BranchOpenRequest alloc] initWithCallback:nil]; + request.urlString = @"https://branch.io"; + + // archive the event + NSError *error = nil; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request requiringSecureCoding:YES error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(data); + + // unarchive the event + id object = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithArray:@[BranchOpenRequest.class]] fromData:data error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(object); + + // check object + XCTAssertTrue([object isKindOfClass:BranchOpenRequest.class]); + BranchOpenRequest *unarchivedRequest = (BranchOpenRequest *)object; + + XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); + XCTAssertNil(unarchivedRequest.callback); + XCTAssertTrue([@"open" isEqualToString:[unarchivedRequest getActionName]]); +} + +- (void)testBranchInstallRequestArchive { + BranchInstallRequest *request = [[BranchInstallRequest alloc] initWithCallback:nil]; + request.urlString = @"https://branch.io"; + + // archive the event + NSError *error = nil; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:request requiringSecureCoding:YES error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(data); + + // unarchive the event + id object = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithArray:@[BranchInstallRequest.class]] fromData:data error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(object); + + // check object + XCTAssertTrue([object isKindOfClass:BranchInstallRequest.class]); + BranchInstallRequest *unarchivedRequest = (BranchInstallRequest *)object; + + XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); + XCTAssertNil(unarchivedRequest.callback); + XCTAssertTrue([@"install" isEqualToString:[unarchivedRequest getActionName]]); +} + +@end diff --git a/BranchSDKTests/BNCCrashlyticsWrapperTests.m b/BranchSDKTests/BNCCrashlyticsWrapperTests.m new file mode 100644 index 000000000..4b9be75a4 --- /dev/null +++ b/BranchSDKTests/BNCCrashlyticsWrapperTests.m @@ -0,0 +1,68 @@ +// +// BNCCrashlyticsWrapperTest.m +// Branch-TestBed +// +// Created by Jimmy Dee on 7/18/17. +// Copyright © 2017 Branch Metrics. All rights reserved. +// + +#import +#import "BNCCrashlyticsWrapper.h" + +#pragma mark Crashlytics SDK Stand-In + +@interface FIRCrashlytics : NSObject ++ (FIRCrashlytics *)crashlytics; +@property NSMutableDictionary *values; +- (void)setCustomValue:(id)value forKey:(NSString *)key; +-(id)getCustomValueForKey:(NSString *)key; +@end + +@implementation FIRCrashlytics + ++ (FIRCrashlytics *)crashlytics { + @synchronized (self) { + static FIRCrashlytics * sharedCrashlytics = nil; + if (!sharedCrashlytics) sharedCrashlytics = [[self alloc] init]; + return sharedCrashlytics; + } +} + +- (void)setCustomValue:(id)value forKey:(NSString *)key { + if (!_values) { + _values = [[NSMutableDictionary alloc] init]; + } + [_values setObject:value forKey:key]; +} + +-(id)getCustomValueForKey:(NSString *)key { + return [_values valueForKey:key]; +} +@end + +#pragma mark - BNCCrashlyticsWrapperTest + +@interface BNCCrashlyticsWrapperTests : XCTestCase +@end + +@implementation BNCCrashlyticsWrapperTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testSetValue { + BNCCrashlyticsWrapper *wrapper = [BNCCrashlyticsWrapper wrapper]; + NSString *value = @"TestString"; + NSString *key = @"TestKey"; + + [wrapper setCustomValue:value forKey:key]; + + XCTAssertEqual([[FIRCrashlytics crashlytics] getCustomValueForKey:key], value); +} + +@end diff --git a/BranchSDKTests/BNCCurrencyTests.m b/BranchSDKTests/BNCCurrencyTests.m new file mode 100644 index 000000000..ec24da0f8 --- /dev/null +++ b/BranchSDKTests/BNCCurrencyTests.m @@ -0,0 +1,194 @@ +// +// BNCCurrencyTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 9/21/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import "BNCCurrency.h" + +@interface BNCCurrencyTests : XCTestCase +@end + +@implementation BNCCurrencyTests + +- (void)testEnumValues { + XCTAssertEqualObjects(BNCCurrencyAED, @"AED"); + XCTAssertEqualObjects(BNCCurrencyAFN, @"AFN"); + XCTAssertEqualObjects(BNCCurrencyALL, @"ALL"); + XCTAssertEqualObjects(BNCCurrencyAMD, @"AMD"); + XCTAssertEqualObjects(BNCCurrencyANG, @"ANG"); + XCTAssertEqualObjects(BNCCurrencyAOA, @"AOA"); + XCTAssertEqualObjects(BNCCurrencyARS, @"ARS"); + XCTAssertEqualObjects(BNCCurrencyAUD, @"AUD"); + XCTAssertEqualObjects(BNCCurrencyAWG, @"AWG"); + XCTAssertEqualObjects(BNCCurrencyAZN, @"AZN"); + XCTAssertEqualObjects(BNCCurrencyBAM, @"BAM"); + XCTAssertEqualObjects(BNCCurrencyBBD, @"BBD"); + XCTAssertEqualObjects(BNCCurrencyBDT, @"BDT"); + XCTAssertEqualObjects(BNCCurrencyBGN, @"BGN"); + XCTAssertEqualObjects(BNCCurrencyBHD, @"BHD"); + XCTAssertEqualObjects(BNCCurrencyBIF, @"BIF"); + XCTAssertEqualObjects(BNCCurrencyBMD, @"BMD"); + XCTAssertEqualObjects(BNCCurrencyBND, @"BND"); + XCTAssertEqualObjects(BNCCurrencyBOB, @"BOB"); + XCTAssertEqualObjects(BNCCurrencyBOV, @"BOV"); + XCTAssertEqualObjects(BNCCurrencyBRL, @"BRL"); + XCTAssertEqualObjects(BNCCurrencyBSD, @"BSD"); + XCTAssertEqualObjects(BNCCurrencyBTN, @"BTN"); + XCTAssertEqualObjects(BNCCurrencyBWP, @"BWP"); + XCTAssertEqualObjects(BNCCurrencyBYN, @"BYN"); + XCTAssertEqualObjects(BNCCurrencyBYR, @"BYR"); + XCTAssertEqualObjects(BNCCurrencyBZD, @"BZD"); + XCTAssertEqualObjects(BNCCurrencyCAD, @"CAD"); + XCTAssertEqualObjects(BNCCurrencyCDF, @"CDF"); + XCTAssertEqualObjects(BNCCurrencyCHE, @"CHE"); + XCTAssertEqualObjects(BNCCurrencyCHF, @"CHF"); + XCTAssertEqualObjects(BNCCurrencyCHW, @"CHW"); + XCTAssertEqualObjects(BNCCurrencyCLF, @"CLF"); + XCTAssertEqualObjects(BNCCurrencyCLP, @"CLP"); + XCTAssertEqualObjects(BNCCurrencyCNY, @"CNY"); + XCTAssertEqualObjects(BNCCurrencyCOP, @"COP"); + XCTAssertEqualObjects(BNCCurrencyCOU, @"COU"); + XCTAssertEqualObjects(BNCCurrencyCRC, @"CRC"); + XCTAssertEqualObjects(BNCCurrencyCUC, @"CUC"); + XCTAssertEqualObjects(BNCCurrencyCUP, @"CUP"); + XCTAssertEqualObjects(BNCCurrencyCVE, @"CVE"); + XCTAssertEqualObjects(BNCCurrencyCZK, @"CZK"); + XCTAssertEqualObjects(BNCCurrencyDJF, @"DJF"); + XCTAssertEqualObjects(BNCCurrencyDKK, @"DKK"); + XCTAssertEqualObjects(BNCCurrencyDOP, @"DOP"); + XCTAssertEqualObjects(BNCCurrencyDZD, @"DZD"); + XCTAssertEqualObjects(BNCCurrencyEGP, @"EGP"); + XCTAssertEqualObjects(BNCCurrencyERN, @"ERN"); + XCTAssertEqualObjects(BNCCurrencyETB, @"ETB"); + XCTAssertEqualObjects(BNCCurrencyEUR, @"EUR"); + XCTAssertEqualObjects(BNCCurrencyFJD, @"FJD"); + XCTAssertEqualObjects(BNCCurrencyFKP, @"FKP"); + XCTAssertEqualObjects(BNCCurrencyGBP, @"GBP"); + XCTAssertEqualObjects(BNCCurrencyGEL, @"GEL"); + XCTAssertEqualObjects(BNCCurrencyGHS, @"GHS"); + XCTAssertEqualObjects(BNCCurrencyGIP, @"GIP"); + XCTAssertEqualObjects(BNCCurrencyGMD, @"GMD"); + XCTAssertEqualObjects(BNCCurrencyGNF, @"GNF"); + XCTAssertEqualObjects(BNCCurrencyGTQ, @"GTQ"); + XCTAssertEqualObjects(BNCCurrencyGYD, @"GYD"); + XCTAssertEqualObjects(BNCCurrencyHKD, @"HKD"); + XCTAssertEqualObjects(BNCCurrencyHNL, @"HNL"); + XCTAssertEqualObjects(BNCCurrencyHRK, @"HRK"); + XCTAssertEqualObjects(BNCCurrencyHTG, @"HTG"); + XCTAssertEqualObjects(BNCCurrencyHUF, @"HUF"); + XCTAssertEqualObjects(BNCCurrencyIDR, @"IDR"); + XCTAssertEqualObjects(BNCCurrencyILS, @"ILS"); + XCTAssertEqualObjects(BNCCurrencyINR, @"INR"); + XCTAssertEqualObjects(BNCCurrencyIQD, @"IQD"); + XCTAssertEqualObjects(BNCCurrencyIRR, @"IRR"); + XCTAssertEqualObjects(BNCCurrencyISK, @"ISK"); + XCTAssertEqualObjects(BNCCurrencyJMD, @"JMD"); + XCTAssertEqualObjects(BNCCurrencyJOD, @"JOD"); + XCTAssertEqualObjects(BNCCurrencyJPY, @"JPY"); + XCTAssertEqualObjects(BNCCurrencyKES, @"KES"); + XCTAssertEqualObjects(BNCCurrencyKGS, @"KGS"); + XCTAssertEqualObjects(BNCCurrencyKHR, @"KHR"); + XCTAssertEqualObjects(BNCCurrencyKMF, @"KMF"); + XCTAssertEqualObjects(BNCCurrencyKPW, @"KPW"); + XCTAssertEqualObjects(BNCCurrencyKRW, @"KRW"); + XCTAssertEqualObjects(BNCCurrencyKWD, @"KWD"); + XCTAssertEqualObjects(BNCCurrencyKYD, @"KYD"); + XCTAssertEqualObjects(BNCCurrencyKZT, @"KZT"); + XCTAssertEqualObjects(BNCCurrencyLAK, @"LAK"); + XCTAssertEqualObjects(BNCCurrencyLBP, @"LBP"); + XCTAssertEqualObjects(BNCCurrencyLKR, @"LKR"); + XCTAssertEqualObjects(BNCCurrencyLRD, @"LRD"); + XCTAssertEqualObjects(BNCCurrencyLSL, @"LSL"); + XCTAssertEqualObjects(BNCCurrencyLYD, @"LYD"); + XCTAssertEqualObjects(BNCCurrencyMAD, @"MAD"); + XCTAssertEqualObjects(BNCCurrencyMDL, @"MDL"); + XCTAssertEqualObjects(BNCCurrencyMGA, @"MGA"); + XCTAssertEqualObjects(BNCCurrencyMKD, @"MKD"); + XCTAssertEqualObjects(BNCCurrencyMMK, @"MMK"); + XCTAssertEqualObjects(BNCCurrencyMNT, @"MNT"); + XCTAssertEqualObjects(BNCCurrencyMOP, @"MOP"); + XCTAssertEqualObjects(BNCCurrencyMRO, @"MRO"); + XCTAssertEqualObjects(BNCCurrencyMUR, @"MUR"); + XCTAssertEqualObjects(BNCCurrencyMVR, @"MVR"); + XCTAssertEqualObjects(BNCCurrencyMWK, @"MWK"); + XCTAssertEqualObjects(BNCCurrencyMXN, @"MXN"); + XCTAssertEqualObjects(BNCCurrencyMXV, @"MXV"); + XCTAssertEqualObjects(BNCCurrencyMYR, @"MYR"); + XCTAssertEqualObjects(BNCCurrencyMZN, @"MZN"); + XCTAssertEqualObjects(BNCCurrencyNAD, @"NAD"); + XCTAssertEqualObjects(BNCCurrencyNGN, @"NGN"); + XCTAssertEqualObjects(BNCCurrencyNIO, @"NIO"); + XCTAssertEqualObjects(BNCCurrencyNOK, @"NOK"); + XCTAssertEqualObjects(BNCCurrencyNPR, @"NPR"); + XCTAssertEqualObjects(BNCCurrencyNZD, @"NZD"); + XCTAssertEqualObjects(BNCCurrencyOMR, @"OMR"); + XCTAssertEqualObjects(BNCCurrencyPAB, @"PAB"); + XCTAssertEqualObjects(BNCCurrencyPEN, @"PEN"); + XCTAssertEqualObjects(BNCCurrencyPGK, @"PGK"); + XCTAssertEqualObjects(BNCCurrencyPHP, @"PHP"); + XCTAssertEqualObjects(BNCCurrencyPKR, @"PKR"); + XCTAssertEqualObjects(BNCCurrencyPLN, @"PLN"); + XCTAssertEqualObjects(BNCCurrencyPYG, @"PYG"); + XCTAssertEqualObjects(BNCCurrencyQAR, @"QAR"); + XCTAssertEqualObjects(BNCCurrencyRON, @"RON"); + XCTAssertEqualObjects(BNCCurrencyRSD, @"RSD"); + XCTAssertEqualObjects(BNCCurrencyRUB, @"RUB"); + XCTAssertEqualObjects(BNCCurrencyRWF, @"RWF"); + XCTAssertEqualObjects(BNCCurrencySAR, @"SAR"); + XCTAssertEqualObjects(BNCCurrencySBD, @"SBD"); + XCTAssertEqualObjects(BNCCurrencySCR, @"SCR"); + XCTAssertEqualObjects(BNCCurrencySDG, @"SDG"); + XCTAssertEqualObjects(BNCCurrencySEK, @"SEK"); + XCTAssertEqualObjects(BNCCurrencySGD, @"SGD"); + XCTAssertEqualObjects(BNCCurrencySHP, @"SHP"); + XCTAssertEqualObjects(BNCCurrencySLL, @"SLL"); + XCTAssertEqualObjects(BNCCurrencySOS, @"SOS"); + XCTAssertEqualObjects(BNCCurrencySRD, @"SRD"); + XCTAssertEqualObjects(BNCCurrencySSP, @"SSP"); + XCTAssertEqualObjects(BNCCurrencySTD, @"STD"); + XCTAssertEqualObjects(BNCCurrencySYP, @"SYP"); + XCTAssertEqualObjects(BNCCurrencySZL, @"SZL"); + XCTAssertEqualObjects(BNCCurrencyTHB, @"THB"); + XCTAssertEqualObjects(BNCCurrencyTJS, @"TJS"); + XCTAssertEqualObjects(BNCCurrencyTMT, @"TMT"); + XCTAssertEqualObjects(BNCCurrencyTND, @"TND"); + XCTAssertEqualObjects(BNCCurrencyTOP, @"TOP"); + XCTAssertEqualObjects(BNCCurrencyTRY, @"TRY"); + XCTAssertEqualObjects(BNCCurrencyTTD, @"TTD"); + XCTAssertEqualObjects(BNCCurrencyTWD, @"TWD"); + XCTAssertEqualObjects(BNCCurrencyTZS, @"TZS"); + XCTAssertEqualObjects(BNCCurrencyUAH, @"UAH"); + XCTAssertEqualObjects(BNCCurrencyUGX, @"UGX"); + XCTAssertEqualObjects(BNCCurrencyUSD, @"USD"); + XCTAssertEqualObjects(BNCCurrencyUSN, @"USN"); + XCTAssertEqualObjects(BNCCurrencyUYI, @"UYI"); + XCTAssertEqualObjects(BNCCurrencyUYU, @"UYU"); + XCTAssertEqualObjects(BNCCurrencyUZS, @"UZS"); + XCTAssertEqualObjects(BNCCurrencyVEF, @"VEF"); + XCTAssertEqualObjects(BNCCurrencyVND, @"VND"); + XCTAssertEqualObjects(BNCCurrencyVUV, @"VUV"); + XCTAssertEqualObjects(BNCCurrencyWST, @"WST"); + XCTAssertEqualObjects(BNCCurrencyXAF, @"XAF"); + XCTAssertEqualObjects(BNCCurrencyXAG, @"XAG"); + XCTAssertEqualObjects(BNCCurrencyXAU, @"XAU"); + XCTAssertEqualObjects(BNCCurrencyXCD, @"XCD"); + XCTAssertEqualObjects(BNCCurrencyXDR, @"XDR"); + XCTAssertEqualObjects(BNCCurrencyXOF, @"XOF"); + XCTAssertEqualObjects(BNCCurrencyXPD, @"XPD"); + XCTAssertEqualObjects(BNCCurrencyXPF, @"XPF"); + XCTAssertEqualObjects(BNCCurrencyXPT, @"XPT"); + XCTAssertEqualObjects(BNCCurrencyXSU, @"XSU"); + XCTAssertEqualObjects(BNCCurrencyXTS, @"XTS"); + XCTAssertEqualObjects(BNCCurrencyXUA, @"XUA"); + XCTAssertEqualObjects(BNCCurrencyXXX, @"XXX"); + XCTAssertEqualObjects(BNCCurrencyYER, @"YER"); + XCTAssertEqualObjects(BNCCurrencyZAR, @"ZAR"); + XCTAssertEqualObjects(BNCCurrencyZMW, @"ZMW"); +} + + +@end diff --git a/BranchSDKTests/BNCDeviceInfoTests.m b/BranchSDKTests/BNCDeviceInfoTests.m new file mode 100644 index 000000000..39b4ee5d8 --- /dev/null +++ b/BranchSDKTests/BNCDeviceInfoTests.m @@ -0,0 +1,190 @@ +// +// BNCDeviceInfoTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 11/21/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +#import "BNCDeviceInfo.h" +#import "BNCUserAgentCollector.h" + +@interface BNCDeviceInfoTests : XCTestCase +@property (nonatomic, strong, readwrite) BNCDeviceInfo *deviceInfo; +@end + +@implementation BNCDeviceInfoTests + +- (void)setUp { + [self workaroundUserAgentLazyLoad]; + self.deviceInfo = [BNCDeviceInfo new]; +} + +// user agent needs to be loaded +- (void)workaroundUserAgentLazyLoad { + __block XCTestExpectation *expectation = [self expectationWithDescription:@"setup"]; + [[BNCUserAgentCollector instance] loadUserAgentWithCompletion:^(NSString * _Nullable userAgent) { + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { }]; +} + +- (void)tearDown { + +} + +- (void)testHardwareId { + XCTAssertNotNil(self.deviceInfo.hardwareId); + + // verify hardwareId is a valid UUID + NSUUID *hardwareId = [[NSUUID alloc] initWithUUIDString:self.deviceInfo.hardwareId]; + XCTAssertNotNil(hardwareId); +} + +- (void)testHardwareIdType { + // without ATT, this is the IDFV. Branch servers expect it as vendor_id + XCTAssert([self.deviceInfo.hardwareIdType isEqualToString:@"vendor_id"]); +} + +- (void)testIsRealHardwareId { + XCTAssert(self.deviceInfo.isRealHardwareId); +} + +- (void)testAdvertiserId { + // the testbed does not show the ATT prompt. + XCTAssertNil(self.deviceInfo.advertiserId); +} + +- (void)testVendorId { + XCTAssertNotNil(self.deviceInfo.vendorId); + + // verify vendorId is a valid UUID + NSUUID *vendorId = [[NSUUID alloc] initWithUUIDString:self.deviceInfo.vendorId]; + XCTAssertNotNil(vendorId); +} + +- (void)testAnonId { + XCTAssertNotNil(self.deviceInfo.anonId); + + // verify anonId is a valid UUID + NSUUID *anonId = [[NSUUID alloc] initWithUUIDString:self.deviceInfo.anonId]; + XCTAssertNotNil(anonId); +} + +- (void)testOptedInStatus { + // the testbed does not show the ATT prompt. + XCTAssert([self.deviceInfo.optedInStatus isEqualToString:@"not_determined"]); +} + +- (void)testIsFirstOptIn { + // the testbed does not show the ATT prompt. + XCTAssert(self.deviceInfo.isFirstOptIn == NO); +} + +- (void)testLocalIPAddress { + NSString *address = [self.deviceInfo localIPAddress]; + XCTAssertNotNil(address); + XCTAssert(address.length > 7); +} + +- (void)testConnectionType { + // simulator is on wifi + XCTAssert([[self.deviceInfo connectionType] isEqualToString:@"wifi"]); +} + +- (void)testBrandName { + XCTAssert([@"Apple" isEqualToString:self.deviceInfo.brandName]); +} + +- (void)testModelName_Simulator { + // intel processor + bool x86_64 = [@"x86_64" isEqualToString:self.deviceInfo.modelName]; + + // apple processor + bool arm64 = [@"arm64" isEqualToString:self.deviceInfo.modelName]; + + XCTAssert(x86_64 || arm64); +} + +- (void)testOSName { + XCTAssertNotNil(self.deviceInfo.osName); + XCTAssert([@"iOS" isEqualToString:self.deviceInfo.osName]); +} + +- (void)testOSVersion { + XCTAssertNotNil(self.deviceInfo.osVersion); + XCTAssert([self.deviceInfo.osVersion isEqualToString:[UIDevice currentDevice].systemVersion]); +} + +- (void)testOSBuildVersion { + XCTAssertNotNil(self.deviceInfo.osBuildVersion); +} + +- (void)testEnvironment { + // currently not running unit tests on extensions + XCTAssert([@"FULL_APP" isEqualToString:self.deviceInfo.environment]); +} + +- (void)testCpuType_Simulator { + // intel processors + bool x86 = [@"7" isEqualToString:self.deviceInfo.cpuType]; + + // apple processors + bool arm = [@"16777228" isEqualToString:self.deviceInfo.cpuType]; + + XCTAssert(x86 || arm); +} + +- (void)testScreenWidth { + XCTAssert(self.deviceInfo.screenWidth.intValue >= 320); +} + +- (void)testScreenHeight { + XCTAssert(self.deviceInfo.screenHeight.intValue >= 320); +} + +- (void)testScreenScale { + XCTAssert(self.deviceInfo.screenScale.intValue >= 1); +} + +- (void)testLocale { + NSString *locale = [NSLocale currentLocale].localeIdentifier; + XCTAssertNotNil(locale); + XCTAssert([locale isEqualToString:self.deviceInfo.locale]); +} + +- (void)testCountry { + NSString *locale = [NSLocale currentLocale].localeIdentifier; + XCTAssertNotNil(locale); + XCTAssert([locale containsString:self.deviceInfo.country]); +} + +- (void)testLanguage { + NSString *locale = [NSLocale currentLocale].localeIdentifier; + XCTAssertNotNil(locale); + XCTAssert([locale containsString:self.deviceInfo.language]); +} + +- (void)testUserAgentString { + XCTAssert([self.deviceInfo.userAgentString containsString:@"AppleWebKit"]); +} + +- (void)testApplicationVersion_TestBed { + XCTAssert([@"1.0" isEqualToString:self.deviceInfo.applicationVersion]); +} + +- (void)testRegisterPluginNameVersion { + XCTAssertNil(self.deviceInfo.pluginName); + XCTAssertNil(self.deviceInfo.pluginVersion); + + NSString *expectedName = @"react native"; + NSString *expectedVersion = @"1.0.0"; + + [self.deviceInfo registerPluginName:expectedName version:expectedVersion]; + + XCTAssert([expectedName isEqualToString:self.deviceInfo.pluginName]); + XCTAssert([expectedVersion isEqualToString:self.deviceInfo.pluginVersion]); +} + +@end diff --git a/BranchSDKTests/BNCDeviceSystemTests.m b/BranchSDKTests/BNCDeviceSystemTests.m new file mode 100644 index 000000000..0a47b73d9 --- /dev/null +++ b/BranchSDKTests/BNCDeviceSystemTests.m @@ -0,0 +1,62 @@ +// +// BNCDeviceSystemTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 11/14/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +#import "BNCDeviceSystem.h" + +@interface BNCDeviceSystemTests : XCTestCase + +@property (nonatomic, strong, readwrite) BNCDeviceSystem *deviceSystem; + +@end + +@implementation BNCDeviceSystemTests + +- (void)setUp { + self.deviceSystem = [BNCDeviceSystem new]; +} + +- (void)tearDown { + +} + +- (void)testSystemBuildVersion { + XCTAssertNotNil(self.deviceSystem.systemBuildVersion); + XCTAssert(self.deviceSystem.systemBuildVersion.length > 0); +} + +- (void)testMachine_Simulator { + // intel processor + bool x86_64 = [@"x86_64" isEqualToString:self.deviceSystem.machine]; + + // apple processor + bool arm64 = [@"arm64" isEqualToString:self.deviceSystem.machine]; + + XCTAssert(x86_64 || arm64); +} + +- (void)testCPUType_Simulator { + // intel processor + bool x86 = [@(7) isEqualToNumber:self.deviceSystem.cpuType]; + bool x86_sub = [@(8) isEqualToNumber:self.deviceSystem.cpuSubType]; + + // apple processor + bool arm = [@(16777228) isEqualToNumber:self.deviceSystem.cpuType]; + bool arm_sub = [@(2) isEqualToNumber:self.deviceSystem.cpuSubType]; + + XCTAssert(x86 || arm); + +// cpu subtype is different on cloud runners +// if (x86) { +// XCTAssert(x86_sub); +// } else { +// XCTAssert(arm_sub); +// } +} + +@end diff --git a/BranchSDKTests/BNCDisableAdNetworkCalloutsTests.m b/BranchSDKTests/BNCDisableAdNetworkCalloutsTests.m new file mode 100644 index 000000000..ea277ac39 --- /dev/null +++ b/BranchSDKTests/BNCDisableAdNetworkCalloutsTests.m @@ -0,0 +1,59 @@ +// +// BNCDisableAdNetworkCalloutsTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 3/2/20. +// Copyright © 2020 Branch, Inc. All rights reserved. +// + +#import +#import "BNCPreferenceHelper.h" +#import "BNCDeviceInfo.h" +#import "BNCServerInterface.h" +@import BranchSDK; + +@interface BNCServerInterface() +- (void)updateDeviceInfoToMutableDictionary:(NSMutableDictionary *)dict; +@end + +@interface BNCDisableAdNetworkCalloutsTests : XCTestCase + +@end + +// These tests are not parallelizable and therefore disabled by default +// This is due to the tight coupling between BNCPreferenceHelper and BNCDeviceInfo +@implementation BNCDisableAdNetworkCalloutsTests +/* +- (void)setUp { + [BNCPreferenceHelper preferenceHelper].disableAdNetworkCallouts = YES; +} + +- (void)tearDown { + [BNCPreferenceHelper preferenceHelper].disableAdNetworkCallouts = NO; +} + +// check on the V2 dictionary +- (void)testV2Dictionary { + NSDictionary *dict = [[BNCDeviceInfo getInstance] v2dictionary]; + XCTAssertNotNil(dict); + XCTAssertNotNil(dict[@"brand"]); + XCTAssertNotNil(dict[@"os"]); + XCTAssertNotNil(dict[@"sdk"]); + XCTAssertNotNil(dict[@"sdk_version"]); + + XCTAssertTrue(dict[@"disable_ad_network_callouts"]); +} + +// check on V1 payload +- (void)testV1Payload { + BNCServerInterface *interface = [BNCServerInterface new]; + interface.preferenceHelper = [BNCPreferenceHelper preferenceHelper]; + + NSMutableDictionary *tmp = [NSMutableDictionary new]; + [interface updateDeviceInfoToMutableDictionary:tmp]; + + XCTAssertNotNil(tmp); + XCTAssertTrue(tmp[@"disable_ad_network_callouts"]); +} +*/ +@end diff --git a/BranchSDKTests/BNCEncodingUtilsTests.m b/BranchSDKTests/BNCEncodingUtilsTests.m new file mode 100644 index 000000000..0b18fe51a --- /dev/null +++ b/BranchSDKTests/BNCEncodingUtilsTests.m @@ -0,0 +1,609 @@ +// +// BNCEncodingUtils.m +// Branch +// +// Created by Graham Mueller on 4/1/15. +// Copyright (c) 2015 Branch Metrics. All rights reserved. +// + +#import +#import "BNCEncodingUtils.h" + +@interface BNCEncodingUtilsTests : XCTestCase +@end + +@implementation BNCEncodingUtilsTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +#pragma mark - EncodeDictionaryToJsonString tests + +- (void)testEncodeDictionaryToJsonStringWithExpectedParams { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; + NSDate *date = [dateFormatter dateFromString:@"2015-04-01T00:00:00-05:00"]; + NSString *formattedDateString = [dateFormatter stringFromDate:date]; + + NSURL *someUrl = [NSURL URLWithString:@"https://branch.io"]; + NSDictionary *dataDict = @{ + @"foo": @"bar", + @"num": @1, + @"array": @[ @"array", @"items" ], + @"dict": @{ @"sub": @1 }, + @"url": someUrl, + @"date": date + }; + NSString *expectedEncodedString = [NSString stringWithFormat: + @"{\"foo\":\"bar\",\"num\":1,\"array\":[\"array\",\"items\"],\"dict\":{\"sub\":1},\"url\":\"https://branch.io\",\"date\":\"%@\"}", + formattedDateString]; + NSString *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonString:dataDict]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeDictionaryToJsonStringWithUnexpectedParams { + NSObject *arbitraryObj = [[NSObject alloc] init]; + NSDictionary *dataDict = @{ @"foo": @"bar", @"random": arbitraryObj }; + NSString *expectedEncodedString = @"{\"foo\":\"bar\"}"; + + NSString *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonString:dataDict]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeDictionaryToJsonStringStringWithNull { + NSDictionary *dataDict = @{ @"foo": [NSNull null] }; + NSString *expectedEncodedString = @"{\"foo\":null}"; + + NSString *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonString:dataDict]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodingNilDictionaryToJsonString { + NSDictionary *dataDict = nil; + NSString *expectedEncodedString = @"{}"; + + NSString *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonString:dataDict]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeDictionaryToJsonStringWithNoKeys { + NSDictionary *emptyDict = @{ }; + NSString *expectedEncodedString = @"{}"; + + NSString *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonString:emptyDict]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeDictionaryToJsonStringWithQuotes { + NSDictionary *dictionaryWithQuotes = @{ @"my\"cool\"key": @"my\"cool\"value" }; + NSString *expectedEncodedString = @"{\"my\\\"cool\\\"key\":\"my\\\"cool\\\"value\"}"; + + NSString *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonString:dictionaryWithQuotes]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testSimpleEncodeDictionaryToJsonData { + NSDictionary *dataDict = @{ @"foo": @"bar" }; + NSData *expectedEncodedData = [@"{\"foo\":\"bar\"}" dataUsingEncoding:NSUTF8StringEncoding]; + + NSData *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonData:dataDict]; + + XCTAssertEqualObjects(expectedEncodedData, encodedValue); +} + +- (void)testEncodeDictionaryToQueryString { + NSDictionary *dataDict = @{ @"foo": @"bar", @"something": @"something & something" }; + NSString *expectedEncodedString = @"?foo=bar&something=something%20%26%20something"; + + NSString *encodedValue = [BNCEncodingUtils encodeDictionaryToQueryString:dataDict]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + + +#pragma mark - EncodeArrayToJsonString + +- (void)testEncodeArrayToJsonStringWithExpectedParams { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; + NSDate *date = [dateFormatter dateFromString:@"2015-04-01T00:00:00Z"]; + NSString *formattedDateString = [dateFormatter stringFromDate:date]; + + NSURL *someUrl = [NSURL URLWithString:@"https://branch.io"]; + NSArray *dataArray = @[ @"bar", @1, @[ @"array", @"items" ], @{ @"sub": @1 }, someUrl, date ]; + NSString *expectedEncodedString = [NSString stringWithFormat:@"[\"bar\",1,[\"array\",\"items\"],{\"sub\":1},\"https://branch.io\",\"%@\"]", formattedDateString]; + + NSString *encodedValue = [BNCEncodingUtils encodeArrayToJsonString:dataArray]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeArrayToJsonStringWithUnexpectedParams { + NSObject *arbitraryObj = [[NSObject alloc] init]; + NSArray *dataArray = @[ @"bar", arbitraryObj ]; + NSString *expectedEncodedString = @"[\"bar\"]"; + + NSString *encodedValue = [BNCEncodingUtils encodeArrayToJsonString:dataArray]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeArrayToJsonStringStringWithNull { + NSArray *dataArray = @[ [NSNull null] ]; + NSString *expectedEncodedString = @"[null]"; + + NSString *encodedValue = [BNCEncodingUtils encodeArrayToJsonString:dataArray]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeArrayToJsonStringWithNoValues { + NSArray *emptyArray = @[ ]; + NSString *expectedEncodedString = @"[]"; + + NSString *encodedValue = [BNCEncodingUtils encodeArrayToJsonString:emptyArray]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodingEmptyArrayToJsonString { + NSArray *emptyArray = nil; + NSString *expectedEncodedString = @"[]"; + + NSString *encodedValue = [BNCEncodingUtils encodeArrayToJsonString:emptyArray]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + +- (void)testEncodeArrayToJsonStringWithQuotes { + NSArray *arrayWithQuotes = @[ @"my\"cool\"value1", @"my\"cool\"value2" ]; + NSString *expectedEncodedString = @"[\"my\\\"cool\\\"value1\",\"my\\\"cool\\\"value2\"]"; + + NSString *encodedValue = [BNCEncodingUtils encodeArrayToJsonString:arrayWithQuotes]; + + XCTAssertEqualObjects(expectedEncodedString, encodedValue); +} + + +#pragma mark - Character Length tests + +- (void)testChineseCharactersWithLengthGreaterThanOne { + NSString *multiCharacterString = @"𥑮"; + NSDictionary *jsonDict = @{ @"foo": multiCharacterString }; + NSString *expectedEncoding = @"{\"foo\":\"𥑮\"}"; + NSInteger expectedLength = [expectedEncoding lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + NSData *encodedValue = [BNCEncodingUtils encodeDictionaryToJsonData:jsonDict]; + + XCTAssertEqual(expectedLength, [encodedValue length]); +} + + +#pragma mark - DecodeToDictionary tests + +- (void)testDecodeJsonDataToDictionary { + NSData *encodedData = [@"{\"foo\":\"bar\"}" dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *expectedDataDict = @{ @"foo": @"bar" }; + + NSDictionary *decodedValue = [BNCEncodingUtils decodeJsonDataToDictionary:encodedData]; + + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +- (void)testDecodeJsonStringToDictionary { + NSString *encodedString = @"{\"foo\":\"bar\"}"; + NSDictionary *expectedDataDict = @{ @"foo": @"bar" }; + + NSDictionary *decodedValue = [BNCEncodingUtils decodeJsonStringToDictionary:encodedString]; + + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +#if 0 + +// From Ed: See note below +- (void)testDecodeJsonStringToDictionaryWithNilDecodedString { + char badCStr[5] = { '{', 'f', ':', 'o', '}' }; // not nil terminated + NSString *encodedString = [NSString stringWithUTF8String:badCStr]; + NSDictionary *expectedDataDict = @{ }; + + NSDictionary *decodedValue = [BNCEncodingUtils decodeJsonStringToDictionary:encodedString]; + + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +#else + +- (void)testDecodeJsonStringToDictionaryWithNilDecodedString { + NSString *encodedString = nil; + NSDictionary *expectedDataDict = @{ }; + NSDictionary *decodedValue = [BNCEncodingUtils decodeJsonStringToDictionary:encodedString]; + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +#endif + +- (void)testDecodeBase64EncodedJsonStringToDictionary { + NSString *encodedString = [BNCEncodingUtils base64EncodeStringToString:@"{\"foo\":\"bar\"}"]; + NSDictionary *expectedDataDict = @{ @"foo": @"bar" }; + + NSDictionary *decodedValue = [BNCEncodingUtils decodeJsonStringToDictionary:encodedString]; + + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +- (void)testDecodeNonASCIIString { + // Should fail, but not crash. + NSString* result = [BNCEncodingUtils base64DecodeStringToString:@"𝄞"]; + XCTAssertNil(result); +} + +#if 0 + +// From Ed: I don't get the point of this test. +// It reads memory from the stack as a C string and decodes it as an NSString? +// The test itself won't run consistently and may fault sometimes. +- (void)testDecodeBase64JsonStringToDictionaryWithNilDecodedString { + char badCStr[5] = { '{', 'f', ':', 'o', '}' }; // not nil terminated + NSString *encodedString = [NSString stringWithUTF8String:badCStr]; + NSString *base64EncodedString = [BNCEncodingUtils base64EncodeStringToString:encodedString]; + NSDictionary *expectedDataDict = @{ }; + + NSDictionary *decodedValue = [BNCEncodingUtils decodeJsonStringToDictionary:base64EncodedString]; + + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +#else + +// This should do the same thing without faulting during the test. +- (void)testDecodeBase64JsonStringToDictionaryWithNilDecodedString { + NSString *base64EncodedString = nil; + NSDictionary *expectedDataDict = @{ }; + NSDictionary *decodedValue = [BNCEncodingUtils decodeJsonStringToDictionary:base64EncodedString]; + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +#endif + +- (void)testDecodeQueryStringToDictionary { + NSString *encodedString = @"foo=bar&baz=1&quux=&quo=Hi%20there"; + NSDictionary *expectedDataDict = @{ @"foo": @"bar", @"baz": @"1", @"quo": @"Hi there" }; // always goes to string + + NSDictionary *decodedValue = [BNCEncodingUtils decodeQueryStringToDictionary:encodedString]; + + XCTAssertEqualObjects(decodedValue, expectedDataDict); +} + +#pragma mark - Test Util methods + +- (NSString *)stringForDate:(NSDate *)date { + static NSDateFormatter *dateFormatter = nil; + static dispatch_once_t onceToken = 0; + + dispatch_once(&onceToken, ^{ + dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; // POSIX to avoid weird issues + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; + }); + + return [dateFormatter stringFromDate:date]; +} + +#pragma mark - Base64EncodeData Tests + +#define _countof(array) (sizeof(array)/sizeof(array[0])) + +- (void)testBase64EncodeData { + NSData *data = nil; + NSString *truth = nil; + NSString *string = nil; + + string = [BNCEncodingUtils base64EncodeData:nil]; + XCTAssertEqualObjects(string, @""); + + string = [BNCEncodingUtils base64EncodeData:[NSData new]]; + XCTAssertEqualObjects(string, @""); + + uint8_t b1[] = {0, 1, 2, 3, 4, 5}; + data = [[NSData alloc] initWithBytes:b1 length:_countof(b1)]; + truth = @"AAECAwQF"; + string = [BNCEncodingUtils base64EncodeData:data]; + XCTAssertEqualObjects(string, truth); + + // Test that 1, 2, 3, 4, 5 length data encode correctly. + + data = [[NSData alloc] initWithBytes:b1 length:1]; + truth = @"AA=="; + string = [BNCEncodingUtils base64EncodeData:data]; + XCTAssertEqualObjects(string, truth); + + data = [[NSData alloc] initWithBytes:b1 length:2]; + truth = @"AAE="; + string = [BNCEncodingUtils base64EncodeData:data]; + XCTAssertEqualObjects(string, truth); + + data = [[NSData alloc] initWithBytes:b1 length:3]; + truth = @"AAEC"; + string = [BNCEncodingUtils base64EncodeData:data]; + XCTAssertEqualObjects(string, truth); + + data = [[NSData alloc] initWithBytes:b1 length:4]; + truth = @"AAECAw=="; + string = [BNCEncodingUtils base64EncodeData:data]; + XCTAssertEqualObjects(string, truth); + + data = [[NSData alloc] initWithBytes:b1 length:5]; + truth = @"AAECAwQ="; + string = [BNCEncodingUtils base64EncodeData:data]; + XCTAssertEqualObjects(string, truth); + + uint8_t b2[] = { + 0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8B, 0x30, 0xD3, 0x8F, 0x41, 0x14, 0x93, 0x51, + 0x55, 0x97, 0x61, 0x96, 0x9B, 0x71, 0xD7, 0x9F, 0x82, 0x18, 0xA3, 0x92, 0x59, 0xA7, 0xA2, 0x9A, + 0xAB, 0xB2, 0xDB, 0xAF, 0xC3, 0x1C, 0xB3, 0xD3, 0x5D, 0xB7, 0xE3, 0x9E, 0xBB, 0xF3, 0xDF, 0xBF, + }; + data = [[NSData alloc] initWithBytes:b2 length:_countof(b2)]; + truth = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + string = [BNCEncodingUtils base64EncodeData:data]; + XCTAssertEqualObjects(string, truth); +} + +- (void)testBase64DecodeString { + NSData *data = nil; + + data = [BNCEncodingUtils base64DecodeString:nil]; + XCTAssertEqual(data, nil); + + data = [BNCEncodingUtils base64DecodeString:@""]; + XCTAssertEqualObjects(data, [NSData new]); + + uint8_t truth[] = {0, 1, 2, 3, 4, 5}; + + data = [BNCEncodingUtils base64DecodeString:@"AAECAwQF"]; + XCTAssertTrue( data.length == 6 && memcmp(data.bytes, truth, 6) == 0 ); + + // Test that 1, 2, 3, 4, 5 length data encode correctly. + + #define testDecode(string, dataLength) { \ + data = [BNCEncodingUtils base64DecodeString:string]; \ + XCTAssertTrue( data.length == dataLength && memcmp(data.bytes, truth, dataLength) == 0 ); \ + } + + testDecode(@"AA==", 1); + testDecode(@"AAE=", 2); + testDecode(@"AAEC", 3); + testDecode(@"AAECAw==", 4); + testDecode(@"AAECAwQ=", 5); + + uint8_t b2[] = { + 0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8B, 0x30, 0xD3, 0x8F, 0x41, 0x14, 0x93, 0x51, + 0x55, 0x97, 0x61, 0x96, 0x9B, 0x71, 0xD7, 0x9F, 0x82, 0x18, 0xA3, 0x92, 0x59, 0xA7, 0xA2, 0x9A, + 0xAB, 0xB2, 0xDB, 0xAF, 0xC3, 0x1C, 0xB3, 0xD3, 0x5D, 0xB7, 0xE3, 0x9E, 0xBB, 0xF3, 0xDF, 0xBF, + }; + data = [BNCEncodingUtils base64DecodeString: + @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]; + XCTAssertTrue( data.length == _countof(b2) && memcmp(data.bytes, b2, _countof(b2)) == 0 ); + + // Test decode invalid data + data = [BNCEncodingUtils base64DecodeString: + @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde (Junk:%&*^**) fghijklmnopqrstuvwxyz0123456789+/"]; + XCTAssertEqual(data, nil); +} + +#pragma mark - Hex String Tests + +- (void) testHexStringFromData { + + NSString *s = nil; + + s = [BNCEncodingUtils hexStringFromData:[NSData data]]; + XCTAssertEqualObjects(s, @""); + + unsigned char bytes1[] = { 0x01 }; + s = [BNCEncodingUtils hexStringFromData:[NSData dataWithBytes:bytes1 length:1]]; + XCTAssertEqualObjects(s, @"01"); + + unsigned char bytes2[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef + }; + NSString *truth = + @"000102030405060708090A0B0C0D0E0F" + "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"; + + s = [BNCEncodingUtils hexStringFromData:[NSData dataWithBytes:bytes2 length:sizeof(bytes2)]]; + XCTAssertEqualObjects(s, truth); +} + +- (void) testDataFromHexString { + + NSData *data = nil; + + // nil + data = [BNCEncodingUtils dataFromHexString:nil]; + XCTAssertEqual(data, nil); + + // empty string + data = [BNCEncodingUtils dataFromHexString:@""]; + XCTAssertEqualObjects(data, [NSData data]); + + // upper string + unsigned char bytes[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef + }; + + NSString *stringUpper = + @"000102030405060708090A0B0C0D0E0F" + "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"; + + data = [BNCEncodingUtils dataFromHexString:stringUpper]; + XCTAssertEqualObjects(data, [NSData dataWithBytes:bytes length:sizeof(bytes)]); + + // lower string + + NSString *stringLower = + @"000102030405060708090a0b0c0d0e0f" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"; + + data = [BNCEncodingUtils dataFromHexString:stringLower]; + XCTAssertEqualObjects(data, [NSData dataWithBytes:bytes length:sizeof(bytes)]); + + // white space + + NSString *stringWS = + @" 000102030405060708090a0b0c0d0e0f\n" + "e0e1e2e3e4e5e6\t\t\te7e8e9eaebeced\vee\ref"; + + data = [BNCEncodingUtils dataFromHexString:stringWS]; + XCTAssertEqualObjects(data, [NSData dataWithBytes:bytes length:sizeof(bytes)]); + + // odd number of charaters + + NSString *stringShort = + @"000102030405060708090a0b0c0d0e0f" + "e0e1e2e3e4e5e6e7e8e9eaebecedeee"; + + data = [BNCEncodingUtils dataFromHexString:stringShort]; + XCTAssertEqual(data, nil); + + // invalid characters + + NSString *stringInvalid = + @"000102030405060708090a0b0c0d0e0fInvalid" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"; + + data = [BNCEncodingUtils dataFromHexString:stringInvalid]; + XCTAssertEqual(data, nil); + + // singles + + NSString *stringShortShort1 = @"A"; + data = [BNCEncodingUtils dataFromHexString:stringShortShort1]; + XCTAssertEqual(data, nil); + + NSString *stringShortShort2 = @"af"; + unsigned char stringShortShort2Bytes[] = { 0xaf }; + data = [BNCEncodingUtils dataFromHexString:stringShortShort2]; + XCTAssertEqualObjects(data, [NSData dataWithBytes:stringShortShort2Bytes length:1]); +} + +- (void) testPercentDecoding { + + NSString *s = nil; + s = [BNCEncodingUtils stringByPercentDecodingString:nil]; + XCTAssert(s == nil); + + NSArray* tests = @[ + @"", + @"", + + @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + + @"-._~", + @"-._~", + + @"one%20two", + @"one two", + + // @"one+two", + // @"one two", + + @"one%2Btwo", + @"one+two", + + @"%21%23%24%26%27%28%29%2A%2B%2C%3A%3B%3D%40%5B%5D", + @"!#$&'()*+,:;=@[]", + ]; + + for (int i = 0; i < tests.count; i+=2) { + NSString *result = [BNCEncodingUtils stringByPercentDecodingString:tests[i]]; + XCTAssertEqualObjects(result, tests[i+1]); + } +} + +- (void) testQueryItems { + + NSURL *URL = nil; + NSArray* items = nil; + NSArray* expected = nil; + + items = [BNCEncodingUtils queryItems:URL]; + XCTAssert(items != nil && items.count == 0); + + URL = [NSURL URLWithString:@"http://example.com/thus?a=1&a=2&b=3"]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ [BNCKeyValue key:@"a" value:@"1"], [BNCKeyValue key:@"a" value:@"2"], [BNCKeyValue key:@"b" value:@"3"] ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus"]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus?"]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus?="]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus?a="]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ [BNCKeyValue key:@"a" value:@""] ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus?=1"]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ [BNCKeyValue key:@"" value:@"1"] ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus?a=1&"]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ [BNCKeyValue key:@"a" value:@"1"] ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus?a=1&&b=2"]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ [BNCKeyValue key:@"a" value:@"1"], [BNCKeyValue key:@"b" value:@"2"] ]; + XCTAssertEqualObjects(items, expected); + + URL = [NSURL URLWithString:@"http://example.com/thus?a=1&b==2"]; + items = [BNCEncodingUtils queryItems:URL]; + expected = @[ [BNCKeyValue key:@"a" value:@"1"], [BNCKeyValue key:@"b" value:@"=2"] ]; + XCTAssertEqualObjects(items, expected); +} + +- (void) testSanitzeString { + NSString*test = @"\b\f\n\r\t\"`\\"; + NSString*truth = @"\\b\\f\\n\\r\\t\\\"'\\\\"; + NSString*result = [BNCEncodingUtils sanitizedStringFromString:test]; + XCTAssertEqualObjects(result, truth); +} + +// Branch servers never return a json array at the top level. However, our parser should enforce it. +- (void)testArrayJSON { + NSString *test = @"[\"helloworld\"]"; + NSDictionary *tmp = [BNCEncodingUtils decodeJsonStringToDictionary:test]; + XCTAssert([tmp isKindOfClass:[NSDictionary class]]); +} + +@end diff --git a/BranchSDKTests/BNCJSONUtilityTests.m b/BranchSDKTests/BNCJSONUtilityTests.m new file mode 100644 index 000000000..7f5f1c1cf --- /dev/null +++ b/BranchSDKTests/BNCJSONUtilityTests.m @@ -0,0 +1,188 @@ +// +// BNCJSONUtilityTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 9/17/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +#import "BNCJSONUtility.h" +#import "BNCJsonLoader.h" + +@interface BNCJSONUtilityTests : XCTestCase +@property (nonatomic, strong, readwrite) NSDictionary *json; +@end + +@implementation BNCJSONUtilityTests + +- (void)setUp { + self.json = [BNCJsonLoader dictionaryFromJSONFileNamed:@"example"]; + XCTAssertNotNil(self.json); +} + +- (void)tearDown { + +} + +- (void)testIsNumber { + NSNumber *number = [NSNumber numberWithInt:314]; + XCTAssertTrue([BNCJSONUtility isNumber:number]); +} + +- (void)testIsNumber_Boxed { + XCTAssertTrue([BNCJSONUtility isNumber:@(1.0)]); +} + +- (void)testIsNumber_Nil { + XCTAssertFalse([BNCJSONUtility isNumber:nil]); +} + +- (void)testIsNumber_String { + XCTAssertFalse([BNCJSONUtility isNumber:@"1.0"]); +} + +- (void)testIsString { + XCTAssertTrue([BNCJSONUtility isString:@"1.0"]); +} + +- (void)testIsString_MutableString { + NSMutableString *string = [NSMutableString new]; + XCTAssertTrue([BNCJSONUtility isString:string]); +} + +- (void)testIsString_EmptyString { + XCTAssertTrue([BNCJSONUtility isString:@""]); +} + +- (void)testIsString_Nil { + XCTAssertFalse([BNCJSONUtility isString:nil]); +} + +- (void)testIsString_Number { + XCTAssertFalse([BNCJSONUtility isString:@(1.0)]); +} + +- (void)testIsArray { + NSArray *tmp = @[@1, @2]; + XCTAssertTrue([BNCJSONUtility isArray:tmp]); +} + +- (void)testIsArray_MutableArray { + NSMutableArray *tmp = [NSMutableArray new]; + XCTAssertTrue([BNCJSONUtility isArray:tmp]); +} + +- (void)testIsArray_EmptyArray { + XCTAssertTrue([BNCJSONUtility isArray:@[]]); +} + +- (void)testIsArray_Nil { + XCTAssertFalse([BNCJSONUtility isArray:nil]); +} + +- (void)testIsArray_Dictionary { + XCTAssertFalse([BNCJSONUtility isArray:[NSDictionary new]]); +} + +// successful call on untyped dictionary +- (void)testUntypedDictionary_CorrectType { + NSString *string = self.json[@"user_string"]; + XCTAssertNotNil(string); + XCTAssertTrue(([string isKindOfClass:[NSString class]] || [string isKindOfClass:[NSMutableString class]])); +} + +// demonstrates that an untyped dictionary can lead to type mismatches cause it always returns id +- (void)testUntypedDictionary_IncorrectType { + NSString *string = self.json[@"user_number"]; + XCTAssertNotNil(string); + XCTAssertTrue(([string isKindOfClass:[NSNumber class]])); +} + +- (void)testStringForKey_InvalidKey { + id key = @(1); + NSString *string = [BNCJSONUtility stringForKey:key json:self.json]; + XCTAssertNil(string); +} + +- (void)testStringForKey { + NSString *string = [BNCJSONUtility stringForKey:@"user_string" json:self.json]; + XCTAssertNotNil(string); + XCTAssertTrue(([string isKindOfClass:[NSString class]] || [string isKindOfClass:[NSMutableString class]])); +} + +- (void)testStringForKey_IncorrectType { + NSString *string = [BNCJSONUtility stringForKey:@"user_number" json:self.json]; + XCTAssertNil(string); +} + +- (void)testNumberForKey { + NSNumber *number = [BNCJSONUtility numberForKey:@"user_number" json:self.json]; + XCTAssertNotNil(number); + XCTAssertTrue([number isKindOfClass:[NSNumber class]]); +} + +- (void)testNumberForKey_IncorrectType { + NSNumber *number = [BNCJSONUtility numberForKey:@"user_string" json:self.json]; + XCTAssertNil(number); +} + +- (void)testDictionaryForKey { + NSDictionary *dict = [BNCJSONUtility dictionaryForKey:@"user_dict" json:self.json]; + XCTAssertNotNil(dict); + XCTAssertTrue(([dict isKindOfClass:NSDictionary.class] || [dict isKindOfClass:NSMutableDictionary.class])); +} + +- (void)testDictionaryForKey_IncorrectType { + NSDictionary *dict = [BNCJSONUtility dictionaryForKey:@"user_array" json:self.json]; + XCTAssertNil(dict); +} + +- (void)testArrayForKey { + NSArray *array = [BNCJSONUtility arrayForKey:@"user_array" json:self.json]; + XCTAssertNotNil(array); + XCTAssertTrue(([array isKindOfClass:[NSArray class]] || [array isKindOfClass:[NSMutableArray class]])); +} + +- (void)testArrayForKey_IncorrectType { + NSArray *array = [BNCJSONUtility arrayForKey:@"user_dict" json:self.json]; + XCTAssertNil(array); +} + +- (void)testStringArrayForKey { + NSArray *array = [BNCJSONUtility stringArrayForKey:@"user_array" json:self.json]; + XCTAssertNotNil(array); + XCTAssertTrue(array.count > 0); +} + +- (void)testStringArrayForKey_MixedTypes { + NSArray *array = [BNCJSONUtility stringArrayForKey:@"user_array_mixed" json:self.json]; + XCTAssertNotNil(array); + XCTAssertTrue(array.count > 0); +} + +- (void)testStringArrayForKey_Numbers { + NSArray *array = [BNCJSONUtility stringArrayForKey:@"user_array_numbers" json:self.json]; + XCTAssertNotNil(array); + XCTAssertTrue(array.count == 0); +} + +- (void)testStringDictionaryForKey { + NSDictionary *dict = [BNCJSONUtility stringDictionaryForKey:@"user_dict" json:self.json]; + XCTAssertNotNil(dict); + XCTAssertTrue(dict.count > 0); +} + +- (void)testStringDictionaryForKey_MixedTypes { + NSDictionary *dict = [BNCJSONUtility stringDictionaryForKey:@"user_dict_mixed" json:self.json]; + XCTAssertNotNil(dict); + XCTAssertTrue(dict.count > 0); +} + +- (void)testStringDictionaryForKey_Numbers { + NSDictionary *dict = [BNCJSONUtility stringDictionaryForKey:@"user_dict_numbers" json:self.json]; + XCTAssertNotNil(dict); + XCTAssertTrue(dict.count == 0); +} + +@end diff --git a/BranchSDKTests/BNCJsonLoader.h b/BranchSDKTests/BNCJsonLoader.h new file mode 100644 index 000000000..6df022300 --- /dev/null +++ b/BranchSDKTests/BNCJsonLoader.h @@ -0,0 +1,20 @@ +// +// BNCJsonLoader.h +// Branch-TestBed +// +// Created by Ernest Cho on 9/16/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BNCJsonLoader : NSObject + +// test utility that loads json files from the Test Bundle. only works on hosted tests ++ (NSDictionary *)dictionaryFromJSONFileNamed:(NSString *)fileName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/BranchSDKTests/BNCJsonLoader.m b/BranchSDKTests/BNCJsonLoader.m new file mode 100644 index 000000000..098798ba8 --- /dev/null +++ b/BranchSDKTests/BNCJsonLoader.m @@ -0,0 +1,27 @@ +// +// BNCJsonLoader.m +// Branch-TestBed +// +// Created by Ernest Cho on 9/16/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import "BNCJsonLoader.h" + +@implementation BNCJsonLoader + ++ (NSDictionary *)dictionaryFromJSONFileNamed:(NSString *)fileName { + + // Since this class is part of the Test target, [self class] returns the Test Bundle + NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:fileName ofType:@"json"]; + + NSString *jsonString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; + + id dict = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; + if ([dict isKindOfClass:NSDictionary.class]) { + return dict; + } + return nil; +} + +@end diff --git a/BranchSDKTests/BNCKeyChainTests.m b/BranchSDKTests/BNCKeyChainTests.m new file mode 100644 index 000000000..07af91a22 --- /dev/null +++ b/BranchSDKTests/BNCKeyChainTests.m @@ -0,0 +1,121 @@ +// +// BNCKeyChainTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 1/6/22. +// Copyright © 2022 Branch, Inc. All rights reserved. +// + +#import +#import "BNCKeyChain.h" + +@interface BNCKeyChainTests : XCTestCase +@property (nonatomic, copy, readwrite) NSString *serviceName; +@end + +@implementation BNCKeyChainTests + +- (void)setUp { + self.serviceName = @"Service"; +} + +- (void)tearDown { + +} + +- (void)testEnvironment { + // Keychain tests must be hosted in an app, otherwise it won't have security access. + XCTAssertFalse([UIApplication sharedApplication] == nil); + + NSString *group = [BNCKeyChain securityAccessGroup]; + XCTAssertTrue(group.length > 0); +} + +- (void)testRemoveValues_Empty { + NSError *error = [BNCKeyChain removeValuesForService:nil key:nil]; + XCTAssertTrue(error == nil); +} + +- (void)testRetrieveDate_Empty { + NSError *error; + NSDate *date = [BNCKeyChain retrieveDateForService:self.serviceName key:@"testKey" error:&error]; + XCTAssertTrue(date == nil && error.code == errSecItemNotFound); +} + +- (void)testStoreAndRetrieveDate { + NSError *error; + NSString *key = @"testKey"; + NSDate *date = [NSDate date]; + + [BNCKeyChain storeDate:date forService:self.serviceName key:key cloudAccessGroup:nil]; + NSDate *tmp = [BNCKeyChain retrieveDateForService:self.serviceName key:key error:&error]; + XCTAssertNil(error); + XCTAssertTrue([date isEqualToDate:tmp]); + + // cleanup + error = [BNCKeyChain removeValuesForService:self.serviceName key:key]; + XCTAssertNil(error); +} + +- (void)testStore_Nil { + NSError *error; + NSString *key = @"testKey"; + NSDate *date = nil; + + error = [BNCKeyChain storeDate:date forService:self.serviceName key:key cloudAccessGroup:nil]; + XCTAssertTrue(error.code == errSecParam); + + NSDate *tmp = [BNCKeyChain retrieveDateForService:self.serviceName key:key error:&error]; + XCTAssertNil(tmp); + XCTAssertTrue(error.code == errSecItemNotFound); +} + +- (void)testStoreAndRetrieveMultipleDates { + NSError *error; + NSString *keyA = @"testKeyA"; + NSString *keyB = @"testKeyB"; + + NSDate *dateA = [NSDate date]; + NSDate *dateB = [NSDate dateWithTimeIntervalSinceNow:1]; + XCTAssertFalse([dateA isEqualToDate:dateB]); + + [BNCKeyChain storeDate:dateA forService:self.serviceName key:keyA cloudAccessGroup:nil]; + [BNCKeyChain storeDate:dateB forService:self.serviceName key:keyB cloudAccessGroup:nil]; + + NSDate *tmpA = [BNCKeyChain retrieveDateForService:self.serviceName key:keyA error:&error]; + XCTAssertNil(error); + XCTAssertTrue([dateA isEqualToDate:tmpA]); + + NSDate *tmpB = [BNCKeyChain retrieveDateForService:self.serviceName key:keyB error:&error]; + XCTAssertNil(error); + XCTAssertTrue([dateB isEqualToDate:tmpB]); + + XCTAssertFalse([tmpA isEqualToDate:tmpB]); + + // cleanup + error = [BNCKeyChain removeValuesForService:self.serviceName key:keyA]; + XCTAssertNil(error); + error = [BNCKeyChain removeValuesForService:self.serviceName key:keyB]; + XCTAssertNil(error); +} + +- (void)testStoreAndRetrieveDate_retrieveWrongKey { + NSError *error; + NSString *keyA = @"testKeyA"; + NSString *keyB = @"testKeyB"; + NSDate *date = [NSDate date]; + + [BNCKeyChain storeDate:date forService:self.serviceName key:keyA cloudAccessGroup:nil]; + NSDate *tmp = [BNCKeyChain retrieveDateForService:self.serviceName key:keyB error:&error]; + XCTAssertNil(tmp); + XCTAssertTrue(error.code == errSecItemNotFound); + + // cleanup + error = [BNCKeyChain removeValuesForService:self.serviceName key:keyA]; + XCTAssertNil(error); + error = [BNCKeyChain removeValuesForService:self.serviceName key:keyB]; + XCTAssertNil(error); +} + + +@end diff --git a/BranchSDKTests/BNCLinkDataTests.m b/BranchSDKTests/BNCLinkDataTests.m new file mode 100644 index 000000000..10eb3cb25 --- /dev/null +++ b/BranchSDKTests/BNCLinkDataTests.m @@ -0,0 +1,103 @@ +// +// BNCLinkDataTests.m +// Branch-TestBed +// +// Created by Graham Mueller on 6/15/15. +// Copyright (c) 2015 Branch Metrics. All rights reserved. +// + +#import +#import "BNCLinkData.h" + +@interface BNCLinkDataTests : XCTestCase +@end + +@implementation BNCLinkDataTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testBasicObjectHash { + BNCLinkData *a = [[BNCLinkData alloc] init]; + BNCLinkData *b = [[BNCLinkData alloc] init]; + + XCTAssertEqual([a hash], [b hash]); +} + +- (void)testObjectHashWithSameValuesForKeys { + NSArray * const TAGS = @[ @"foo-tag" ]; + NSString * const ALIAS = @"foo-alias"; + BranchLinkType const LINK_TYPE = BranchLinkTypeOneTimeUse; + NSString * const CHANNEL = @"foo-channel"; + NSString * const FEATURE = @"foo-feature"; + NSString * const STAGE = @"foo-stage"; + NSDictionary * const PARAMS = @{ @"foo-key": @"foo-value" }; + NSInteger const DURATION = 1; + NSString * const IGNORE_UA = @"foo-ua"; + + BNCLinkData *a = [[BNCLinkData alloc] init]; + [a setupTags:TAGS]; + [a setupAlias:ALIAS]; + [a setupType:LINK_TYPE]; + [a setupChannel:CHANNEL]; + [a setupFeature:FEATURE]; + [a setupStage:STAGE]; + [a setupParams:PARAMS]; + [a setupMatchDuration:DURATION]; + [a setupIgnoreUAString:IGNORE_UA]; + + BNCLinkData *b = [[BNCLinkData alloc] init]; + [b setupTags:TAGS]; + [b setupAlias:ALIAS]; + [b setupType:LINK_TYPE]; + [b setupChannel:CHANNEL]; + [b setupFeature:FEATURE]; + [b setupStage:STAGE]; + [b setupParams:PARAMS]; + [b setupMatchDuration:DURATION]; + [b setupIgnoreUAString:IGNORE_UA]; + + XCTAssertEqual([a hash], [b hash]); +} + +- (void)testObjectHashWithDifferentValuesForSameKeys { + BNCLinkData *a = [[BNCLinkData alloc] init]; + [a setupTags:@[ @"foo-tags" ]]; + [a setupAlias:@"foo-alias"]; + [a setupType:BranchLinkTypeOneTimeUse]; + [a setupChannel:@"foo-channel"]; + [a setupFeature:@"foo-feature"]; + [a setupStage:@"foo-stage"]; + [a setupParams:@{ @"foo-key": @"foo-value" }]; + [a setupMatchDuration:1]; + [a setupIgnoreUAString:@"foo-ua"]; + + BNCLinkData *b = [[BNCLinkData alloc] init]; + [b setupTags:@[ @"bar-tag" ]]; + [b setupAlias:@"bar-alias"]; + [b setupType:BranchLinkTypeUnlimitedUse]; + [b setupChannel:@"bar-channel"]; + [b setupFeature:@"bar-feature"]; + [b setupStage:@"bar-stage"]; + [b setupParams:@{ @"bar-key": @"bar-value" }]; + [b setupMatchDuration:2]; + [b setupIgnoreUAString:@"bar-ua"]; + + XCTAssertNotEqual([a hash], [b hash]); +} + +- (void)testObjectHashWithDifferentCasedValues { + BNCLinkData *a = [[BNCLinkData alloc] init]; + [a setupAlias:@"foo-alias"]; + BNCLinkData *b = [[BNCLinkData alloc] init]; + [b setupAlias:@"FOO-ALIAS"]; + + XCTAssertNotEqual([a hash], [b hash]); +} + +@end diff --git a/BranchSDKTests/BNCNetworkInterfaceTests.m b/BranchSDKTests/BNCNetworkInterfaceTests.m new file mode 100644 index 000000000..8869ecc37 --- /dev/null +++ b/BranchSDKTests/BNCNetworkInterfaceTests.m @@ -0,0 +1,82 @@ +// +// BNCNetworkInterfaceTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 3/10/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import +#import "BNCNetworkInterface.h" + +// Category using inet_pton to validate +@implementation NSString (Test) + +- (BOOL)isValidIPAddress { + const char *utf8 = [self UTF8String]; + int success; + + struct in_addr dst; + success = inet_pton(AF_INET, utf8, &dst); + if (success != 1) { + struct in6_addr dst6; + success = inet_pton(AF_INET6, utf8, &dst6); + } + + return success == 1; +} + +@end + +@interface BNCNetworkInterfaceTests : XCTestCase + +@end + +@implementation BNCNetworkInterfaceTests + +- (void)setUp { + +} + +- (void)tearDown { + +} + +// verify tooling method works +- (void)testIPValidationCategory { + XCTAssert(![@"" isValidIPAddress]); + + // ipv4 + XCTAssert([@"0.0.0.0" isValidIPAddress]); + XCTAssert([@"127.0.0.1" isValidIPAddress]); + XCTAssert([@"10.1.2.3" isValidIPAddress]); + XCTAssert([@"172.0.0.0" isValidIPAddress]); + XCTAssert([@"192.0.0.0" isValidIPAddress]); + XCTAssert([@"255.255.255.255" isValidIPAddress]); + + // invalid ipv4 + XCTAssert(![@"-1.0.0.0" isValidIPAddress]); + XCTAssert(![@"256.0.0.0" isValidIPAddress]); + + // ipv6 + XCTAssert([@"2001:0db8:0000:0000:0000:8a2e:0370:7334" isValidIPAddress]); + XCTAssert([@"2001:db8::8a2e:370:7334" isValidIPAddress]); + + // invalid ipv6 + XCTAssert(![@"2001:0db8:0000:0000:0000:8a2e:0370:733g" isValidIPAddress]); + XCTAssert(![@"2001:0db8:0000:0000:0000:8a2e:0370:7330:1234" isValidIPAddress]); +} + +- (void)testLocalIPAddress { + XCTAssert([[BNCNetworkInterface localIPAddress] isValidIPAddress]); +} + +- (void)testAllIPAddresses { + // All IP addresses is a debug method that returns object descriptions + for (NSString *address in BNCNetworkInterface.allIPAddresses) { + XCTAssert([address containsString:@"BNCNetworkInterface"]); + } +} + +@end diff --git a/BranchSDKTests/BNCODMTests.m b/BranchSDKTests/BNCODMTests.m new file mode 100644 index 000000000..38d1b0cc2 --- /dev/null +++ b/BranchSDKTests/BNCODMTests.m @@ -0,0 +1,102 @@ +// +// BNCODMTests.m +// Branch-SDK-Tests +// +// Created by Nidhi Dixit on 4/16/25. +// Copyright © 2025 Branch, Inc. All rights reserved. +// + +#import +#import "Branch.h" +#import "BNCPreferenceHelper.h" +#import "BNCRequestFactory.h" +#import "BNCEncodingUtils.h" +#import "BNCODMInfoCollector.h" +#import "NSError+Branch.h" + +@interface BNCODMTests : XCTestCase +@property (nonatomic, strong, readwrite) BNCPreferenceHelper *prefHelper; +@end + +@implementation BNCODMTests + +- (void)setUp { + _prefHelper = [BNCPreferenceHelper sharedInstance]; +} + +- (void)testSetODM { + NSString *odm = @"testODMString"; + NSDate *firstOpenTS = [NSDate date]; + [Branch setODMInfo:odm andFirstOpenTimestamp:firstOpenTS]; + XCTAssertTrue([_prefHelper.odmInfo isEqualToString:odm]); + XCTAssertTrue([_prefHelper.odmInfoInitDate isEqualToDate:firstOpenTS]); + +} + +- (void)testSetODMandSDKRequests { + NSString* requestUUID = [[NSUUID UUID ] UUIDString]; + NSNumber* requestCreationTimeStamp = BNCWireFormatFromDate([NSDate date]); + NSString *odm = @"testODMString"; + NSDate *firstOpenTS = [NSDate date]; + + [Branch setODMInfo:odm andFirstOpenTimestamp:firstOpenTS]; + + [[Branch getInstance] setConsumerProtectionAttributionLevel:BranchAttributionLevelFull]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:requestUUID TimeStamp:requestCreationTimeStamp]; + NSDictionary *jsonInstall = [factory dataForInstallWithURLString:@"https://branch.io"]; + XCTAssertTrue([odm isEqualToString:[jsonInstall objectForKey:@"odm_info"]]); + + NSDictionary *jsonOpen = [factory dataForOpenWithURLString:@"https://branch.io"]; + XCTAssertTrue([odm isEqualToString:[jsonOpen objectForKey:@"odm_info"]]); + + NSDictionary *event = @{@"name": @"ADD_TO_CART"}; + NSDictionary *jsonEvent = [factory dataForEventWithEventDictionary:[event mutableCopy]]; + XCTAssertTrue([jsonEvent objectForKey:@"odm_info"] == nil); + + [[Branch getInstance] setConsumerProtectionAttributionLevel:BranchAttributionLevelReduced]; + jsonInstall = [factory dataForInstallWithURLString:@"https://branch.io"]; + XCTAssertTrue([jsonInstall objectForKey:@"odm_info"] == nil); + + jsonOpen = [factory dataForOpenWithURLString:@"https://branch.io"]; + XCTAssertTrue([jsonOpen objectForKey:@"odm_info"] == nil); + + self.prefHelper.odmInfo = nil; + self.prefHelper.odmInfoInitDate = nil; +} + +- (void)testODMTimeOut { + + NSString* requestUUID = [[NSUUID UUID ] UUIDString]; + NSNumber* requestCreationTimeStamp = BNCWireFormatFromDate([NSDate date]); + NSString *odm = @"testODMString"; + NSDate *firstOpenTS = [[NSDate date] dateByAddingTimeInterval:-((180*24*3600) - 5)]; + + [Branch setODMInfo:odm andFirstOpenTimestamp:firstOpenTS]; + + [[Branch getInstance] setConsumerProtectionAttributionLevel:BranchAttributionLevelFull]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:requestUUID TimeStamp:requestCreationTimeStamp]; + NSDictionary *jsonInstall = [factory dataForInstallWithURLString:@"https://branch.io"]; + XCTAssertTrue([odm isEqualToString:[jsonInstall objectForKey:@"odm_info"]]); + + sleep(10); + + NSDictionary *jsonOpen = [factory 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]; + } + }]; + [self waitForExpectationsWithTimeout:15 handler:nil]; +} + +@end diff --git a/BranchSDKTests/BNCPartnerParametersTests.m b/BranchSDKTests/BNCPartnerParametersTests.m new file mode 100644 index 000000000..7fad38ac2 --- /dev/null +++ b/BranchSDKTests/BNCPartnerParametersTests.m @@ -0,0 +1,219 @@ +// +// BNCPartnerParametersTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 12/9/20. +// Copyright © 2020 Branch, Inc. All rights reserved. +// + +#import +#import "BNCPartnerParameters.h" + +// expose private methods for testing +@interface BNCPartnerParameters() +- (BOOL)sha256HashSanityCheckValue:(NSString *)value; +- (BOOL)isStringHex:(NSString *)string; +@end + +@interface BNCPartnerParametersTests : XCTestCase +@property (nonatomic, strong, readwrite) BNCPartnerParameters *partnerParams; +@end + +@implementation BNCPartnerParametersTests + +- (void)setUp { + self.partnerParams = [BNCPartnerParameters new]; +} + +- (void)tearDown { + +} + +- (void)testStringHexNil { + XCTAssertFalse([self.partnerParams isStringHex:nil]); +} + +- (void)testStringHexEmpty { + XCTAssertTrue([self.partnerParams isStringHex:@""]); +} + +- (void)testStringHexDash { + XCTAssertFalse([self.partnerParams isStringHex:@"-1"]); +} + +- (void)testStringHexDecimal { + XCTAssertFalse([self.partnerParams isStringHex:@"1.0"]); +} + +- (void)testStringHexFraction { + XCTAssertFalse([self.partnerParams isStringHex:@"2/4"]); +} + +- (void)testStringHexAt { + XCTAssertFalse([self.partnerParams isStringHex:@"test@12345"]); +} + +- (void)testStringHexUpperG { + XCTAssertFalse([self.partnerParams isStringHex:@"0123456789ABCDEFG"]); +} + +- (void)testStringHexLowerG { + XCTAssertFalse([self.partnerParams isStringHex:@"0123456789abcdefg"]); +} + +- (void)testStringHexUpperCase { + XCTAssertTrue([self.partnerParams isStringHex:@"0123456789ABCDEF"]); +} + +- (void)testStringHexLowerCase { + XCTAssertTrue([self.partnerParams isStringHex:@"0123456789abcdef"]); +} + +- (void)testSha256HashSanityCheckValueNil { + XCTAssertFalse([self.partnerParams sha256HashSanityCheckValue:nil]); +} + +- (void)testSha256HashSanityCheckValueEmpty { + XCTAssertFalse([self.partnerParams sha256HashSanityCheckValue:@""]); +} + +- (void)testSha256HashSanityCheckValueTooShort { + // 63 char string + XCTAssertFalse([self.partnerParams sha256HashSanityCheckValue:@"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcde"]); +} + +- (void)testSha256HashSanityCheckValueTooLong { + // 65 char string + XCTAssertFalse([self.partnerParams sha256HashSanityCheckValue:@"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdeff"]); +} + +- (void)testSha256HashSanityCheckValueLowerCase { + // 64 char string + XCTAssertTrue([self.partnerParams sha256HashSanityCheckValue:@"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"]); +} + +- (void)testSha256HashSanityCheckValueUpperCase { + // 64 char string + XCTAssertTrue([self.partnerParams sha256HashSanityCheckValue:@"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"]); +} + +- (void)testSha256HashSanityCheckValueMixedCase { + // 64 char string + XCTAssertTrue([self.partnerParams sha256HashSanityCheckValue:@"0123456789ABCDEF0123456789ABCDEF1234567890abcdef1234567890abcdef"]); +} + +- (void)testJsonEmpty { + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +- (void)testJsonFBParameterEmpty { + [self.partnerParams addFacebookParameterWithName:@"em" value:@""]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +- (void)testJsonFBParameterShort { + [self.partnerParams addFacebookParameterWithName:@"em" value:@"0123456789ABCDEF0123456789ABCDEF1234567890abcdef1234567890abcde"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +- (void)testJsonFBParameterPhoneNumberIsIgnored { + [self.partnerParams addFacebookParameterWithName:@"em" value:@"1-555-555-5555"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +- (void)testJsonFBParameterEmailIsIgnored { + [self.partnerParams addFacebookParameterWithName:@"em" value:@"test@branch.io"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +- (void)testJsonFBParameterBase64EncodedIsIgnored { + // 123456789012345678901234567890123456789012345678 -> MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4 + [self.partnerParams addFacebookParameterWithName:@"em" value:@"MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +- (void)testJsonFBParameterHashedValue { + [self.partnerParams addFacebookParameterWithName:@"em" value:@"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{\"fb\":{\"em\":\"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088\"}}" isEqualToString:jsonString]); +} + +- (void)testJsonFBParameterExample { + [self.partnerParams addFacebookParameterWithName:@"em" value:@"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088"]; + [self.partnerParams addFacebookParameterWithName:@"ph" value:@"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + + XCTAssertTrue([@"{\"fb\":{\"ph\":\"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b\",\"em\":\"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088\"}}" isEqualToString:jsonString]); +} + +- (void)testJsonSnapParameterExample { + [self.partnerParams addSnapParameterWithName:@"hashed_email_address" value:@"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088"]; + [self.partnerParams addSnapParameterWithName:@"hashed_phone_number" value:@"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + + XCTAssertTrue([@"{\"snap\":{\"hashed_phone_number\":\"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b\",\"hashed_email_address\":\"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088\"}}" isEqualToString:jsonString]); +} + + +- (void)testJsonMultipleParameterExample { + [self.partnerParams addFacebookParameterWithName:@"em" value:@"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088"]; + [self.partnerParams addFacebookParameterWithName:@"ph" value:@"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b"]; + [self.partnerParams addSnapParameterWithName:@"hashed_email_address" value:@"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088"]; + [self.partnerParams addSnapParameterWithName:@"hashed_phone_number" value:@"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b"]; + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + + NSString *expectedJsonString = @"{\"snap\":{\"hashed_phone_number\":\"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b\",\"hashed_email_address\":\"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088\"},\"fb\":{\"ph\":\"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b\",\"em\":\"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088\"}}"; + + XCTAssertTrue([expectedJsonString isEqualToString:jsonString]); +} + +- (void)testParameterClear { + [self.partnerParams addFacebookParameterWithName:@"em" value:@"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088"]; + [self.partnerParams addFacebookParameterWithName:@"ph" value:@"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b"]; + [self.partnerParams addSnapParameterWithName:@"hashed_email_address" value:@"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088"]; + [self.partnerParams addSnapParameterWithName:@"hashed_phone_number" value:@"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b"]; + [self.partnerParams clearAllParameters]; + + NSString *jsonString = [self jsonStringFromDictionary:[self.partnerParams parameterJson]]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +// sanity check test func on an empty dictionary +- (void)testEmptyJson { + NSString *jsonString = [self jsonStringFromDictionary:@{}]; + XCTAssertTrue([@"{}" isEqualToString:jsonString]); +} + +// sanity check test func on the sample json dictionary +- (void)testSampleJson { + NSString *jsonString = [self jsonStringFromDictionary:@{ + @"fb": @{ + @"ph": @"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b", + @"em": @"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088" + } + }]; + + XCTAssertTrue([@"{\"fb\":{\"ph\":\"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b\",\"em\":\"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088\"}}" isEqualToString:jsonString] || [@"{\"fb\":{\"em\":\"11234e56af071e9c79927651156bd7a10bca8ac34672aba121056e2698ee7088\",\"ph\":\"b90598b67534f00b1e3e68e8006631a40d24fba37a3a34e2b84922f1f0b3b29b\"}}" isEqualToString:jsonString]); +} + +// There is an assumption that this code always results in the same string for the same json data. +// This appears to be true, but I haven't found documentation to confirm it. +- (NSString *)jsonStringFromDictionary:(NSDictionary *)dictionary { + NSError *error; + NSData *json = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error]; + + if (!error) { + NSString *tmp = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding]; + return tmp; + } else { + return @""; + } +} + +@end diff --git a/BranchSDKTests/BNCPasteboardTests.m b/BranchSDKTests/BNCPasteboardTests.m new file mode 100644 index 000000000..6a3a4d69c --- /dev/null +++ b/BranchSDKTests/BNCPasteboardTests.m @@ -0,0 +1,170 @@ +// +// BNCPasteboardTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 7/19/21. +// Copyright © 2021 Branch, Inc. All rights reserved. +// + +#import +#import "BNCPasteboard.h" +#import "Branch.h" + +@interface BNCPasteboardTests : XCTestCase + +@property (nonatomic, assign, readwrite) NSString *testString; +@property (nonatomic, strong, readwrite) NSURL *testBranchURL; + +@end + +@implementation BNCPasteboardTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. + self.testString = @"Pasteboard String"; + self.testBranchURL = [NSURL URLWithString:@"https://123.app.link"]; +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)addStringToPasteboard { +#if !TARGET_OS_TV + [UIPasteboard.generalPasteboard setString:self.testString]; +#endif +} + +- (void)addBranchURLToPasteboard { +#if !TARGET_OS_TV + [UIPasteboard.generalPasteboard setURL:self.testBranchURL]; +#endif +} + +- (void)addNonBranchURLToPasteboard { +#if !TARGET_OS_TV + [UIPasteboard.generalPasteboard setURL:[NSURL URLWithString:@"https://www.apple.com"]]; +#endif +} + +- (void)clearPasteboard { +#if !TARGET_OS_TV + // cannot delete items from the pasteboard, but we can put something else on there + [[UIPasteboard generalPasteboard] setString:@""]; +#endif +} + +- (NSString *)getStringFromClipboard { + NSString *string = nil; +#if !TARGET_OS_TV + string = [UIPasteboard.generalPasteboard string]; +#endif + return string; +} + +- (NSURL *)getURLFromPasteboard { + NSURL *url = nil; +#if !TARGET_OS_TV + url = [UIPasteboard.generalPasteboard URL]; +#endif + return url; +} + +- (void)testStringUtilityMethods { + + // set and retrieve a string + [self addStringToPasteboard]; + NSString *tmp = [self getStringFromClipboard]; + XCTAssert([self.testString isEqualToString:tmp]); + + // overwrite the pasteboard + [self clearPasteboard]; + tmp = [self getStringFromClipboard]; + XCTAssert([@"" isEqualToString:tmp]); +} + +- (void)testURLUtilityMethods { + + // set and retrieve a url + [self addBranchURLToPasteboard]; + NSURL *tmp = [self getURLFromPasteboard]; + XCTAssert([self.testBranchURL.absoluteString isEqualToString:tmp.absoluteString]); + + // overwrite the pasteboard + [self clearPasteboard]; + tmp = [self getURLFromPasteboard]; + XCTAssertNil(tmp); +} + +- (void)testDefaultState { + // host app sets this to true, should consider a no-op test host + XCTAssertFalse([BNCPasteboard sharedInstance].checkOnInstall); +} + +- (void)testIsUrlOnPasteboard { + XCTAssertFalse([[BNCPasteboard sharedInstance] isUrlOnPasteboard]); + + [self addBranchURLToPasteboard]; + XCTAssertTrue([[BNCPasteboard sharedInstance] isUrlOnPasteboard]); + + [self clearPasteboard]; + XCTAssertFalse([[BNCPasteboard sharedInstance] isUrlOnPasteboard]); +} + +- (void)testCheckForBranchLink { + [self addBranchURLToPasteboard]; + XCTAssertTrue([[BNCPasteboard sharedInstance] isUrlOnPasteboard]); + + NSURL *tmp = [[BNCPasteboard sharedInstance] checkForBranchLink]; + XCTAssert([self.testBranchURL.absoluteString isEqualToString:tmp.absoluteString]); + + [self clearPasteboard]; +} + +- (void)testCheckForBranchLink_nonBranchLink { + [self addNonBranchURLToPasteboard]; + XCTAssertTrue([[BNCPasteboard sharedInstance] isUrlOnPasteboard]); + + NSURL *tmp = [[BNCPasteboard sharedInstance] checkForBranchLink]; + XCTAssertNil(tmp); + + [self clearPasteboard]; +} + +- (void)testCheckForBranchLink_noLink { + [self addStringToPasteboard]; + XCTAssertFalse([[BNCPasteboard sharedInstance] isUrlOnPasteboard]); + + NSURL *tmp = [[BNCPasteboard sharedInstance] checkForBranchLink]; + XCTAssertNil(tmp); + + [self clearPasteboard]; +} + +#if 0 +// This test fails intermittently when executed with other tests - depending upon the order in which its executed +- (void) testPassPasteControl { +#if !TARGET_OS_TV + if (@available(iOS 16.0, macCatalyst 16.0, *)) { + + long long timeStamp = ([[NSDate date] timeIntervalSince1970] - 5*60)*1000; // 5 minute earlier timestamp + NSString *urlString = [NSString stringWithFormat:@"https://bnctestbed-alternate.app.link/9R7MbTmnRtb?__branch_flow_type=viewapp&__branch_flow_id=1105940563590163783&__branch_mobile_deepview_type=1&nl_opt_in=1&_cpts=%lld", timeStamp]; + NSURL *testURL = [[NSURL alloc] initWithString:urlString]; + + NSArray *itemProviders = @[[[NSItemProvider alloc] initWithItem:testURL typeIdentifier:UTTypeURL.identifier]]; + XCTestExpectation *openExpectation = [self expectationWithDescription:@"Test open"]; + + [[Branch getInstance] initSessionWithLaunchOptions:@{} andRegisterDeepLinkHandler:^(NSDictionary *params, NSError *error) { + [openExpectation fulfill]; + XCTAssertNil(error); + }]; + + [[Branch getInstance] passPasteItemProviders:itemProviders]; + [self waitForExpectationsWithTimeout:5.0 handler:NULL]; + + } +#endif +} +#endif + +@end diff --git a/BranchSDKTests/BNCPreferenceHelperTests.m b/BranchSDKTests/BNCPreferenceHelperTests.m new file mode 100644 index 000000000..5269501b3 --- /dev/null +++ b/BranchSDKTests/BNCPreferenceHelperTests.m @@ -0,0 +1,417 @@ +// +// BNCPreferenceHelperTests.m +// Branch-TestBed +// +// Created by Graham Mueller on 4/2/15. +// Copyright (c) 2015 Branch Metrics. All rights reserved. +// + +#import +#import "BNCPreferenceHelper.h" +#import "BNCEncodingUtils.h" +#import "Branch.h" +#import "BNCConfig.h" + +@interface BNCPreferenceHelper() + +// expose private methods for testing +- (NSMutableDictionary *)deserializePrefDictFromData:(NSData *)data; +- (NSData *)serializePrefDict:(NSMutableDictionary *)dict; + +@end + +@interface BNCPreferenceHelperTests : XCTestCase +@property (nonatomic, strong, readwrite) BNCPreferenceHelper *prefHelper; +@end + +@implementation BNCPreferenceHelperTests + +- (void)setUp { + self.prefHelper = [BNCPreferenceHelper new]; +} + +- (void)tearDown { + +} + +- (void)testPreferenceDefaults { + XCTAssertEqual(self.prefHelper.timeout, 5.5); + XCTAssertEqual(self.prefHelper.retryInterval, 0); + XCTAssertEqual(self.prefHelper.retryCount, 3); + XCTAssertFalse(self.prefHelper.disableAdNetworkCallouts); +} + +- (void)testPreferenceSets { + self.prefHelper.retryCount = NSIntegerMax; + self.prefHelper.retryInterval = NSIntegerMax; + self.prefHelper.timeout = NSIntegerMax; + + XCTAssertEqual(self.prefHelper.retryCount, NSIntegerMax); + XCTAssertEqual(self.prefHelper.retryInterval, NSIntegerMax); + XCTAssertEqual(self.prefHelper.timeout, NSIntegerMax); +} + +// This test is not reliable when run concurrently with other tests that set the patterListURL +- (void)testURLFilter { + XCTAssertTrue([@"https://cdn.branch.io" isEqualToString:self.prefHelper.patternListURL]); + + NSString *customURL = @"https://banned.branch.io"; + self.prefHelper.patternListURL = customURL; + XCTAssertTrue([customURL isEqualToString:self.prefHelper.patternListURL]); +} + +- (void)testSerializeDict_Nil { + NSMutableDictionary *dict = nil; + NSData *data = [self.prefHelper serializePrefDict:dict]; + XCTAssert(data == nil); +} + +- (void)testSerializeDict_Empty { + NSMutableDictionary *dict = [NSMutableDictionary new]; + NSData *data = [self.prefHelper serializePrefDict:dict]; + NSMutableDictionary *tmp = [self.prefHelper deserializePrefDictFromData:data]; + + XCTAssert(tmp != nil); + XCTAssert([tmp isKindOfClass:NSMutableDictionary.class]); + XCTAssert(tmp.count == 0); +} + +- (void)testSerializeDict_String { + NSMutableDictionary *dict = [NSMutableDictionary new]; + NSString *value = @"the quick brown fox jumps over the lazy dog"; + NSString *key = @"test"; + [dict setObject:value forKey:key]; + + NSData *data = [self.prefHelper serializePrefDict:dict]; + NSMutableDictionary *tmp = [self.prefHelper deserializePrefDictFromData:data]; + + XCTAssert(tmp != nil); + XCTAssert([tmp isKindOfClass:NSMutableDictionary.class]); + XCTAssert(tmp.count == 1); + + XCTAssert([[tmp objectForKey:key] isEqualToString:value]); +} + +- (void)testSerializeDict_Date { + NSMutableDictionary *dict = [NSMutableDictionary new]; + NSDate *value = [NSDate date]; + NSString *key = @"test"; + [dict setObject:value forKey:key]; + + NSData *data = [self.prefHelper serializePrefDict:dict]; + NSMutableDictionary *tmp = [self.prefHelper deserializePrefDictFromData:data]; + + XCTAssert(tmp != nil); + XCTAssert([tmp isKindOfClass:NSMutableDictionary.class]); + XCTAssert(tmp.count == 1); + + XCTAssert([[tmp objectForKey:key] isEqual:value]); +} + +- (void)testSerializeDict_Bool { + NSMutableDictionary *dict = [NSMutableDictionary new]; + bool value = YES; + NSString *key = @"test"; + [dict setObject:@(value) forKey:key]; + + NSData *data = [self.prefHelper serializePrefDict:dict]; + NSMutableDictionary *tmp = [self.prefHelper deserializePrefDictFromData:data]; + + XCTAssert(tmp != nil); + XCTAssert([tmp isKindOfClass:NSMutableDictionary.class]); + XCTAssert(tmp.count == 1); + + XCTAssert([[tmp objectForKey:key] isEqual:@(value)]); +} + +- (void)testSerializeDict_Integer { + NSMutableDictionary *dict = [NSMutableDictionary new]; + NSInteger value = 1234; + NSString *key = @"test"; + [dict setObject:@(value) forKey:key]; + + NSData *data = [self.prefHelper serializePrefDict:dict]; + NSMutableDictionary *tmp = [self.prefHelper deserializePrefDictFromData:data]; + + XCTAssert(tmp != nil); + XCTAssert([tmp isKindOfClass:NSMutableDictionary.class]); + XCTAssert(tmp.count == 1); + + XCTAssert([[tmp objectForKey:key] isEqual:@(value)]); +} + +- (void)testSerializeDict_All { + NSMutableDictionary *dict = [NSMutableDictionary new]; + + NSString *value1 = @"the quick brown fox jumps over the lazy dog"; + NSString *key1 = @"test1"; + [dict setObject:value1 forKey:key1]; + + NSDate *value2 = [NSDate date]; + NSString *key2 = @"test2"; + [dict setObject:value2 forKey:key2]; + + bool value3 = YES; + NSString *key3 = @"test3"; + [dict setObject:@(value3) forKey:key3]; + + NSInteger value4 = 1234; + NSString *key4 = @"test4"; + [dict setObject:@(value4) forKey:key4]; + + NSData *data = [self.prefHelper serializePrefDict:dict]; + NSMutableDictionary *tmp = [self.prefHelper deserializePrefDictFromData:data]; + + XCTAssert(tmp != nil); + XCTAssert([tmp isKindOfClass:NSMutableDictionary.class]); + XCTAssert(tmp.count == 4); + + XCTAssert([[tmp objectForKey:key1] isEqualToString:value1]); + XCTAssert([[tmp objectForKey:key2] isEqual:value2]); + XCTAssert([[tmp objectForKey:key3] isEqual:@(value3)]); + XCTAssert([[tmp objectForKey:key4] isEqual:@(value4)]); +} + +- (void)testURLSkipList { + NSMutableDictionary *dict = [NSMutableDictionary new]; + NSString *key = @"test"; + NSArray *value = @[ + @"^fb\\d+:", + @"^li\\d+:", + @"^pdk\\d+:", + @"^twitterkit-.*:", + @"^com\\.googleusercontent\\.apps\\.\\d+-.*:\\/oauth", + @"^(?i)(?!(http|https):).*(:|:.*\\b)(password|o?auth|o?auth.?token|access|access.?token)\\b", + @"^(?i)((http|https):\\/\\/).*[\\/|?|#].*\\b(password|o?auth|o?auth.?token|access|access.?token)\\b", + ]; + [dict setObject:value forKey:key]; + NSData *data = [self.prefHelper serializePrefDict:dict]; + + NSMutableDictionary *tmp = [self.prefHelper deserializePrefDictFromData:data]; + + XCTAssert(tmp != nil); + XCTAssert([tmp isKindOfClass:NSMutableDictionary.class]); + + NSArray *filter = [tmp objectForKey:key]; + + NSString *filterDesc = filter.description; + NSString *valueDesc = value.description; + XCTAssert([filterDesc isEqualToString:valueDesc]); +} + +- (void)testSetCDNBaseURL_Example { + + NSString *url = @"https://www.example.com/"; + [self.prefHelper setPatternListURL:url]; + + NSString *urlStored = self.prefHelper.patternListURL ; + XCTAssert([url isEqualToString:urlStored]); +} + +- (void)testSetCDNBaseURL_InvalidHttp { + + NSString *url = @"Invalid://www.example.com/"; + [self.prefHelper setPatternListURL:url] ; + + NSString *urlStored = self.prefHelper.patternListURL ; + XCTAssert(![url isEqualToString:urlStored]); + XCTAssert([urlStored isEqualToString:BNC_CDN_URL]); +} + +- (void)testSetCDNBaseURL_InvalidEmpty { + + [self.prefHelper setPatternListURL:@""] ; + + NSString *urlStored = self.prefHelper.patternListURL ; + XCTAssert(![urlStored isEqualToString:@""]); + XCTAssert([urlStored isEqualToString:BNC_CDN_URL]); +} + +- (void)testSetPatternListURL { + NSString *expectedURL = @"https://example.com"; + [self.prefHelper setPatternListURL:expectedURL]; + + NSString *patternListURL = self.prefHelper.patternListURL; + XCTAssert([patternListURL isEqualToString: expectedURL]); +} + +- (void)testSetLastStrongMatchDate { + NSDate *expectedDate = [NSDate date]; + [self.prefHelper setLastStrongMatchDate: expectedDate]; + + NSDate *actualDate = [self.prefHelper lastStrongMatchDate]; + XCTAssertEqualObjects(expectedDate, actualDate); +} + +- (void)testSetAppVersion { + NSString *expectedVersion = @"1.0.0"; + [self.prefHelper setAppVersion: expectedVersion]; + + NSString *actualVersion = [self.prefHelper appVersion]; + XCTAssertEqualObjects(expectedVersion, actualVersion); +} + +- (void)testSetLocalUrl { + NSString *expectedLocalURL = @"https://local.example.com"; + [self.prefHelper setLocalUrl:expectedLocalURL]; + + NSString *localURL = [self.prefHelper localUrl]; + XCTAssertEqualObjects(localURL, expectedLocalURL); +} + +- (void)testSetInitialReferrer { + NSString *expectedReferrer = @"referrer.example.com"; + [self.prefHelper setInitialReferrer:expectedReferrer]; + + NSString *actualReferrer = [self.prefHelper initialReferrer]; + XCTAssertEqualObjects(actualReferrer, expectedReferrer); +} + +- (void)testSetAppleAttributionTokenChecked { + BOOL expectedValue = YES; + [self.prefHelper setAppleAttributionTokenChecked:expectedValue]; + + BOOL actualValue = [self.prefHelper appleAttributionTokenChecked]; + XCTAssertEqual(expectedValue, actualValue); +} + +- (void)testSetHasOptedInBefore { + BOOL expectedValue = YES; + [self.prefHelper setHasOptedInBefore:expectedValue]; + + BOOL actualValue = [self.prefHelper hasOptedInBefore]; + XCTAssertEqual(expectedValue, actualValue); +} + +- (void)testSetHasCalledHandleATTAuthorizationStatus { + BOOL expectedValue = YES; + [self.prefHelper setHasCalledHandleATTAuthorizationStatus:expectedValue]; + + BOOL actualValue = [self.prefHelper hasCalledHandleATTAuthorizationStatus]; + XCTAssertEqual(expectedValue, actualValue); +} + +- (void)testSetRequestMetadataKeyValidKeyValue { + NSString *key = @"testKey"; + NSString *value = @"testValue"; + + [self.prefHelper setRequestMetadataKey:key value:value]; + + NSObject *retrievedValue = [self.prefHelper.requestMetadataDictionary objectForKey:key]; + XCTAssertEqualObjects(retrievedValue, value); +} + +- (void)testSetRequestMetadataKeyValidKeyNilValue { + NSString *key = @"testKey"; + NSString *value = @"testValue"; + + [self.prefHelper.requestMetadataDictionary setObject:value forKey:key]; + + [self.prefHelper setRequestMetadataKey:key value:nil]; + + NSObject *retrievedValue = [self.prefHelper.requestMetadataDictionary objectForKey:key]; + XCTAssertNil(retrievedValue); +} + +- (void)testSetRequestMetadataKeyValidKeyNilValueKeyNotExists { + NSString *key = @"testKeyNotExists"; + + NSUInteger initialDictCount = [self.prefHelper.requestMetadataDictionary count]; + + [self.prefHelper setRequestMetadataKey:key value:nil]; + + NSUInteger postActionDictCount = [self.prefHelper.requestMetadataDictionary count]; + XCTAssertEqual(initialDictCount, postActionDictCount); +} + +- (void)testSetRequestMetadataKeyNilKey { + NSString *value = @"testValue"; + NSUInteger initialDictCount = [self.prefHelper.requestMetadataDictionary count]; + + [self.prefHelper setRequestMetadataKey:nil value:value]; + + NSUInteger postActionDictCount = [self.prefHelper.requestMetadataDictionary count]; + XCTAssertEqual(initialDictCount, postActionDictCount); +} + +- (void)testSetLimitFacebookTracking { + BOOL expectedValue = YES; + + [self.prefHelper setLimitFacebookTracking:expectedValue]; + + BOOL storedValue = [self.prefHelper limitFacebookTracking]; + + XCTAssertEqual(expectedValue, storedValue); +} + +- (void)testSetTrackingDisabled_YES { + [self.prefHelper setTrackingDisabled:YES]; + + BOOL storedValue = [self.prefHelper trackingDisabled]; + XCTAssertTrue(storedValue); + [self.prefHelper setTrackingDisabled:NO]; +} + +- (void)testSetTrackingDisabled_NO { + [self.prefHelper setTrackingDisabled:NO]; + + BOOL storedValue = [self.prefHelper trackingDisabled]; + XCTAssertFalse(storedValue); +} + +// TODO: rethink this test as these values are not set in a freshly instantiated prefHelper +- (void)testClearTrackingInformation { + [self.prefHelper clearTrackingInformation]; + + XCTAssertNil(self.prefHelper.sessionID); + XCTAssertNil(self.prefHelper.linkClickIdentifier); + XCTAssertNil(self.prefHelper.spotlightIdentifier); + XCTAssertNil(self.prefHelper.referringURL); + XCTAssertNil(self.prefHelper.universalLinkUrl); + XCTAssertNil(self.prefHelper.initialReferrer); + XCTAssertNil(self.prefHelper.installParams); + XCTAssertNil(self.prefHelper.sessionParams); + XCTAssertNil(self.prefHelper.externalIntentURI); + XCTAssertNil(self.prefHelper.savedAnalyticsData); + XCTAssertNil(self.prefHelper.previousAppBuildDate); + XCTAssertEqual(self.prefHelper.requestMetadataDictionary.count, 0); + XCTAssertNil(self.prefHelper.lastStrongMatchDate); + XCTAssertNil(self.prefHelper.userIdentity); + XCTAssertNil(self.prefHelper.referringURLQueryParameters); + XCTAssertNil(self.prefHelper.anonID); +} + +- (void)testSaveBranchAnalyticsData { + NSString *dummySessionID = @"testSession123"; + NSDictionary *dummyAnalyticsData = @{ @"key1": @"value1", @"key2": @"value2" }; + + self.prefHelper.sessionID = dummySessionID; + + [self.prefHelper saveBranchAnalyticsData:dummyAnalyticsData]; + + NSMutableDictionary *retrievedData = [self.prefHelper getBranchAnalyticsData]; + + NSArray *viewDataArray = [retrievedData objectForKey:dummySessionID]; + XCTAssertNotNil(viewDataArray); + XCTAssertEqual(viewDataArray.count, 1); + XCTAssertEqualObjects(viewDataArray.firstObject, dummyAnalyticsData); +} + +- (void)testClearBranchAnalyticsData { + [self.prefHelper clearBranchAnalyticsData]; + + NSMutableDictionary *retrievedData = [self.prefHelper getBranchAnalyticsData]; + XCTAssertEqual(retrievedData.count, 0); +} + +- (void)testSaveContentAnalyticsManifest { + NSDictionary *dummyManifest = @{ @"manifestKey1": @"manifestValue1", @"manifestKey2": @"manifestValue2" }; + + [self.prefHelper saveContentAnalyticsManifest:dummyManifest]; + + NSDictionary *retrievedManifest = [self.prefHelper getContentAnalyticsManifest]; + + XCTAssertEqualObjects(retrievedManifest, dummyManifest); +} + +@end diff --git a/BranchSDKTests/BNCReachabilityTests.m b/BranchSDKTests/BNCReachabilityTests.m new file mode 100644 index 000000000..3f159c1e3 --- /dev/null +++ b/BranchSDKTests/BNCReachabilityTests.m @@ -0,0 +1,45 @@ +// +// BNCReachabilityTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 11/18/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +#import "BNCReachability.h" + +@interface BNCReachabilityTests : XCTestCase +@property (nonatomic, strong, readwrite) BNCReachability *reachability; +@end + +@implementation BNCReachabilityTests + +- (void)setUp { + self.reachability = [BNCReachability new]; +} + +- (void)tearDown { + +} + +- (void)testSimulator_WIFI { + NSString *status = [self.reachability reachabilityStatus]; + XCTAssertNotNil(status); + XCTAssert([@"wifi" isEqualToString:status]); +} + +// Only works on a device with cell +//- (void)testDevice_Cell { +// NSString *status = [self.reachability reachabilityStatus]; +// XCTAssertNotNil(status); +// XCTAssert([@"mobile" isEqualToString:status]); +//} + +// Only works on a device in Airplane mode +//- (void)testDevice_AirplaneMode { +// NSString *status = [self.reachability reachabilityStatus]; +// XCTAssertNil(status); +//} + +@end diff --git a/BranchSDKTests/BNCReferringURLUtilityTests.m b/BranchSDKTests/BNCReferringURLUtilityTests.m new file mode 100644 index 000000000..b76f91b4d --- /dev/null +++ b/BranchSDKTests/BNCReferringURLUtilityTests.m @@ -0,0 +1,538 @@ +// +// BNCReferringURLUtilityTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 3/9/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import "BNCReferringURLUtility.h" +#import "BNCUrlQueryParameter.h" +#import "BNCPreferenceHelper.h" + +@interface BNCReferringURLUtility(Test) +// expose the private data structure so tests can clear it +@property (strong, readwrite, nonatomic) NSMutableDictionary *urlQueryParameters; + +// expose private methods to test data migration +- (void)checkForAndMigrateOldGbraid; +@end + +@interface BNCReferringURLUtilityTests : XCTestCase + +@end + +@implementation BNCReferringURLUtilityTests + +// test constants +static NSString *openEndpoint = @"/v1/open"; +static NSString *eventEndpoint = @"/v2/event"; + ++ (void)tearDown { + // clear test data from global storage + [BNCPreferenceHelper sharedInstance].referringURLQueryParameters = nil; + [BNCPreferenceHelper sharedInstance].referrerGBRAID = nil; + [BNCPreferenceHelper sharedInstance].referrerGBRAIDValidityWindow = 0; + [BNCPreferenceHelper sharedInstance].referrerGBRAIDInitDate = nil; +} + +// workaround for BNCPreferenceHelper being persistent across tests and not currently mockable +- (BNCReferringURLUtility *)referringUtilityForTests { + BNCReferringURLUtility *utility = [BNCReferringURLUtility new]; + utility.urlQueryParameters = [NSMutableDictionary new]; + return utility; +} + +// make gbraid equality check simpler by excluding timestamp +- (NSDictionary *)removeTimestampFromParams:(NSDictionary *)params { + NSMutableDictionary *paramsWithoutTimestamp = [params mutableCopy]; + paramsWithoutTimestamp[@"gbraid_timestamp"] = nil; + return paramsWithoutTimestamp; +} + +// gbraid timestamp is a string representing time in millis +- (void)validateGbraidTimestampInReferringParameters:(NSDictionary *)params { + id timestamp = params[@"gbraid_timestamp"]; + XCTAssert(timestamp != nil); + XCTAssert([timestamp isKindOfClass:NSString.class]); +} + +- (void)expireValidityWindowsInUtility:(BNCReferringURLUtility *)utility { + for (NSString *paramName in utility.urlQueryParameters.allKeys) { + BNCUrlQueryParameter *param = utility.urlQueryParameters[paramName]; + + // currently the longest validity window is 30 days + NSTimeInterval sixtyDaysAgo = -1 * 60 * 24 * 60 * 60; + param.timestamp = [NSDate dateWithTimeIntervalSinceNow:sixtyDaysAgo]; + } +} + +- (void)testReferringURLWithNoParams { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link"]; + NSDictionary *expected = @{}; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testNilReferringURL { + NSURL *url = nil; + NSDictionary *expected = @{}; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLIgnoredParam { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?other=12345"]; + NSDictionary *expected = @{ }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclid { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid=12345"]; + NSDictionary *expected = @{ + @"gclid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +// NSURL treats URI schemes in a consistent manner with Universal Links +- (void)testReferringURLWithURISchemeSanityCheck{ + NSURL *url = [NSURL URLWithString:@"branchtest://?gclid=12345"]; + NSDictionary *expected = @{ + @"gclid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidCapitalized { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?GCLID=12345"]; + NSDictionary *expected = @{ + @"gclid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidMixedCase { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?GcLiD=12345"]; + NSDictionary *expected = @{ + @"gclid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidNoValue { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid="]; + NSDictionary *expected = @{ + @"gclid": @"" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidValueCasePreserved { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid=aAbBcC"]; + NSDictionary *expected = @{ + @"gclid": @"aAbBcC" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidIgnoredParam { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid=12345&other=abcde"]; + NSDictionary *expected = @{ + @"gclid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidFragment{ + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid=12345#header"]; + NSDictionary *expected = @{ + @"gclid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidAsFragment{ + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?other=abcde#gclid=12345"]; + NSDictionary *expected = @{ }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGclidOverwritesValue { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid=12345"]; + NSDictionary *expected = @{ + @"gclid": @"12345" + }; + + NSURL *url2 = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid=abcde"]; + NSDictionary *expected2 = @{ + @"gclid": @"abcde" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + XCTAssert([expected isEqualToDictionary:params]); + + [utility parseReferringURL:url2]; + NSDictionary *params2 = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected2 isEqualToDictionary:params2]); +} + +- (void)testReferringURLWithMetaCampaignIdsAndInvalidURL { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?al_applink_data=[]#target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22campaign_ids%22%3A%22ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA%22%2C%22test_deeplink%22%3A1%7D"]; + NSDictionary *expected = @{}; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithMetaCampaignIds { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22campaign_ids%22%3A%22ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA%22%2C%22test_deeplink%22%3A1%7D"]; + NSDictionary *expected = @{ + @"meta_campaign_ids": @"ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithMetaCampaignIdsExpired { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22campaign_ids%22%3A%22ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA%22%2C%22test_deeplink%22%3A1%7D"]; + NSDictionary *expected = @{ }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + [self expireValidityWindowsInUtility:utility]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithMetaNoCampaignIds { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22test_deeplink%22%3A1%7D"]; + NSDictionary *expected = @{ }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithGbraid { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gbraid=abcde"]; + NSDictionary *expected = @{ + @"gbraid": @"abcde", + @"is_deeplink_gbraid": @(true) + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + [self validateGbraidTimestampInReferringParameters:params]; + NSDictionary *paramsWithoutTimestamp = [self removeTimestampFromParams:params]; + XCTAssert([expected isEqualToDictionary:paramsWithoutTimestamp]); +} + +- (void)testReferringURLWithGbraidOnEvent { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gbraid=abcde"]; + NSDictionary *expected = @{ + @"gbraid": @"abcde" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:eventEndpoint]; + + [self validateGbraidTimestampInReferringParameters:params]; + NSDictionary *paramsWithoutTimestamp = [self removeTimestampFromParams:params]; + XCTAssert([expected isEqualToDictionary:paramsWithoutTimestamp]); +} + +- (void)testReferringURLWithGbraidExpired { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gbraid=abcde"]; + NSDictionary *expected = @{ }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + [self expireValidityWindowsInUtility:utility]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLPreservesNonZeroValidityWindowForGbraid { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gbraid=12345"]; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + + // pretend this object was loaded from disk + // this simulates setting a custom non-zero validity window, only supported for gbraid + BNCUrlQueryParameter *existingParam = [BNCUrlQueryParameter new]; + existingParam.name = @"gbraid"; + existingParam.value = @""; + existingParam.timestamp = [NSDate date]; + existingParam.validityWindow = 5; // not the default gbraid window + utility.urlQueryParameters[@"gbraid"] = existingParam; + + [utility parseReferringURL:url]; + + // verify validity window was not changed + XCTAssert(utility.urlQueryParameters[@"gbraid"].validityWindow == 5); +} + +- (void)testReferringURLOverwritesZeroValidityWindowForGbraid { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gbraid=12345"]; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + + // pretend this object was loaded from disk + // for gbraid, or any param, we overwrite the 0 validity windows with the default + BNCUrlQueryParameter *existingParam = [BNCUrlQueryParameter new]; + existingParam.name = @"gbraid"; + existingParam.value = @""; + existingParam.timestamp = [NSDate date]; + existingParam.validityWindow = 0; + utility.urlQueryParameters[@"gbraid"] = existingParam; + + [utility parseReferringURL:url]; + + // verify validity window was changed + XCTAssert(utility.urlQueryParameters[@"gbraid"].validityWindow != 0); +} + +- (void)testReferringURLWithGclidGbraid { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?gclid=12345&gbraid=abcde"]; + NSDictionary *expected = @{ + @"gclid": @"12345", + @"gbraid": @"abcde", + @"is_deeplink_gbraid": @(true) + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + [self validateGbraidTimestampInReferringParameters:params]; + NSDictionary *paramsWithoutTimestamp = [self removeTimestampFromParams:params]; + XCTAssert([expected isEqualToDictionary:paramsWithoutTimestamp]); +} + +- (void)testGbraidDataMigration { + // Manipulates the global BNCPreferenceHelper. + // This is not safe for concurrent unit tests, so only the happy path is tested. + [self clearCurrentQueryParameters]; + [self addOldGbraidData]; + + NSDictionary *expected = @{ + @"gbraid": @"abcde", + @"is_deeplink_gbraid": @(false) + }; + + BNCReferringURLUtility *utility = [BNCReferringURLUtility new]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + [self validateGbraidTimestampInReferringParameters:params]; + NSDictionary *paramsWithoutTimestamp = [self removeTimestampFromParams:params]; + XCTAssert([expected isEqualToDictionary:paramsWithoutTimestamp]); + + [self verifyOldGbraidDataIsCleared]; +} + +- (void)clearCurrentQueryParameters { + [BNCPreferenceHelper sharedInstance].referringURLQueryParameters = nil; +} + +- (void)addOldGbraidData { + [BNCPreferenceHelper sharedInstance].referrerGBRAID = @"abcde"; + [BNCPreferenceHelper sharedInstance].referrerGBRAIDValidityWindow = 2592000; + [BNCPreferenceHelper sharedInstance].referrerGBRAIDInitDate = [NSDate date]; +} + +- (void)verifyOldGbraidDataIsCleared { + XCTAssertNil([BNCPreferenceHelper sharedInstance].referrerGBRAID); + XCTAssert([BNCPreferenceHelper sharedInstance].referrerGBRAIDValidityWindow == 0); + XCTAssertNil([BNCPreferenceHelper sharedInstance].referrerGBRAIDInitDate); +} + +- (void)testReferringURLWithSccid { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?sccid=12345"]; + NSDictionary *expected = @{ + @"sccid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithSccidMixedCase { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?ScCiD=12345"]; + NSDictionary *expected = @{ + @"sccid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithSccidNoValue { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?sccid="]; + NSDictionary *expected = @{ + @"sccid": @"" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithSccidValueCasePreserved { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?sccid=aAbBcC"]; + NSDictionary *expected = @{ + @"sccid": @"aAbBcC" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithSccidIgnoredParam { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?sccid=12345&other=abcde"]; + NSDictionary *expected = @{ + @"sccid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithSccidFragment{ + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?sccid=12345#header"]; + NSDictionary *expected = @{ + @"sccid": @"12345" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithSccidAsFragment{ + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?other=abcde#sccid=12345"]; + NSDictionary *expected = @{ }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected isEqualToDictionary:params]); +} + +- (void)testReferringURLWithSccidOverwritesValue { + NSURL *url = [NSURL URLWithString:@"https://bnctestbed.app.link?sccid=12345"]; + NSDictionary *expected = @{ + @"sccid": @"12345" + }; + + NSURL *url2 = [NSURL URLWithString:@"https://bnctestbed.app.link?sccid=abcde"]; + NSDictionary *expected2 = @{ + @"sccid": @"abcde" + }; + + BNCReferringURLUtility *utility = [self referringUtilityForTests]; + [utility parseReferringURL:url]; + NSDictionary *params = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + XCTAssert([expected isEqualToDictionary:params]); + + [utility parseReferringURL:url2]; + NSDictionary *params2 = [utility referringURLQueryParamsForEndpoint:openEndpoint]; + + XCTAssert([expected2 isEqualToDictionary:params2]); +} + + +@end diff --git a/BranchSDKTests/BNCRequestFactoryTests.m b/BranchSDKTests/BNCRequestFactoryTests.m new file mode 100644 index 000000000..c28d3b6e0 --- /dev/null +++ b/BranchSDKTests/BNCRequestFactoryTests.m @@ -0,0 +1,234 @@ +// +// BNCRequestFactoryTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 8/21/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import "BNCRequestFactory.h" +#import "BranchConstants.h" +#import "BNCEncodingUtils.h" + +@interface BNCRequestFactoryTests : XCTestCase +@property (nonatomic, copy, readwrite) NSString *requestUUID; +@property (nonatomic, copy, readwrite) NSNumber *requestCreationTimeStamp; +@end + +@implementation BNCRequestFactoryTests + +- (void)setUp { + _requestUUID = [[NSUUID UUID ] UUIDString]; + _requestCreationTimeStamp = BNCWireFormatFromDate([NSDate date]); +} + +- (void)tearDown { + +} + +- (void)testInitWithBranchKeyNil { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:nil UUID:_requestUUID TimeStamp:_requestCreationTimeStamp]; + NSDictionary *json = [factory dataForInstallWithURLString:@"https://branch.io"]; + XCTAssertNotNil(json); + + // key is omitted when nil + XCTAssertNil([json objectForKey:@"branch_key"]); + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testInitWithBranchKeyEmpty { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForInstallWithURLString:@"https://branch.io"]; + XCTAssertNotNil(json); + + // empty string is allowed + XCTAssertTrue([@"" isEqualToString:[json objectForKey:@"branch_key"]]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testInitWithBranchKey { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForInstallWithURLString:@"https://branch.io"]; + XCTAssertNotNil(json); + XCTAssertTrue([@"key_abcd" isEqualToString:[json objectForKey:@"branch_key"]]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForInstall { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForInstallWithURLString:@"https://branch.io"]; + XCTAssertNotNil(json); + + XCTAssertTrue([@"key_abcd" isEqualToString:[json objectForKey:@"branch_key"]]); + XCTAssertNotNil([json objectForKey:@"sdk"]); + XCTAssertTrue([@"Apple" isEqualToString:[json objectForKey:@"brand"]]); + XCTAssertNotNil([json objectForKey:@"ios_vendor_id"]); + + // not present on installs + XCTAssertNil([json objectForKey:@"randomized_bundle_token"]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForOpen { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForOpenWithURLString:@"https://branch.io"]; + XCTAssertNotNil(json); + + XCTAssertTrue([@"key_abcd" isEqualToString:[json objectForKey:@"branch_key"]]); + XCTAssertNotNil([json objectForKey:@"sdk"]); + XCTAssertTrue([@"Apple" isEqualToString:[json objectForKey:@"brand"]]); + XCTAssertNotNil([json objectForKey:@"ios_vendor_id"]); + + // Present only on opens. Assumes test runs after the host app completes an install. + // This is not a reliable assumption on test runners + //XCTAssertNotNil([json objectForKey:@"randomized_bundle_token"]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForEvent { + NSDictionary *event = @{@"name": @"ADD_TO_CART"}; + + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; + XCTAssertNotNil(json); + + XCTAssertTrue([@"ADD_TO_CART" isEqualToString:[json objectForKey:@"name"]]); + + NSDictionary *userData = [json objectForKey:@"user_data"]; + XCTAssertNotNil(userData); + XCTAssertNotNil([userData objectForKey:@"idfv"]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForEventWithContentItem { + NSDictionary *event = @{ + @"name": @"ADD_TO_CART", + @"content_items": @[ + @{ + @"$og_title": @"TestTitle", + @"$quantity": @(2), + @"$product_name": @"TestProduct", + @"$price": @(10) + } + ] + }; + + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; + XCTAssertNotNil(json); + + XCTAssertTrue([@"ADD_TO_CART" isEqualToString:[json objectForKey:@"name"]]); + + NSDictionary *contentItems = [json objectForKey:@"content_items"]; + XCTAssertNotNil(contentItems); + XCTAssertTrue(contentItems.count == 1); + + NSDictionary *userData = [json objectForKey:@"user_data"]; + XCTAssertNotNil(userData); + XCTAssertNotNil([userData objectForKey:@"idfv"]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForEventWithTwoContentItem { + NSDictionary *event = @{ + @"name": @"ADD_TO_CART", + @"content_items": @[ + @{ + @"$og_title": @"TestTitle1", + @"$quantity": @(2), + @"$product_name": @"TestProduct1", + @"$price": @(10) + }, + @{ + @"$og_title": @"TestTitle2", + @"$quantity": @(3), + @"$product_name": @"TestProduct2", + @"$price": @(20) + } + ] + }; + + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; + XCTAssertNotNil(json); + + XCTAssertTrue([@"ADD_TO_CART" isEqualToString:[json objectForKey:@"name"]]); + + NSDictionary *contentItems = [json objectForKey:@"content_items"]; + XCTAssertNotNil(contentItems); + XCTAssertTrue(contentItems.count == 2); + + NSDictionary *userData = [json objectForKey:@"user_data"]; + XCTAssertNotNil(userData); + XCTAssertNotNil([userData objectForKey:@"idfv"]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForEventEmpty { + NSDictionary *event = @{}; + + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; + XCTAssertNotNil(json); + + XCTAssertNil([json objectForKey:@"name"]); + + NSDictionary *userData = [json objectForKey:@"user_data"]; + XCTAssertNotNil(userData); + XCTAssertNotNil([userData objectForKey:@"idfv"]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForEventNil { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForEventWithEventDictionary:nil]; + XCTAssertNotNil(json); + + XCTAssertNil([json objectForKey:@"name"]); + + NSDictionary *userData = [json objectForKey:@"user_data"]; + XCTAssertNotNil(userData); + XCTAssertNotNil([userData objectForKey:@"idfv"]); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + + +- (void)testDataForShortURL { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForShortURLWithLinkDataDictionary:@{}.mutableCopy isSpotlightRequest:NO]; + XCTAssertNotNil(json); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +- (void)testDataForLATD { + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; + NSDictionary *json = [factory dataForLATDWithDataDictionary:@{}.mutableCopy]; + XCTAssertNotNil(json); + + XCTAssertTrue(self.requestCreationTimeStamp == [json objectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]); + XCTAssertTrue([self.requestUUID isEqualToString:[json objectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]]); +} + +@end diff --git a/BranchSDKTests/BNCSKAdNetworkTests.m b/BranchSDKTests/BNCSKAdNetworkTests.m new file mode 100644 index 000000000..d71cf203a --- /dev/null +++ b/BranchSDKTests/BNCSKAdNetworkTests.m @@ -0,0 +1,264 @@ +// +// BNCSKAdNetworkTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 8/13/20. +// Copyright © 2020 Branch, Inc. All rights reserved. +// + +#import +#import "BNCSKAdNetwork.h" +#import "BranchEvent.h" + +// Expose private methods for testing +@interface BNCSKAdNetwork() + +@property (nonatomic, copy, readwrite) NSDate *installDate; + +- (BOOL)shouldAttemptSKAdNetworkCallout; + +@end + +@interface BranchEvent() + +// private BranchEvent methods used to check data before sending to network service. +- (NSDictionary *)buildEventDictionary; +- (BranchEventRequest *)buildRequestWithEventDictionary:(NSDictionary *)eventDictionary; + +@end + + +@interface BNCSKAdNetworkTests : XCTestCase + +@property (nonatomic, strong, readwrite) BNCSKAdNetwork *skAdNetwork; + +@end + +@implementation BNCSKAdNetworkTests + +- (void)setUp { + self.skAdNetwork = [BNCSKAdNetwork new]; + self.skAdNetwork.installDate = [NSDate date]; +} + +- (void)tearDown { + +} + +- (void)testDefaultMaxTimeout { + NSTimeInterval days; + if (@available(iOS 16.1, macCatalyst 16.1, *)) { + days = 3600.0 * 24.0 * 60.0; // one day + } else { + days = 3600.0 * 24.0; // one day + } + XCTAssertTrue(self.skAdNetwork.maxTimeSinceInstall == days); +} + +- (void)testShouldAttemptSKAdNetworkCallout { + XCTAssertTrue([self.skAdNetwork shouldAttemptSKAdNetworkCallout]); +} + +- (void)testShouldAttemptSKAdNetworkCalloutFalse { + self.skAdNetwork.maxTimeSinceInstall = 0.0; + XCTAssertFalse([self.skAdNetwork shouldAttemptSKAdNetworkCallout]); +} + +- (void)testPostbackCall { + + if (@available(iOS 16.1, macCatalyst 16.1, *)) { + self.skAdNetwork.maxTimeSinceInstall = 3600.0 * 24.0 * 60.0; + } else { + self.skAdNetwork.maxTimeSinceInstall = 3600.0 * 24.0; // one day + } + + XCTAssertTrue([self.skAdNetwork shouldAttemptSKAdNetworkCallout]); + + [[BNCSKAdNetwork sharedInstance] registerAppForAdNetworkAttribution]; + + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventInvite]; + NSDictionary *eventDictionary = [event buildEventDictionary]; + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"TestPostback"]; + BNCServerResponse *openInstallResponse = [[BNCServerResponse alloc] init]; + + openInstallResponse.data = @{ @"update_conversion_value": @60 }; + request.completion = ^(NSDictionary*_Nullable response, NSError*_Nullable error){ + [expectation fulfill]; + }; + [request processResponse:openInstallResponse error:Nil]; + + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + +- (void)testSKAN4ParamsDefaultValues { + + if (@available(iOS 16.1, macCatalyst 16.1, *)) { + NSString *coarseValue = [[BNCSKAdNetwork sharedInstance] getCoarseConversionValueFromDataResponse:@{}]; + XCTAssertTrue([coarseValue isEqualToString:@"low"]); + + BOOL isLocked = [[BNCSKAdNetwork sharedInstance] getLockedStatusFromDataResponse:@{}]; + XCTAssertFalse(isLocked); + + BOOL ascendingOnly = [[BNCSKAdNetwork sharedInstance] getAscendingOnlyFromDataResponse:@{}]; + XCTAssertTrue(ascendingOnly); + } +} + +- (void)testSKAN4ParamsValues { + + if (@available(iOS 16.1, macCatalyst 16.1, *)) { + + NSDictionary *response = @{@"update_conversion_value": @16, @"coarse_key": @"high", @"locked": @YES, @"ascending_only":@NO }; + BNCSKAdNetwork *adNetwork = [BNCSKAdNetwork sharedInstance]; + + NSString *coarseValue = [adNetwork getCoarseConversionValueFromDataResponse:response]; + XCTAssertTrue([coarseValue isEqualToString:@"high"]); + + BOOL isLocked = [adNetwork getLockedStatusFromDataResponse:response]; + XCTAssertTrue(isLocked); + + BOOL ascendingOnly = [adNetwork getAscendingOnlyFromDataResponse:response]; + XCTAssertFalse(ascendingOnly); + } +} + +- (void)testSKAN4CurrentWindow { + + BNCSKAdNetwork *adNetwork = [BNCSKAdNetwork sharedInstance]; + BNCPreferenceHelper *prefs = [BNCPreferenceHelper sharedInstance]; + + NSDate *currentDateAndTime = [NSDate date]; + prefs.firstAppLaunchTime = [currentDateAndTime dateByAddingTimeInterval:-30]; + NSInteger win = [adNetwork calculateSKANWindowForTime:currentDateAndTime]; + XCTAssertTrue(win == 1); + + win = [adNetwork calculateSKANWindowForTime: [ currentDateAndTime dateByAddingTimeInterval:24*3600*3 ]]; + XCTAssertTrue(win == 2); + + win = [adNetwork calculateSKANWindowForTime: [ currentDateAndTime dateByAddingTimeInterval:24*3600*10 ]]; + XCTAssertTrue(win == 3); + + win = [adNetwork calculateSKANWindowForTime: [ currentDateAndTime dateByAddingTimeInterval:24*3600*36 ]]; + XCTAssertTrue(win == 0); + + prefs.firstAppLaunchTime = nil; + [prefs synchronize]; + win = [adNetwork calculateSKANWindowForTime: currentDateAndTime]; + XCTAssertTrue(win == 0); +} + +- (void)testSKAN4HighestConversionValue { + + BNCSKAdNetwork *adNetwork = [BNCSKAdNetwork sharedInstance]; + BNCPreferenceHelper *prefs = [BNCPreferenceHelper sharedInstance]; + + prefs.highestConversionValueSent = 0; + prefs.skanCurrentWindow = 0; + NSDate *currentDateAndTime = [NSDate date]; + prefs.invokeRegisterApp = YES; + + prefs.firstAppLaunchTime = [currentDateAndTime dateByAddingTimeInterval:-30 ]; + [adNetwork shouldCallPostbackForDataResponse:@{}]; + XCTAssertTrue(prefs.highestConversionValueSent == 0); + + [adNetwork shouldCallPostbackForDataResponse:@{@"update_conversion_value": @6}]; + XCTAssertTrue(prefs.highestConversionValueSent == 6); + + [adNetwork shouldCallPostbackForDataResponse:@{@"update_conversion_value": @3}]; + XCTAssertTrue(prefs.highestConversionValueSent == 6); + + + prefs.firstAppLaunchTime = [currentDateAndTime dateByAddingTimeInterval:-24*3600*3 ]; + [adNetwork shouldCallPostbackForDataResponse:@{}]; + XCTAssertTrue(prefs.highestConversionValueSent == 0); +} + +- (void)testSKAN4ShouldCallPostback { + + BNCSKAdNetwork *adNetwork = [BNCSKAdNetwork sharedInstance]; + BNCPreferenceHelper *prefs = [BNCPreferenceHelper sharedInstance]; + + prefs.firstAppLaunchTime = nil; + [prefs synchronize]; + + NSDictionary *response = @{@"update_conversion_value": @16, @"coarse_key": @"high", @"locked": @YES, @"ascending_only":@NO }; + + BOOL shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertFalse(shouldCall); + +} + +- (void)testSKAN4ShouldCallPostback2 { + + BNCSKAdNetwork *adNetwork = [BNCSKAdNetwork sharedInstance]; + BNCPreferenceHelper *prefs = [BNCPreferenceHelper sharedInstance]; + + prefs.invokeRegisterApp = YES; + prefs.highestConversionValueSent = 0; + prefs.firstAppLaunchTime = [NSDate date]; + prefs.skanCurrentWindow = 0; + [prefs synchronize]; + + NSMutableDictionary *response = [[NSMutableDictionary alloc] initWithDictionary: + @{@"update_conversion_value": @16, @"coarse_key": @"high", @"locked": @YES, @"ascending_only":@YES }]; + + BOOL shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertTrue(shouldCall); + + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertFalse(shouldCall); + + response[@"update_conversion_value"] = @14; + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertFalse(shouldCall); + + response[@"update_conversion_value"] = @18; + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertTrue(shouldCall); + + prefs.firstAppLaunchTime = nil; + prefs.firstAppLaunchTime = [[NSDate date] dateByAddingTimeInterval:-24*3600*3]; + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + NSLog(@"Conv : %ld", prefs.highestConversionValueSent); + XCTAssertTrue(shouldCall); + + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertFalse(shouldCall); +} + +- (void)testSKAN4ShouldCallPostback3 { + BNCSKAdNetwork *adNetwork = [BNCSKAdNetwork sharedInstance]; + BNCPreferenceHelper *prefs = [BNCPreferenceHelper sharedInstance]; + + prefs.invokeRegisterApp = YES; + prefs.highestConversionValueSent = 0; + prefs.firstAppLaunchTime = [NSDate date]; + [prefs synchronize]; + + NSMutableDictionary *response = [[NSMutableDictionary alloc] initWithDictionary: + @{@"update_conversion_value": @16, @"coarse_key": @"high", @"locked": @YES, @"ascending_only":@NO }]; + + BOOL shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertTrue(shouldCall); + + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertTrue(shouldCall); + + response[@"update_conversion_value"] = @14; + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertTrue(shouldCall); + + response[@"update_conversion_value"] = @18; + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + XCTAssertTrue(shouldCall); + + prefs.firstAppLaunchTime = [[NSDate date] dateByAddingTimeInterval:-24*3600*3]; + //NSLog(@"Conv : %ld", (long)prefs.highestConversionValueSent); + shouldCall = [adNetwork shouldCallPostbackForDataResponse:response]; + NSLog(@"Conv : %ld", prefs.highestConversionValueSent); + XCTAssertTrue(shouldCall); +} + +@end diff --git a/BranchSDKTests/BNCSystemObserverTests.m b/BranchSDKTests/BNCSystemObserverTests.m new file mode 100644 index 000000000..8cc54f632 --- /dev/null +++ b/BranchSDKTests/BNCSystemObserverTests.m @@ -0,0 +1,139 @@ +// +// BNCSystemObserverTests.m +// Branch-TestBed +// +// Created by Graham Mueller on 4/22/15. +// Copyright (c) 2015 Branch Metrics. All rights reserved. +// + +#import +#import "BNCSystemObserver.h" + +@interface BNCSystemObserver () ++ (BOOL)compareUriSchemes:(NSString *)serverUriScheme With:(NSArray *)urlTypes; +@end + +@interface BNCSystemObserverTests : XCTestCase + +@end + +@implementation BNCSystemObserverTests + +- (void)testDefaultURIScheme_TestBed { + //ND XCTAssert([[BNCSystemObserver defaultURIScheme] isEqualToString:@"branchtest"]); +} + +- (void)testAppVersion_TestBed { + XCTAssert([[BNCSystemObserver applicationVersion] isEqualToString:@"1.0"]); +} + +- (void)testBundleIdentifier_TestBed { + NSString *bundleId = [BNCSystemObserver bundleIdentifier]; + XCTAssert([bundleId isEqualToString:@"branch.BranchSDKTestsHostApp"]); +} + +- (void)testBrand { + XCTAssert([[BNCSystemObserver brand] isEqualToString:@"Apple"]); +} + +- (void)testModel_Simulator { + // simulator models + NSString *tmp = [BNCSystemObserver model]; + XCTAssert([tmp containsString:@"arm64"] || [tmp containsString:@"x86_64"]); +} + +//- (void)testModelName_iPhone7 { +// XCTAssert([@"iPhone9,3" isEqualToString:[BNCSystemObserver model]]); +//} + +- (void)testOSName { + XCTAssertNotNil([BNCSystemObserver osName]); + + // This is not the system name, but rather the name Branch server expects + // XCTAssert([self.deviceInfo.osName isEqualToString:[UIDevice currentDevice].systemName]); + XCTAssert([@"iOS" isEqualToString:[BNCSystemObserver osName]] || [@"tv_OS" isEqualToString:[BNCSystemObserver osName]]); +} + +- (void)testOSVersion { + XCTAssertNotNil([BNCSystemObserver osVersion]); + XCTAssert([[BNCSystemObserver osVersion] isEqualToString:[UIDevice currentDevice].systemVersion]); +} + +/* + * Sample device screens + * original iPhone 320x480 1 + * iPad Pro (6th gen 12.9") 2048x2732 2 + * iPhone 14 Pro max 1290x2796 3 + */ + +- (void)testScreenWidth { + XCTAssert([BNCSystemObserver screenWidth].intValue >= 320 && [BNCSystemObserver screenWidth].intValue <= 2796); +} + +- (void)testScreenHeight { + XCTAssert([BNCSystemObserver screenHeight].intValue >= 320 && [BNCSystemObserver screenWidth].intValue <= 2796); +} + +- (void)testScreenScale { + XCTAssert([BNCSystemObserver screenScale].intValue >= 1 && [BNCSystemObserver screenScale].intValue <= 3); +} + +- (void)testIsSimulator_Simulator { + XCTAssert([BNCSystemObserver isSimulator]); +} + +- (void)testAdvertiserIdentifier_NoATTPrompt { + XCTAssertNil([BNCSystemObserver advertiserIdentifier]); +} + +- (void)testOptedInStatus_NoATTPrompt { + XCTAssert([[BNCSystemObserver attOptedInStatus] isEqualToString:@"not_determined"]); +} + +- (void)testAppleAttributionToken_Simulator { + NSString *token = [BNCSystemObserver appleAttributionToken]; + XCTAssertNil(token); +} + +- (void)testEnvironment { + // currently not running unit tests on extensions + XCTAssert([@"FULL_APP" isEqualToString:[BNCSystemObserver environment]]); +} + +- (void)testIsAppClip { + // currently not running unit tests on extensions + XCTAssert(![BNCSystemObserver isAppClip]); +} + +- (void)testCompareURIScemes { + + NSString *serverUriScheme = @"bnctest://"; + NSArray *urlTypes = @[@{@"CFBundleURLSchemes" : @[@""]}, @{@"CFBundleURLSchemes" : @[@"bnctest", @"xyzs"]}]; + + XCTAssertTrue([BNCSystemObserver compareUriSchemes:serverUriScheme With:urlTypes]); + + XCTAssertFalse([BNCSystemObserver compareUriSchemes:serverUriScheme With:nil]); + + XCTAssertFalse([BNCSystemObserver compareUriSchemes:nil With:nil]); + + XCTAssertFalse([BNCSystemObserver compareUriSchemes:nil With:urlTypes]); + + serverUriScheme = @":/"; + XCTAssertFalse([BNCSystemObserver compareUriSchemes:serverUriScheme With:urlTypes]); + + serverUriScheme = @"bnctest"; + XCTAssertTrue([BNCSystemObserver compareUriSchemes:serverUriScheme With:urlTypes]); + + serverUriScheme = @"bnctest://"; + urlTypes = @[ @{@"CFBundleURLSchemes" : @[@"bnctestX", @"xyzs"]}]; + XCTAssertFalse([BNCSystemObserver compareUriSchemes:serverUriScheme With:urlTypes]); + + serverUriScheme = @"://"; + XCTAssertFalse([BNCSystemObserver compareUriSchemes:serverUriScheme With:urlTypes]); + + XCTAssertFalse([BNCSystemObserver compareUriSchemes:@"" With:urlTypes]); + + XCTAssertFalse([BNCSystemObserver compareUriSchemes:@"" With:@[@{}]]); +} + +@end diff --git a/BranchSDKTests/BNCURLFilterSkiplistUpgradeTests.m b/BranchSDKTests/BNCURLFilterSkiplistUpgradeTests.m new file mode 100644 index 000000000..ad7d5b029 --- /dev/null +++ b/BranchSDKTests/BNCURLFilterSkiplistUpgradeTests.m @@ -0,0 +1,273 @@ +// +// BNCURLFilterSkiplistUpgradeTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 4/4/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import "BNCURLFilter.h" + +@interface BNCURLFilterSkiplistUpgradeTests : XCTestCase + +@end + +@implementation BNCURLFilterSkiplistUpgradeTests + +- (void)setUp { + +} + +- (void)tearDown { + +} + + // v0 list + // https://cdn.branch.io/sdk/uriskiplist_v0.json +- (NSArray *)v0PatternList { + NSArray *patternList = @[ + @"^fb\\d+:", + @"^li\\d+:", + @"^pdk\\d+:", + @"^twitterkit-.*:", + @"^com\\.googleusercontent\\.apps\\.\\d+-.*:\\/oauth", + @"^(?i)(?!(http|https):).*(:|:.*\\b)(password|o?auth|o?auth.?token|access|access.?token)\\b", + @"^(?i)((http|https):\\/\\/).*[\\/|?|#].*\\b(password|o?auth|o?auth.?token|access|access.?token)\\b" + ]; + return patternList; +} + +// v1 list +// https://cdn.branch.io/sdk/uriskiplist_v1.json +- (NSArray *)v1PatternList { + NSArray *patternList = @[ + @"^fb\\d+:", + @"^li\\d+:", + @"^pdk\\d+:", + @"^twitterkit-.*:", + @"^com\\.googleusercontent\\.apps\\.\\d+-.*:\\/oauth", + @"^(?i)(?!(http|https):).*(:|:.*\\b)(password|o?auth|o?auth.?token|access|access.?token)\\b", + @"^(?i)((http|https):\\/\\/).*[\\/|?|#].*\\b(password|o?auth|o?auth.?token|access|access.?token)\\b" + ]; + return patternList; +} + +// v2 list +// https://cdn.branch.io/sdk/uriskiplist_v2.json +- (NSArray *)v2PatternList { + NSArray *patternList = @[ + @"^fb\\d+:((?!campaign_ids).)*$", + @"^li\\d+:", + @"^pdk\\d+:", + @"^twitterkit-.*:", + @"^com\\.googleusercontent\\.apps\\.\\d+-.*:\\/oauth", + @"^(?i)(?!(http|https):).*(:|:.*\\b)(password|o?auth|o?auth.?token|access|access.?token)\\b", + @"^(?i)((http|https):\\/\\/).*[\\/|?|#].*\\b(password|o?auth|o?auth.?token|access|access.?token)\\b" + ]; + return patternList; +} + +- (BNCURLFilter *)filterWithV0List { + BNCURLFilter *filter = [BNCURLFilter new]; + [self migrateFilter:filter patternList:[self v1PatternList]]; + return filter; +} + +- (BNCURLFilter *)filterWithV1List { + BNCURLFilter *filter = [BNCURLFilter new]; + [self migrateFilter:filter patternList:[self v1PatternList]]; + return filter; +} + +- (BNCURLFilter *)filterWithV2List { + BNCURLFilter *filter = [BNCURLFilter new]; + [self migrateFilter:filter patternList:[self v2PatternList]]; + return filter; +} + +- (void)migrateFilter:(BNCURLFilter *)filter patternList:(NSArray *)patternList { + [filter useCustomPatternList:patternList]; +} + +- (NSArray *)badURLs { + NSArray *kBadURLs = @[ + @"fb123456:login/464646", + @"twitterkit-.4545:", + @"shsh:oauth/login", + @"https://myapp.app.link/oauth_token=fred", + @"https://myapp.app.link/auth_token=fred", + @"https://myapp.app.link/authtoken=fred", + @"https://myapp.app.link/auth=fred", + @"fb1234:", + @"fb1234:/", + @"fb1234:/this-is-some-extra-info/?whatever", + @"fb1234:/this-is-some-extra-info/?whatever:andstuff", + @"myscheme:path/to/resource?oauth=747474", + @"myscheme:oauth=747474", + @"myscheme:/oauth=747474", + @"myscheme://oauth=747474", + @"myscheme://path/oauth=747474", + @"myscheme://path/:oauth=747474", + @"https://google.com/userprofile/devonbanks=oauth?", + ]; + return kBadURLs; +} + +- (NSArray *)goodURLs { + NSArray *kGoodURLs = @[ + @"shshs:/content/path", + @"shshs:content/path", + @"https://myapp.app.link/12345/link", + @"fb123x:/", + @"https://myapp.app.link?authentic=true&tokemonsta=false", + @"myscheme://path/brauth=747474", + ]; + return kGoodURLs; +} + +- (void)testOldBadURLsWithV0 { + BNCURLFilter *filter = [self filterWithV0List]; + NSArray *list = [self badURLs]; + for (NSString *string in list) { + NSURL *url = [NSURL URLWithString:string]; + if (url) { + XCTAssertTrue([filter shouldIgnoreURL:url], @"Checking '%@'.", url); + } + } +} + +- (void)testOldGoodURLsWithV0 { + BNCURLFilter *filter = [self filterWithV0List]; + NSArray *list = [self goodURLs]; + for (NSString *string in list) { + NSURL *url = [NSURL URLWithString:string]; + if (url) { + XCTAssertFalse([filter shouldIgnoreURL:url], @"Checking '%@'.", url); + } + } +} + +- (void)testOldBadURLsWithV2 { + BNCURLFilter *filter = [self filterWithV2List]; + NSArray *list = [self badURLs]; + for (NSString *string in list) { + NSURL *url = [NSURL URLWithString:string]; + if (url) { + XCTAssertTrue([filter shouldIgnoreURL:url], @"Checking '%@'.", url); + } + } +} + +- (void)testOldGoodURLsWithV2 { + BNCURLFilter *filter = [self filterWithV2List]; + NSArray *list = [self goodURLs]; + for (NSString *string in list) { + NSURL *url = [NSURL URLWithString:string]; + if (url) { + XCTAssertFalse([filter shouldIgnoreURL:url], @"Checking '%@'.", url); + } + } +} + +- (void)testMetaAEMWithV0 { + NSString *string = @"fb1://?campaign_ids=a"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV0List]; + XCTAssertTrue([filter shouldIgnoreURL:url]); + } +} + +- (void)testMetaAEMWithV2 { + NSString *string = @"fb1://?campaign_ids=a"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV2List]; + XCTAssertFalse([filter shouldIgnoreURL:url]); + } +} + +- (void)testMetaAEMWithV2WithTrailingParameters { + NSString *string = @"fb1://?campaign_ids=a&token=abcde"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV2List]; + XCTAssertFalse([filter shouldIgnoreURL:url]); + } +} + +- (void)testMetaAEMWithV2WithPrecedingParameters { + NSString *string = @"fb1://?brand=abcde&campaign_ids=a"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV2List]; + XCTAssertFalse([filter shouldIgnoreURL:url]); + } +} + +- (void)testMetaAEMWithV2WithPrecedingAndTrailingParameters { + NSString *string = @"fb1://?brand=abcde&campaign_ids=a&link=12345"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV2List]; + XCTAssertFalse([filter shouldIgnoreURL:url]); + } +} + +- (void)testSampleMetaAEMWithV0 { + NSString *string = @"fb123456789://products/next?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22campaign_ids%22%3A%22ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA%22%2C%22test_deeplink%22%3A1%7D"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV0List]; + XCTAssertTrue([filter shouldIgnoreURL:url]); + } +} + +- (void)testSampleMetaAEMWithV1 { + NSString *string = @"fb123456789://products/next?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22campaign_ids%22%3A%22ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA%22%2C%22test_deeplink%22%3A1%7D"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV1List]; + XCTAssertTrue([filter shouldIgnoreURL:url]); + } +} + +// This one is not filtered! +- (void)testSampleMetaAEMWithV2 { + NSString *string = @"fb123456789://products/next?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22campaign_ids%22%3A%22ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA%22%2C%22test_deeplink%22%3A1%7D"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV2List]; + XCTAssertFalse([filter shouldIgnoreURL:url]); + } +} + +- (void)testSampleMetaAEMNoCampignIDsWithV0 { + NSString *string = @"fb123456789://products/next?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22test_deeplink%22%3A1%7D"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV0List]; + XCTAssertTrue([filter shouldIgnoreURL:url]); + } +} + +- (void)testSampleMetaAEMNoCampignIDsWithV1 { + NSString *string = @"fb123456789://products/next?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22test_deeplink%22%3A1%7D"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV1List]; + XCTAssertTrue([filter shouldIgnoreURL:url]); + } +} + +- (void)testSampleMetaAEMNoCampignIDsWithV2 { + NSString *string = @"fb123456789://products/next?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22test_deeplink%22%3A1%7D"; + NSURL *url = [NSURL URLWithString:string]; + if (url) { + BNCURLFilter *filter = [self filterWithV2List]; + XCTAssertTrue([filter shouldIgnoreURL:url]); + } +} + +@end diff --git a/BranchSDKTests/BNCURLFilterTests.m b/BranchSDKTests/BNCURLFilterTests.m new file mode 100644 index 000000000..1573bc36e --- /dev/null +++ b/BranchSDKTests/BNCURLFilterTests.m @@ -0,0 +1,168 @@ +/** + @file BNCURLFilterTests.m + @package Branch-SDK-Tests + @brief BNCURLFilter tests. + + @author Edward Smith + @date February 14, 2018 + @copyright Copyright © 2018 Branch. All rights reserved. +*/ + +#import +#import "BNCURLFilter.h" + +@interface BNCURLFilterTests : XCTestCase +@end + +@implementation BNCURLFilterTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testPatternMatchingURL_nil { + BNCURLFilter *filter = [BNCURLFilter new]; + NSURL *url = nil; + NSString *matchingRegex = [filter patternMatchingURL:url]; + XCTAssertNil(matchingRegex); +} + +- (void)testPatternMatchingURL_emptyString { + BNCURLFilter *filter = [BNCURLFilter new]; + NSURL *url = [NSURL URLWithString:@""]; + NSString *matchingRegex = [filter patternMatchingURL:url]; + XCTAssertNil(matchingRegex); +} + +- (void)testPatternMatchingURL_fbRegexMatches { + NSString *pattern = @"^fb\\d+:((?!campaign_ids).)*$"; + NSString *sampleURL = @"fb12345://"; + + BNCURLFilter *filter = [BNCURLFilter new]; + NSURL *url = [NSURL URLWithString:sampleURL]; + NSString *matchingRegex = [filter patternMatchingURL:url]; + XCTAssertTrue([pattern isEqualToString:matchingRegex]); +} + +- (void)testPatternMatchingURL_fbRegexDoesNotMatch { + NSString *pattern = @"^fb\\d+:((?!campaign_ids).)*$"; + NSString *sampleURL = @"fb12345://campaign_ids"; + + BNCURLFilter *filter = [BNCURLFilter new]; + NSURL *url = [NSURL URLWithString:sampleURL]; + NSString *matchingRegex = [filter patternMatchingURL:url]; + XCTAssertFalse([pattern isEqualToString:matchingRegex]); +} + + +- (void)testIgnoredSuspectedAuthURLs { + NSArray *urls = @[ + @"fb123456:login/464646", + @"shsh:oauth/login", + @"https://myapp.app.link/oauth_token=fred", + @"https://myapp.app.link/auth_token=fred", + @"https://myapp.app.link/authtoken=fred", + @"https://myapp.app.link/auth=fred", + @"myscheme:path/to/resource?oauth=747474", + @"myscheme:oauth=747474", + @"myscheme:/oauth=747474", + @"myscheme://oauth=747474", + @"myscheme://path/oauth=747474", + @"myscheme://path/:oauth=747474", + @"https://google.com/userprofile/devonbanks=oauth?" + ]; + + BNCURLFilter *filter = [BNCURLFilter new]; + for (NSString *string in urls) { + NSURL *URL = [NSURL URLWithString:string]; + XCTAssertTrue([filter shouldIgnoreURL:URL], @"Checking '%@'.", URL); + } +} + +- (void)testAllowedURLsSimilarToAuthURLs { + NSArray *urls = @[ + @"shshs:/content/path", + @"shshs:content/path", + @"https://myapp.app.link/12345/link", + @"https://myapp.app.link?authentic=true&tokemonsta=false", + @"myscheme://path/brauth=747474" + ]; + + BNCURLFilter *filter = [BNCURLFilter new]; + for (NSString *string in urls) { + NSURL *URL = [NSURL URLWithString:string]; + XCTAssertFalse([filter shouldIgnoreURL:URL], @"Checking '%@'", URL); + } +} + +- (void)testIgnoredFacebookURLs { + // Most FB URIs are ignored + NSArray *urls = @[ + @"fb123456://login/464646", + @"fb1234:", + @"fb1234:/", + @"fb1234:/this-is-some-extra-info/?whatever", + @"fb1234:/this-is-some-extra-info/?whatever:andstuff" + ]; + + BNCURLFilter *filter = [BNCURLFilter new]; + for (NSString *string in urls) { + NSURL *URL = [NSURL URLWithString:string]; + XCTAssertTrue([filter shouldIgnoreURL:URL], @"Checking '%@'.", URL); + } +} + +- (void)testAllowedFacebookURLs { + NSArray *urls = @[ + // Facebook URIs do not contain letters other than an fb prefix + @"fb123x://", + // FB URIs with campaign ids are allowed + @"fb1234://helloworld?al_applink_data=%7B%22target_url%22%3A%22http%3A%5C%2F%5C%2Fitunes.apple.com%5C%2Fapp%5C%2Fid880047117%22%2C%22extras%22%3A%7B%22fb_app_id%22%3A2020399148181142%7D%2C%22referer_app_link%22%3A%7B%22url%22%3A%22fb%3A%5C%2F%5C%2F%5C%2F%3Fapp_id%3D2020399148181142%22%2C%22app_name%22%3A%22Facebook%22%7D%2C%22acs_token%22%3A%22debuggingtoken%22%2C%22campaign_ids%22%3A%22ARFUlbyOurYrHT2DsknR7VksCSgN4tiH8TzG8RIvVoUQoYog5bVCvADGJil5kFQC6tQm-fFJQH0w8wCi3NbOmEHHrtgCNglkXNY-bECEL0aUhj908hIxnBB0tchJCqwxHjorOUqyk2v4bTF75PyWvxOksZ6uTzBmr7wJq8XnOav0bA%22%2C%22test_deeplink%22%3A1%7D" + ]; + + BNCURLFilter *filter = [BNCURLFilter new]; + for (NSString *string in urls) { + NSURL *URL = [NSURL URLWithString:string]; + XCTAssertFalse([filter shouldIgnoreURL:URL], @"Checking '%@'", URL); + } +} + +- (void)testCustomPatternList { + BNCURLFilter *filter = [BNCURLFilter new]; + + // sanity check default pattern list + XCTAssertTrue([filter shouldIgnoreURL:[NSURL URLWithString:@"fb123://"]]); + XCTAssertFalse([filter shouldIgnoreURL:[NSURL URLWithString:@"branch123://"]]); + + // confirm new pattern list is enforced + [filter useCustomPatternList:@[@"^branch\\d+:"]]; + XCTAssertFalse([filter shouldIgnoreURL:[NSURL URLWithString:@"fb123://"]]); + XCTAssertTrue([filter shouldIgnoreURL:[NSURL URLWithString:@"branch123://"]]); +} + +// This is an end to end test and relies on a server call +- (void)testUpdatePatternListFromServer { + BNCURLFilter *filter = [BNCURLFilter new]; + + // confirm new pattern list is enforced + [filter useCustomPatternList:@[@"^branch\\d+:"]]; + XCTAssertFalse([filter shouldIgnoreURL:[NSURL URLWithString:@"fb123://"]]); + XCTAssertTrue([filter shouldIgnoreURL:[NSURL URLWithString:@"branch123://"]]); + + __block XCTestExpectation *expectation = [self expectationWithDescription:@"List updated"]; + [filter updatePatternListFromServerWithCompletion:^{ + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) { }]; + + // the retrieved list should match default pattern list + XCTAssertTrue([filter shouldIgnoreURL:[NSURL URLWithString:@"fb123://"]]); + XCTAssertFalse([filter shouldIgnoreURL:[NSURL URLWithString:@"branch123://"]]); +} + +@end diff --git a/BranchSDKTests/BNCUserAgentCollectorTests.m b/BranchSDKTests/BNCUserAgentCollectorTests.m new file mode 100644 index 000000000..e54cca1bc --- /dev/null +++ b/BranchSDKTests/BNCUserAgentCollectorTests.m @@ -0,0 +1,111 @@ +// +// BNCUserAgentCollectorTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 8/29/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +#import "BNCPreferenceHelper.h" +#import "BNCDeviceSystem.h" +#import "BNCUserAgentCollector.h" + +// expose private methods for unit testing +@interface BNCUserAgentCollector() + +- (NSString *)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion; +- (void)saveUserAgent:(NSString *)userAgent forSystemBuildVersion:(NSString *)systemBuildVersion; +- (void)collectUserAgentWithCompletion:(void (^)(NSString * _Nullable userAgent))completion; + +@end + +@interface BNCUserAgentCollectorTests : XCTestCase + +@end + +@implementation BNCUserAgentCollectorTests + ++ (void)setUp { + [BNCUserAgentCollectorTests resetPersistentData]; +} + +- (void)setUp { + +} + +- (void)tearDown { + [BNCUserAgentCollectorTests resetPersistentData]; +} + ++ (void)resetPersistentData { + BNCPreferenceHelper *preferences = [BNCPreferenceHelper sharedInstance]; + preferences.browserUserAgentString = nil; + preferences.lastSystemBuildVersion = nil; +} + +- (void)testResetPersistentData { + BNCPreferenceHelper *preferences = [BNCPreferenceHelper sharedInstance]; + XCTAssertNil(preferences.browserUserAgentString); + XCTAssertNil(preferences.lastSystemBuildVersion); +} + +- (void)testSaveAndLoadUserAgent { + NSString *systemBuildVersion = @"test"; + NSString *userAgent = @"UserAgent"; + + BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; + [collector saveUserAgent:userAgent forSystemBuildVersion:systemBuildVersion]; + NSString *expected = [collector loadUserAgentForSystemBuildVersion:systemBuildVersion]; + XCTAssertTrue([userAgent isEqualToString:expected]); +} + +- (void)testCollectUserAgent { + XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"]; + + BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; + [collector collectUserAgentWithCompletion:^(NSString * _Nullable userAgent) { + XCTAssertNotNil(userAgent); + XCTAssertTrue([userAgent containsString:@"AppleWebKit"]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:4.0 handler:^(NSError * _Nullable error) { + + }]; +} + +- (void)testLoadUserAgent_EmptyDataStore { + XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"]; + + BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; + [collector loadUserAgentWithCompletion:^(NSString * _Nullable userAgent) { + XCTAssertNotNil(userAgent); + XCTAssertTrue([userAgent containsString:@"AppleWebKit"]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) { + + }]; +} + +- (void)testLoadUserAgent_FilledDataStore { + XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"]; + NSString *savedUserAgent = @"UserAgent"; + + BNCUserAgentCollector *collector = [BNCUserAgentCollector new]; + [collector saveUserAgent:savedUserAgent forSystemBuildVersion:[BNCDeviceSystem new].systemBuildVersion]; + [collector loadUserAgentWithCompletion:^(NSString * _Nullable userAgent) { + XCTAssertNotNil(userAgent); + XCTAssertTrue([userAgent isEqualToString:savedUserAgent]); + XCTAssertFalse([userAgent containsString:@"AppleWebKit"]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) { + + }]; +} + +@end diff --git a/BranchSDKTests/Branch-SDK-Tests-Bridging-Header.h b/BranchSDKTests/Branch-SDK-Tests-Bridging-Header.h new file mode 100644 index 000000000..169bd50f3 --- /dev/null +++ b/BranchSDKTests/Branch-SDK-Tests-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Module headers for Branch SDK unit testing. +// + +#import "Branch.h" diff --git a/BranchSDKTests/BranchActivityItemTests.m b/BranchSDKTests/BranchActivityItemTests.m new file mode 100644 index 000000000..f117859f7 --- /dev/null +++ b/BranchSDKTests/BranchActivityItemTests.m @@ -0,0 +1,39 @@ +// +// BranchActivityItemTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 9/21/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import "Branch.h" + +@interface BranchActivityItemTests: XCTestCase +@end + +@implementation BranchActivityItemTests + +// Rework this test, it's not reliable. +//- (void)testGetBranchActivityItemWithAllParams { +// NSDictionary *params = @{@"key": @"value"}; +// NSString *feature = @"feature4"; +// NSString *stage = @"stage3"; +// NSArray *tags = @[@"tag3", @"tag4"]; +// NSString *campaign = @"campaign1"; +// NSString *alias = [[NSUUID UUID] UUIDString]; +// BranchActivityItemProvider *provider = [Branch getBranchActivityItemWithParams:params feature:feature stage:stage campaign:campaign tags:tags alias:alias]; +// sleep(2000); +// if ([[provider item] isKindOfClass:[NSURL class]]) { +// NSURL *urlObject = (NSURL *)[provider item]; +// NSString *url = [urlObject absoluteString]; +// +// NSLog(@"Provider URL as String: %@", url); +// +// XCTAssertTrue([url isEqualToString:[@"https://bnctestbed.app.link/" stringByAppendingString:alias]]); +// } else { +// XCTFail("Provider Data is not of type NSURL"); +// } +//} + +@end diff --git a/BranchSDKTests/BranchClassTests.m b/BranchSDKTests/BranchClassTests.m new file mode 100644 index 000000000..d1e7713f1 --- /dev/null +++ b/BranchSDKTests/BranchClassTests.m @@ -0,0 +1,265 @@ +// +// BranchClassTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 9/25/23. +// Copyright © 2023 Branch, Inc. All rights reserved. +// + +#import +#import "Branch.h" +#import "BranchConstants.h" +#import "BNCPasteboard.h" +#import "BNCAppGroupsData.h" +#import "BNCPartnerParameters.h" + +@interface BNCPreferenceHelper(Test) +// Expose internal private method to clear EEA data +- (void)writeObjectToDefaults:(NSString *)key value:(NSObject *)value; +@end + +@interface BranchClassTests : XCTestCase +@property (nonatomic, strong) Branch *branch; +@end + +@implementation BranchClassTests + +- (void)setUp { + [super setUp]; + self.branch = [Branch getInstance]; +} + +- (void)tearDown { + self.branch = nil; + [super tearDown]; +} + +- (void)testIsUserIdentified { + [self.branch setIdentity: @"userId"]; + XCTAssertTrue([self.branch isUserIdentified], @"User should be identified"); +} + +- (void)testDisableAdNetworkCallouts { + [self.branch disableAdNetworkCallouts:YES]; + XCTAssertTrue([BNCPreferenceHelper sharedInstance].disableAdNetworkCallouts, @"AdNetwork callouts should be disabled"); +} + +- (void)testSetNetworkTimeout { + [self.branch setNetworkTimeout:5.0]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].timeout, 5.0, @"Network timeout should be set to 5.0"); +} + +//- (void)testSetMaxRetries { +// [self.branch setMaxRetries:3]; +// XCTAssertEqual([BNCPreferenceHelper sharedInstance].retryCount, 3, @"Max retries should be set to 3"); +//} + +- (void)testSetRetryInterval { + [self.branch setRetryInterval:2.0]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].retryInterval, 2.0, @"Retry interval should be set to 2.0"); +} + +- (void)testSetRequestMetadataKeyAndValue { + [self.branch setRequestMetadataKey:@"key" value:@"value"]; + NSDictionary *metadata = [BNCPreferenceHelper sharedInstance].requestMetadataDictionary; + XCTAssertEqualObjects(metadata[@"key"], @"value"); +} + +- (void)testSetTrackingDisabled { + XCTAssertFalse([BNCPreferenceHelper sharedInstance].trackingDisabled); + + [Branch setTrackingDisabled:YES]; + XCTAssertTrue([BNCPreferenceHelper sharedInstance].trackingDisabled); + + [Branch setTrackingDisabled:NO]; + XCTAssertFalse([BNCPreferenceHelper sharedInstance].trackingDisabled); +} + +- (void)testCheckPasteboardOnInstall { + [self.branch checkPasteboardOnInstall]; + BOOL checkOnInstall = [BNCPasteboard sharedInstance].checkOnInstall; + XCTAssertTrue(checkOnInstall); +} + +- (void)testWillShowPasteboardToast_ShouldReturnYes { + [BNCPreferenceHelper sharedInstance].randomizedBundleToken = nil; + [BNCPasteboard sharedInstance].checkOnInstall = YES; + UIPasteboard.generalPasteboard.URL = [NSURL URLWithString:@"https://example.com"]; + + BOOL result = [self.branch willShowPasteboardToast]; + XCTAssertTrue(result); +} + +- (void)testWillShowPasteboardToast_ShouldReturnNo { + [BNCPreferenceHelper sharedInstance].randomizedBundleToken = @"some_token"; + [BNCPasteboard sharedInstance].checkOnInstall = NO; + + BOOL result = [self.branch willShowPasteboardToast]; + XCTAssertFalse(result); +} + +- (void)testSetAppClipAppGroup { + NSString *testAppGroup = @"testAppGroup"; + [self.branch setAppClipAppGroup:testAppGroup]; + NSString *actualAppGroup = [BNCAppGroupsData shared].appGroup; + + XCTAssertEqualObjects(testAppGroup, actualAppGroup); +} + +- (void)testClearPartnerParameters { + [self.branch addFacebookPartnerParameterWithName:@"ph" value:@"123456789"]; + [[BNCPartnerParameters shared] clearAllParameters]; + + NSDictionary *result = [[BNCPartnerParameters shared] parameterJson]; + XCTAssertEqual([result count], 0, @"Parameters should be empty after calling clearAllParameters"); +} + +- (void)testAddFacebookParameterWithName_Value { + [self.branch addFacebookPartnerParameterWithName:@"name" value:@"3D4F2BF07DC1BE38B20C653EE9A7E446158F84E525BBB98FEDF721CB5A40A346"]; + + NSDictionary *result = [[BNCPartnerParameters shared] parameterJson][@"fb"]; + XCTAssertEqualObjects(result[@"name"], @"3D4F2BF07DC1BE38B20C653EE9A7E446158F84E525BBB98FEDF721CB5A40A346", @"Should add parameter for Facebook"); +} + +- (void)testAddSnapParameterWithName_Value { + [self.branch addSnapPartnerParameterWithName:@"name" value:@"3D4F2BF07DC1BE38B20C653EE9A7E446158F84E525BBB98FEDF721CB5A40A346"]; + + NSDictionary *result = [[BNCPartnerParameters shared] parameterJson][@"snap"]; + XCTAssertEqualObjects(result[@"name"], @"3D4F2BF07DC1BE38B20C653EE9A7E446158F84E525BBB98FEDF721CB5A40A346", @"Should add parameter for Snap"); +} + +- (void)testGetFirstReferringBranchUniversalObject_ClickedBranchLink { + NSString *installParamsString = @"{\"$canonical_identifier\":\"content/12345\",\"$creation_timestamp\":1694557342247,\"$desktop_url\":\"https://example.com/home\",\"$og_description\":\"My Content Description\",\"$og_title\":\"My Content Title\",\"+click_timestamp\":1695749249,\"+clicked_branch_link\":1,\"+is_first_session\":1,\"+match_guaranteed\":1,\"custom\":\"data\",\"key1\":\"value1\",\"~campaign\":\"content 123 launch\",\"~channel\":\"facebook\",\"~creation_source\":3,\"~feature\":\"sharing\",\"~id\":1230269548213984984,\"~referring_link\":\"https://bnctestbed.app.link/uSPHktjO2Cb\"}"; + [[BNCPreferenceHelper sharedInstance] setInstallParams: installParamsString]; + + BranchUniversalObject *result = [self.branch getFirstReferringBranchUniversalObject];\ + XCTAssertNotNil(result); + XCTAssertEqualObjects(result.title, @"My Content Title"); + XCTAssertEqualObjects(result.canonicalIdentifier, @"content/12345"); +} + +- (void)testGetFirstReferringBranchUniversalObject_NotClickedBranchLink { + NSString *installParamsString = @"{\"+clicked_branch_link\":false,\"+is_first_session\":true}"; + [[BNCPreferenceHelper sharedInstance] setInstallParams: installParamsString]; + + BranchUniversalObject *result = [self.branch getFirstReferringBranchUniversalObject]; + XCTAssertNil(result); +} + +- (void)testGetFirstReferringBranchLinkProperties_ClickedBranchLink { + NSString *installParamsString = @"{\"+clicked_branch_link\":1,\"+is_first_session\":1,\"~campaign\":\"content 123 launch\"}"; + [[BNCPreferenceHelper sharedInstance] setInstallParams:installParamsString]; + + BranchLinkProperties *result = [self.branch getFirstReferringBranchLinkProperties]; + XCTAssertNotNil(result); + XCTAssertEqualObjects(result.campaign, @"content 123 launch"); +} + +- (void)testGetFirstReferringBranchLinkProperties_NotClickedBranchLink { + NSString *installParamsString = @"{\"+clicked_branch_link\":false,\"+is_first_session\":true}"; + [[BNCPreferenceHelper sharedInstance] setInstallParams:installParamsString]; + + BranchLinkProperties *result = [self.branch getFirstReferringBranchLinkProperties]; + XCTAssertNil(result); +} + +- (void)testGetFirstReferringParams { + NSString *installParamsString = @"{\"+clicked_branch_link\":true,\"+is_first_session\":true}"; + [[BNCPreferenceHelper sharedInstance] setInstallParams:installParamsString]; + + NSDictionary *result = [self.branch getFirstReferringParams]; + XCTAssertEqualObjects([result objectForKey:@"+clicked_branch_link"], @true); +} + +- (void)testGetLatestReferringParams { + NSString *sessionParamsString = @"{\"+clicked_branch_link\":true,\"+is_first_session\":false}"; + [[BNCPreferenceHelper sharedInstance] setSessionParams:sessionParamsString]; + + NSDictionary *result = [self.branch getLatestReferringParams]; + XCTAssertEqualObjects([result objectForKey:@"+clicked_branch_link"], @true); +} + +//- (void)testGetLatestReferringParamsSynchronous { +// NSString *sessionParamsString = @"{\"+clicked_branch_link\":true,\"+is_first_session\":false}"; +// [[BNCPreferenceHelper sharedInstance] setSessionParams:sessionParamsString]; +// +// NSDictionary *result = [self.branch getLatestReferringParamsSynchronous]; +// XCTAssertEqualObjects([result objectForKey:@"+clicked_branch_link"], @true); +//} + +- (void)testGetLatestReferringBranchUniversalObject_ClickedBranchLink { + NSString *sessionParamsString = @"{\"+clicked_branch_link\":1,\"+is_first_session\":false,\"$og_title\":\"My Latest Content\"}"; + [[BNCPreferenceHelper sharedInstance] setSessionParams:sessionParamsString]; + + BranchUniversalObject *result = [self.branch getLatestReferringBranchUniversalObject]; + XCTAssertNotNil(result); + XCTAssertEqualObjects(result.title, @"My Latest Content"); +} + +- (void)testGetLatestReferringBranchLinkProperties_ClickedBranchLink { + NSString *sessionParamsString = @"{\"+clicked_branch_link\":true,\"+is_first_session\":false,\"~campaign\":\"latest campaign\"}"; + [[BNCPreferenceHelper sharedInstance] setSessionParams:sessionParamsString]; + + BranchLinkProperties *result = [self.branch getLatestReferringBranchLinkProperties]; + XCTAssertNotNil(result); + XCTAssertEqualObjects(result.campaign, @"latest campaign"); +} + +- (void)testGetShortURL { + NSString *shortURL = [self.branch getShortURL]; + XCTAssertNotNil(shortURL, @"URL should not be nil"); + XCTAssertTrue([shortURL hasPrefix:@"https://"], @"URL should start with 'https://'"); +} + +- (void)testGetLongURLWithParamsAndChannelAndTagsAndFeatureAndStageAndAlias { + NSDictionary *params = @{@"key": @"value"}; + NSString *channel = @"channel1"; + NSArray *tags = @[@"tag1", @"tag2"]; + NSString *feature = @"feature1"; + NSString *stage = @"stage1"; + NSString *alias = @"alias1"; + + NSString *generatedURL = [self.branch getLongURLWithParams:params andChannel:channel andTags:tags andFeature:feature andStage:stage andAlias:alias]; + NSString *expectedURL = @"https://bnc.lt/a/key_live_hcnegAumkH7Kv18M8AOHhfgiohpXq5tB?tags=tag1&tags=tag2&alias=alias1&feature=feature1&stage=stage1&source=ios&data=eyJrZXkiOiJ2YWx1ZSJ9"; + + XCTAssertEqualObjects(generatedURL, expectedURL, @"URL should match the expected format"); +} + +- (void)testSetDMAParamsForEEA { + XCTAssertFalse([[BNCPreferenceHelper sharedInstance] eeaRegionInitialized]); + + [Branch setDMAParamsForEEA:FALSE AdPersonalizationConsent:TRUE AdUserDataUsageConsent:TRUE]; + XCTAssertTrue([[BNCPreferenceHelper sharedInstance] eeaRegionInitialized]); + XCTAssertFalse([BNCPreferenceHelper sharedInstance].eeaRegion); + XCTAssertTrue([BNCPreferenceHelper sharedInstance].adPersonalizationConsent); + XCTAssertTrue([BNCPreferenceHelper sharedInstance].adUserDataUsageConsent); + + // Manually clear values after testing + // By design, this API is meant to be set once and always set. However, in a test scenario it needs to be cleared. + [[BNCPreferenceHelper sharedInstance] writeObjectToDefaults:@"bnc_dma_eea" value:nil]; + [[BNCPreferenceHelper sharedInstance] writeObjectToDefaults:@"bnc_dma_ad_personalization" value:nil]; + [[BNCPreferenceHelper sharedInstance] writeObjectToDefaults:@"bnc_dma_ad_user_data" value:nil]; +} + +- (void)testSetConsumerProtectionAttributionLevel { + // Set to Reduced and check + Branch *branch = [Branch getInstance]; + [branch setConsumerProtectionAttributionLevel:BranchAttributionLevelReduced]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].attributionLevel, BranchAttributionLevelReduced); + + // Set to Minimal and check + [branch setConsumerProtectionAttributionLevel:BranchAttributionLevelMinimal]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].attributionLevel, BranchAttributionLevelMinimal); + + // Set to None and check + [branch setConsumerProtectionAttributionLevel:BranchAttributionLevelNone]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].attributionLevel, BranchAttributionLevelNone); + + // Set to Full and check + [branch setConsumerProtectionAttributionLevel:BranchAttributionLevelFull]; + XCTAssertEqual([BNCPreferenceHelper sharedInstance].attributionLevel, BranchAttributionLevelFull); + +} + + +@end diff --git a/BranchSDKTests/BranchConfigurationControllerTests.m b/BranchSDKTests/BranchConfigurationControllerTests.m new file mode 100644 index 000000000..74acc4106 --- /dev/null +++ b/BranchSDKTests/BranchConfigurationControllerTests.m @@ -0,0 +1,105 @@ +// +// BranchConfigurationControllerTests.m +// Branch-SDK-Tests +// +// Created by Nidhi Dixit on 6/12/25. +// + + +#import +#import "BranchConstants.h" +#import "BNCRequestFactory.h" +#import "BNCEncodingUtils.h" + +#if SWIFT_PACKAGE +@import BranchSwiftSDK; +#else +#import "BranchSDK/BranchSDK-Swift.h" +#endif + +@interface BranchConfigurationControllerTests : XCTestCase +@end + +@implementation BranchConfigurationControllerTests + +- (void)testSingletonInstance { + + ConfigurationController *instance1 = [ConfigurationController shared]; + XCTAssertNotNil(instance1); + + ConfigurationController *instance2 = [ConfigurationController shared]; + XCTAssertEqual(instance1, instance2); +} + +- (void)testPropertySettersAndGetters { + ConfigurationController *configController = [ConfigurationController shared]; + + NSString *keySource = BRANCH_KEY_SOURCE_GET_INSTANCE_API; + configController.branchKeySource = keySource; + XCTAssertTrue([configController.branchKeySource isEqualToString:keySource]); + + configController.deferInitForPluginRuntime = YES; + XCTAssertTrue(configController.deferInitForPluginRuntime); + configController.deferInitForPluginRuntime = NO; + XCTAssertFalse(configController.deferInitForPluginRuntime); + + configController.checkPasteboardOnInstall = YES; + XCTAssertTrue(configController.checkPasteboardOnInstall); + configController.checkPasteboardOnInstall = NO; + XCTAssertFalse(configController.checkPasteboardOnInstall); +} + +- (void)testGetConfiguration { + ConfigurationController *configController = [ConfigurationController shared]; + configController.branchKeySource = BRANCH_KEY_SOURCE_INFO_PLIST; + configController.deferInitForPluginRuntime = YES; + configController.checkPasteboardOnInstall = YES; + + NSDictionary *configDict = [configController getConfiguration]; + XCTAssertNotNil(configDict); + + XCTAssertTrue([configDict[BRANCH_REQUEST_KEY_BRANCH_KEY_SOURCE] isEqualToString:BRANCH_KEY_SOURCE_INFO_PLIST]); + XCTAssertEqualObjects(configDict[BRANCH_REQUEST_KEY_DEFER_INIT_FOR_PLUGIN_RUNTIME], @(YES)); + XCTAssertEqualObjects(configDict[BRANCH_REQUEST_KEY_CHECK_PASTEBOARD_ON_INSTALL], @(YES)); + + NSDictionary *frameworks = configDict[BRANCH_REQUEST_KEY_LINKED_FRAMEORKS]; + XCTAssertNotNil(frameworks); + + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_SUPPORT], @(YES)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_ATT_TRACKING_MANAGER], @(YES)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_FIREBASE_CRASHLYTICS], @(YES)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_SAFARI_SERVICES], @(NO)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_APP_ADS_ONDEVICE_CONVERSION], @(NO)); + +} + +- (void)testInstallRequestParams { + ConfigurationController *configController = [ConfigurationController shared]; + configController.branchKeySource = BRANCH_KEY_SOURCE_INFO_PLIST; + configController.deferInitForPluginRuntime = YES; + configController.checkPasteboardOnInstall = YES; + + NSString* requestUUID = [[NSUUID UUID ] UUIDString]; + NSNumber* requestCreationTimeStamp = BNCWireFormatFromDate([NSDate date]); + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:requestUUID TimeStamp:requestCreationTimeStamp]; + NSDictionary *installDict = [factory dataForInstallWithURLString:@"https://branch.io"]; + + NSDictionary *configDict = installDict[BRANCH_REQUEST_KEY_OPERATIONAL_METRICS]; + XCTAssertNotNil(configDict); + + XCTAssertTrue([configDict[BRANCH_REQUEST_KEY_BRANCH_KEY_SOURCE] isEqualToString:BRANCH_KEY_SOURCE_INFO_PLIST]); + XCTAssertEqualObjects(configDict[BRANCH_REQUEST_KEY_DEFER_INIT_FOR_PLUGIN_RUNTIME], @(YES)); + XCTAssertEqualObjects(configDict[BRANCH_REQUEST_KEY_CHECK_PASTEBOARD_ON_INSTALL], @(YES)); + + NSDictionary *frameworks = configDict[BRANCH_REQUEST_KEY_LINKED_FRAMEORKS]; + XCTAssertNotNil(frameworks); + + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_SUPPORT], @(YES)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_ATT_TRACKING_MANAGER], @(YES)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_FIREBASE_CRASHLYTICS], @(YES)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_SAFARI_SERVICES], @(NO)); + XCTAssertEqualObjects(frameworks[FRAMEWORK_AD_APP_ADS_ONDEVICE_CONVERSION], @(NO)); + +} + +@end diff --git a/BranchSDKTests/BranchEvent.Test.m b/BranchSDKTests/BranchEvent.Test.m new file mode 100644 index 000000000..ddb3d220f --- /dev/null +++ b/BranchSDKTests/BranchEvent.Test.m @@ -0,0 +1,575 @@ +// +// BranchEvent.Test.m +// Branch-SDK-Tests +// +// Created by Edward Smith on 8/15/17. +// Copyright © 2017 Branch Metrics. All rights reserved. +// +#import +#import "BNCPreferenceHelper.h" +#import "BranchConstants.h" +#import "BranchEvent.h" +#import "BNCDeviceInfo.h" +#import "NSError+Branch.h" + +@interface Branch (BranchEventTest) +- (void) processNextQueueItem; +@end + +@interface BranchEvent() + +- (NSString *)jsonStringForAdType:(BranchEventAdType)adType; + +// private BranchEvent methods used to check data before sending to network service. +- (NSDictionary *)buildEventDictionary; +- (BranchEventRequest *)buildRequestWithEventDictionary:(NSDictionary *)eventDictionary; + +@end + +@interface BranchEventTest : XCTestCase +@end + +@implementation BranchEventTest + +- (void)setUp { + [BNCPreferenceHelper sharedInstance].randomizedBundleToken = @"575759106028389737"; + [[BNCPreferenceHelper sharedInstance] clearInstrumentationDictionary]; +} + +- (void)tearDown { + +} + +// TODO: fix this test +- (void)testDescription { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventPurchase]; + event.transactionID = @"1234"; + event.currency = BNCCurrencyUSD; + event.revenue = [NSDecimalNumber decimalNumberWithString:@"10.50"]; + event.eventDescription= @"Event description."; + event.customData = (NSMutableDictionary*) @{ + @"Key1": @"Value1" + }; + + NSString *d = event.description; +// BNCTAssertEqualMaskedString(d, +// @""); +} + +- (void)testExampleSyntax { + BranchUniversalObject *contentItem = [BranchUniversalObject new]; + contentItem.canonicalIdentifier = @"item/123"; + contentItem.canonicalUrl = @"https://branch.io/item/123"; + contentItem.contentMetadata.ratingAverage = 5.0; + + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventCompleteRegistration]; + event.eventDescription = @"Product Search"; + event.searchQuery = @"product name"; + event.customData = @{ @"rating": @"5" }; + + [event logEvent]; +} + +- (void)testStandardInviteEvent { + + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventInvite]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"INVITE"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomInviteEvent { + + BranchEvent *event = [BranchEvent customEventWithName:@"INVITE"]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"INVITE"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardLoginEvent { + + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventLogin]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"LOGIN"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomLoginEvent { + + BranchEvent *event = [BranchEvent customEventWithName:@"LOGIN"]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"LOGIN"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardReserveEvent { + + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventReserve]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"RESERVE"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomReserveEvent { + + BranchEvent *event = [BranchEvent customEventWithName:@"RESERVE"]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"RESERVE"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardSubscribeEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventSubscribe]; + event.currency = BNCCurrencyUSD; + event.revenue = [NSDecimalNumber decimalNumberWithString:@"1.0"]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"SUBSCRIBE"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"currency"] isEqualToString:BNCCurrencyUSD]); + XCTAssert([eventData[@"revenue"] isEqual:[NSDecimalNumber decimalNumberWithString:@"1.0"]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomSubscribeEvent { + + BranchEvent *event = [BranchEvent customEventWithName:@"SUBSCRIBE"]; + event.currency = BNCCurrencyUSD; + event.revenue = [NSDecimalNumber decimalNumberWithString:@"1.0"]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"SUBSCRIBE"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"currency"] isEqualToString:BNCCurrencyUSD]); + XCTAssert([eventData[@"revenue"] isEqual:[NSDecimalNumber decimalNumberWithString:@"1.0"]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardStartTrialEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventStartTrial]; + event.currency = BNCCurrencyUSD; + event.revenue = [NSDecimalNumber decimalNumberWithString:@"1.0"]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"START_TRIAL"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"currency"] isEqualToString:BNCCurrencyUSD]); + XCTAssert([eventData[@"revenue"] isEqual:[NSDecimalNumber decimalNumberWithString:@"1.0"]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomStartTrialEvent { + + BranchEvent *event = [BranchEvent customEventWithName:@"START_TRIAL"]; + event.currency = BNCCurrencyUSD; + event.revenue = [NSDecimalNumber decimalNumberWithString:@"1.0"]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"START_TRIAL"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"currency"] isEqualToString:BNCCurrencyUSD]); + XCTAssert([eventData[@"revenue"] isEqual:[NSDecimalNumber decimalNumberWithString:@"1.0"]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardClickAdEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventClickAd]; + event.adType = BranchEventAdTypeBanner; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"CLICK_AD"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"ad_type"] isEqual:[event jsonStringForAdType:event.adType]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomClickAdEvent { + + BranchEvent *event = [BranchEvent customEventWithName:@"CLICK_AD"]; + event.adType = BranchEventAdTypeBanner; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"CLICK_AD"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"ad_type"] isEqual:[event jsonStringForAdType:event.adType]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardViewAdEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventViewAd]; + event.adType = BranchEventAdTypeBanner; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"VIEW_AD"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"ad_type"] isEqual:[event jsonStringForAdType:event.adType]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomViewAdEvent { + + BranchEvent *event = [BranchEvent customEventWithName:@"VIEW_AD"]; + event.adType = BranchEventAdTypeBanner; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + buo.canonicalIdentifier = @"item/12345"; + buo.canonicalUrl = @"https://branch.io/deepviews"; + buo.title = @"My Content Title"; + buo.contentDescription = @"my_product_description1"; + + NSMutableArray *contentItems = [NSMutableArray new]; + [contentItems addObject:buo]; + event.contentItems = contentItems; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"VIEW_AD"]); + XCTAssertNotNil(eventDictionary[@"content_items"]); + + NSDictionary *eventData = eventDictionary[@"event_data"]; + XCTAssert([eventData[@"ad_type"] isEqual:[event jsonStringForAdType:event.adType]]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardOptInEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventOptIn]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"OPT_IN"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomOptInEvent { + BranchEvent *event = [BranchEvent customEventWithName:@"OPT_IN"]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"OPT_IN"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardOptOutEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventOptOut]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"OPT_OUT"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomOptOutEvent { + BranchEvent *event = [BranchEvent customEventWithName:@"OPT_OUT"]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"OPT_OUT"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + + +- (void)testStandardInitiateStreamEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventInitiateStream]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"INITIATE_STREAM"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomInitiateStreamEvent { + BranchEvent *event = [BranchEvent customEventWithName:@"INITIATE_STREAM"]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"INITIATE_STREAM"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testStandardCompleteStreamEvent { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventCompleteStream]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"COMPLETE_STREAM"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testCustomCompleteStreamEvent { + BranchEvent *event = [BranchEvent customEventWithName:@"COMPLETE_STREAM"]; + + NSDictionary *eventDictionary = [event buildEventDictionary]; + XCTAssertNotNil(eventDictionary); + XCTAssert([eventDictionary[@"name"] isEqualToString:@"COMPLETE_STREAM"]); + + BranchEventRequest *request = [event buildRequestWithEventDictionary:eventDictionary]; + XCTAssert([request.serverURL.absoluteString containsString:@"branch.io/v2/event/standard"]); +} + +- (void)testJsonStringForAdTypeNone { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventViewAd]; + XCTAssertNil([event jsonStringForAdType:BranchEventAdTypeNone]); +} + +- (void)testJsonStringForAdTypeBanner { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventViewAd]; + XCTAssertTrue([[event jsonStringForAdType:BranchEventAdTypeBanner] isEqualToString:@"BANNER"]); +} + +- (void)testJsonStringForAdTypeInterstitial { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventViewAd]; + XCTAssertTrue([[event jsonStringForAdType:BranchEventAdTypeInterstitial] isEqualToString:@"INTERSTITIAL"]); +} + +- (void)testJsonStringForAdTypeRewardedVideo { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventViewAd]; + XCTAssertTrue([[event jsonStringForAdType:BranchEventAdTypeRewardedVideo] isEqualToString:@"REWARDED_VIDEO"]); +} + +- (void)testJsonStringForAdTypeNative { + BranchEvent *event = [BranchEvent standardEvent:BranchStandardEventViewAd]; + XCTAssertTrue([[event jsonStringForAdType:BranchEventAdTypeNative] isEqualToString:@"NATIVE"]); +} + +- (void) testCustomEventWithContentItem { + BranchUniversalObject *buo = [[BranchUniversalObject new] initWithTitle:@"buoTitle"]; + BranchEvent *event = [BranchEvent customEventWithName:@"testEvent" contentItem:buo]; + + XCTAssertTrue(event.contentItems.count == 1); + XCTAssertTrue([event.contentItems.firstObject.title isEqualToString:@"buoTitle"]); +} + +- (void)testLogEventWithCompletion_InvalidEventName { + XCTestExpectation *expectation = [self expectationWithDescription:@"Logging Event"]; + BranchEvent *event = [BranchEvent customEventWithName:@""]; + + [event logEventWithCompletion:^(BOOL success, NSError * _Nullable error) { + XCTAssertFalse(success, @"Success should be NO for invalid event name"); + XCTAssertNotNil(error, @"Error should not be nil for invalid event name"); + XCTAssertEqual(error.code, BNCGeneralError, @"Error code should match expected value for invalid event name"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:nil]; +} + + +@end diff --git a/BranchSDKTests/BranchEvent.Test.swift b/BranchSDKTests/BranchEvent.Test.swift new file mode 100644 index 000000000..49d819cc9 --- /dev/null +++ b/BranchSDKTests/BranchEvent.Test.swift @@ -0,0 +1,129 @@ +// +// BranchEvent.Test.swift +// Branch-SDK-Tests +// +// Created by edward on 10/9/17. +// Copyright © 2017 Branch, Inc. All rights reserved. +// + +import XCTest +/* +// TODO: fix this test class, requires modules which our testbed is not using +final class BranchEventTestSwift : XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + Branch.getInstance("key_live_foo") + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testBranchEvent() throws { + + // Set up the Branch Universal Object -- + + let branchUniversalObject = BranchUniversalObject.init() + branchUniversalObject.canonicalIdentifier = "item/12345" + branchUniversalObject.canonicalUrl = "https://branch.io/deepviews" + branchUniversalObject.title = "My Content Title" + branchUniversalObject.contentDescription = "my_product_description1" + branchUniversalObject.imageUrl = "https://test_img_url" + branchUniversalObject.keywords = [ "My_Keyword1", "My_Keyword2" ] + branchUniversalObject.creationDate = Date.init(timeIntervalSince1970:1501869445321.0/1000.0) + branchUniversalObject.expirationDate = Date.init(timeIntervalSince1970:212123232544.0/1000.0) + branchUniversalObject.locallyIndex = true + branchUniversalObject.publiclyIndex = false + + branchUniversalObject.contentMetadata.contentSchema = .commerceProduct + branchUniversalObject.contentMetadata.quantity = 2 + branchUniversalObject.contentMetadata.price = 23.20 + branchUniversalObject.contentMetadata.currency = .USD + branchUniversalObject.contentMetadata.sku = "1994320302" + branchUniversalObject.contentMetadata.productName = "my_product_name1" + branchUniversalObject.contentMetadata.productBrand = "my_prod_Brand1" + branchUniversalObject.contentMetadata.productCategory = .babyToddler + branchUniversalObject.contentMetadata.productVariant = "3T" + branchUniversalObject.contentMetadata.condition = .fair + + branchUniversalObject.contentMetadata.ratingAverage = 5; + branchUniversalObject.contentMetadata.ratingCount = 5; + branchUniversalObject.contentMetadata.ratingMax = 7; + branchUniversalObject.contentMetadata.rating = 6; + branchUniversalObject.contentMetadata.addressStreet = "Street_name1" + branchUniversalObject.contentMetadata.addressCity = "city1" + branchUniversalObject.contentMetadata.addressRegion = "Region1" + branchUniversalObject.contentMetadata.addressCountry = "Country1" + branchUniversalObject.contentMetadata.addressPostalCode = "postal_code" + branchUniversalObject.contentMetadata.latitude = 12.07 + branchUniversalObject.contentMetadata.longitude = -97.5 + branchUniversalObject.contentMetadata.imageCaptions = [ + "my_img_caption1", + "my_img_caption_2" + ] + branchUniversalObject.contentMetadata.customMetadata = [ + "Custom_Content_metadata_key1": "Custom_Content_metadata_val1", + "Custom_Content_metadata_key2": "Custom_Content_metadata_val2" + ] + + // Set up the event properties -- + + let event = BranchEvent.standardEvent(.purchase) + event.transactionID = "12344555" + event.currency = .USD; + event.revenue = 1.5 + event.shipping = 10.2 + event.tax = 12.3 + event.coupon = "test_coupon"; + event.affiliation = "test_affiliation"; + event.eventDescription = "Event _description"; + event.searchQuery = "Query" + event.customData = [ + "Custom_Event_Property_Key1": "Custom_Event_Property_val1", + "Custom_Event_Property_Key2": "Custom_Event_Property_val2" + ] + + var testDictionary = event.dictionary() + var dictionary = self.mutableDictionaryFromBundleJSON(withKey: "V2EventProperties") + XCTAssert((dictionary?.isEqual(to: testDictionary))!) + + testDictionary = branchUniversalObject.dictionary() as! [AnyHashable : Any] + dictionary = self.mutableDictionaryFromBundleJSON(withKey: "BranchUniversalObjectJSON") + dictionary!["$publicly_indexable"] = nil // Remove this value since we don't add false values. + XCTAssert((dictionary?.isEqual(to: testDictionary))!) + + event.contentItems = [ branchUniversalObject ] + event.logEvent() + } + + func testExampleSyntaxSwift() throws { + let contentItem = BranchUniversalObject.init() + contentItem.canonicalIdentifier = "item/123" + contentItem.canonicalUrl = "https://branch.io/item/123" + contentItem.contentMetadata.ratingAverage = 5.0; + + var event = BranchEvent.standardEvent(.spendCredits) + event.transactionID = "tx1234" + event.eventDescription = "Product Search" + event.searchQuery = "user search query terms for product xyz" + event.customData["Custom_Event_Property_Key1"] = "Custom_Event_Property_val1" + event.contentItems = [ contentItem ] + event.logEvent() + + event = BranchEvent.standardEvent(.viewItem) + event.logEvent(); + + // Quickly log an event: + BranchEvent.standardEvent(.viewItem).logEvent() + + // Quickly log an event with content: + let branchUniversalObject = BranchUniversalObject.init() + branchUniversalObject.canonicalIdentifier = "item/12345" + branchUniversalObject.canonicalUrl = "https://branch.io/deepviews" + branchUniversalObject.title = "My Content Title" + BranchEvent.standardEvent(.viewItem, withContentItem: branchUniversalObject).logEvent() + } +} +*/ diff --git a/BranchSDKTests/BranchLastAttributedTouchDataTests.m b/BranchSDKTests/BranchLastAttributedTouchDataTests.m new file mode 100644 index 000000000..908711125 --- /dev/null +++ b/BranchSDKTests/BranchLastAttributedTouchDataTests.m @@ -0,0 +1,63 @@ +// +// BranchLastAttributedTouchDataTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 9/18/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +#import "BNCJsonLoader.h" +#import "BranchLastAttributedTouchData.h" + +@interface BranchLastAttributedTouchDataTests : XCTestCase + +@end + +@implementation BranchLastAttributedTouchDataTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testBuildFromJSON { + NSDictionary *json = [BNCJsonLoader dictionaryFromJSONFileNamed:@"latd"]; + XCTAssertNotNil(json); + + BranchLastAttributedTouchData *latd = [BranchLastAttributedTouchData buildFromJSON:json]; + XCTAssertNotNil(latd); + XCTAssertNotNil(latd.lastAttributedTouchJSON); + XCTAssertNotNil(latd.attributionWindow); +} + +- (void)testBuildFromJSON_EmptyData { + NSDictionary *json = [BNCJsonLoader dictionaryFromJSONFileNamed:@"latd_empty_data"]; + XCTAssertNotNil(json); + + BranchLastAttributedTouchData *latd = [BranchLastAttributedTouchData buildFromJSON:json]; + XCTAssertNotNil(latd); + XCTAssertTrue(latd.lastAttributedTouchJSON.count == 0); +} + +- (void)testBuildFromJSON_MissingData { + NSDictionary *json = [BNCJsonLoader dictionaryFromJSONFileNamed:@"latd_missing_data"]; + XCTAssertNotNil(json); + + BranchLastAttributedTouchData *latd = [BranchLastAttributedTouchData buildFromJSON:json]; + XCTAssertNil(latd); +} + +- (void)testBuildFromJSON_MissingWindow { + NSDictionary *json = [BNCJsonLoader dictionaryFromJSONFileNamed:@"latd_missing_window"]; + XCTAssertNotNil(json); + + BranchLastAttributedTouchData *latd = [BranchLastAttributedTouchData buildFromJSON:json]; + XCTAssertNotNil(latd); + XCTAssertNil(latd.attributionWindow); +} + +@end diff --git a/BranchSDKTests/BranchLoggerTests.m b/BranchSDKTests/BranchLoggerTests.m new file mode 100644 index 000000000..1a4be3490 --- /dev/null +++ b/BranchSDKTests/BranchLoggerTests.m @@ -0,0 +1,261 @@ +// +// BranchLoggerTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 2/5/24. +// Copyright © 2024 Branch, Inc. All rights reserved. +// + +#import +#import "BranchLogger.h" +#import "Branch.h" + +@interface BranchLoggerTests : XCTestCase + +@end + +@implementation BranchLoggerTests + +// public API test +- (void)testEnableLoggingSetsCorrectDefaultLevel { + [[Branch getInstance] enableLogging]; + XCTAssertEqual([BranchLogger shared].logLevelThreshold, BranchLogLevelDebug, "Default log level should be Debug."); +} + +- (void)testLoggingEnabled_NOByDefault { + BranchLogger *logger = [BranchLogger new]; + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + [logger logError:@"msg" error:nil]; + + XCTAssertTrue(count == 0); +} + +- (void)testLoggingEnabled_Yes { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 1); + + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 2); +} + +- (void)testLoggingIgnoresNil { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + [logger logError:nil error:nil]; + XCTAssertTrue(count == 0); +} + +- (void)testLoggingIgnoresEmptyString { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + [logger logError:@"" error:nil]; + XCTAssertTrue(count == 0); +} + +- (void)testLoggingEnabled_YesThenNo { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + // one call + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 1); + + // disable, second call is ignored + logger.loggingEnabled = NO; + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 1); +} + +- (void)testLogLevel_DebugByDefault { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 1); + [logger logWarning:@"msg" error:nil]; + XCTAssertTrue(count == 2); + [logger logDebug:@"msg" error:nil]; + XCTAssertTrue(count == 3); + + // this should be ignored and the counter not incremented + [logger logVerbose:@"msg" error:nil]; + XCTAssertTrue(count == 3); +} + +- (void)testLogLevel_Error { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + logger.logLevelThreshold = BranchLogLevelError; + + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 1); + + // these should be ignored and the counter not incremented + [logger logWarning:@"msg" error:nil]; + XCTAssertTrue(count == 1); + [logger logDebug:@"msg" error:nil]; + XCTAssertTrue(count == 1); + [logger logVerbose:@"msg" error:nil]; + XCTAssertTrue(count == 1); +} + +- (void)testLogLevel_Warning { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + logger.logLevelThreshold = BranchLogLevelWarning; + + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 1); + [logger logWarning:@"msg" error:nil]; + XCTAssertTrue(count == 2); + + // this should be ignored and the counter not incremented + [logger logDebug:@"msg" error:nil]; + XCTAssertTrue(count == 2); + [logger logVerbose:@"msg" error:nil]; + XCTAssertTrue(count == 2); +} + +- (void)testLogLevel_Verbose { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + logger.logLevelThreshold = BranchLogLevelVerbose; + + + __block int count = 0; + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + count = count + 1; + }; + + [logger logError:@"msg" error:nil]; + XCTAssertTrue(count == 1); + [logger logWarning:@"msg" error:nil]; + XCTAssertTrue(count == 2); + [logger logDebug:@"msg" error:nil]; + XCTAssertTrue(count == 3); + [logger logVerbose:@"msg" error:nil]; + XCTAssertTrue(count == 4); +} + +- (void)testLogFormat_Default { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + NSString *expectedMessage = @"[BranchLoggerTests testLogFormat_Default] msg"; + + XCTAssertTrue([expectedMessage isEqualToString:message]); + XCTAssertTrue(logLevel == BranchLogLevelError); + XCTAssertNil(error); + }; + + [logger logError:@"msg" error:nil]; +} + +- (void)testLogFormat_NSError { + __block NSError *originalError = [NSError errorWithDomain:@"com.domain.test" code:200 userInfo:@{@"Error Message": @"Test Error"}]; + + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + NSString *expectedMessage = @"[BranchLoggerTests testLogFormat_NSError] msg"; + + XCTAssertTrue([expectedMessage isEqualToString:message]); + XCTAssertTrue(logLevel == BranchLogLevelError); + XCTAssertTrue(originalError == error); + }; + + [logger logError:@"msg" error:originalError]; +} + +- (void)testLogFormat_includeCallerDetailsNO { + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + logger.includeCallerDetails = NO; + + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + NSString *expectedMessage = @"msg"; + + XCTAssertTrue([expectedMessage isEqualToString:message]); + XCTAssertTrue(logLevel == BranchLogLevelError); + XCTAssertNil(error); + }; + + [logger logError:@"msg" error:nil]; +} + +- (void)testLogFormat_includeCallerDetailsNO_NSError { + __block NSError *originalError = [NSError errorWithDomain:@"com.domain.test" code:200 userInfo:@{@"Error Message": @"Test Error"}]; + + BranchLogger *logger = [BranchLogger new]; + logger.loggingEnabled = YES; + logger.includeCallerDetails = NO; + + logger.logCallback = ^(NSString * _Nonnull message, BranchLogLevel logLevel, NSError * _Nullable error) { + NSString *expectedMessage = @"msg"; + + XCTAssertTrue([expectedMessage isEqualToString:message]); + XCTAssertTrue(logLevel == BranchLogLevelError); + XCTAssertTrue(originalError == error); + }; + + [logger logError:@"msg" error:originalError]; +} + +- (void)testDefaultBranchLogFormat { + NSError *error = [NSError errorWithDomain:@"com.domain.test" code:200 userInfo:@{@"Error Message": @"Test Error"}]; + + NSString *expectedMessage = @"[BranchSDK][Error]msg NSError: Error Domain=com.domain.test Code=200 \"(null)\" UserInfo={Error Message=Test Error}"; + NSString *formattedMessage = [BranchLogger formatMessage:@"msg" logLevel:BranchLogLevelError error:error]; + + XCTAssertTrue([expectedMessage isEqualToString:formattedMessage]); +} + +@end diff --git a/BranchSDKTests/BranchPluginSupportTests.m b/BranchSDKTests/BranchPluginSupportTests.m new file mode 100644 index 000000000..d46b620fc --- /dev/null +++ b/BranchSDKTests/BranchPluginSupportTests.m @@ -0,0 +1,91 @@ +// +// BranchPluginSupportTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 1/25/22. +// Copyright © 2022 Branch, Inc. All rights reserved. +// + +#import +#import "BranchPluginSupport.h" + +@interface BranchPluginSupportTests : XCTestCase +@property (nonatomic, strong, readwrite) NSDictionary *deviceDescription; +@end + +@implementation BranchPluginSupportTests + +- (void)setUp { + self.deviceDescription = [[BranchPluginSupport new] deviceDescription]; +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testAppVersion { + // checks test app version + XCTAssert([@"1.0" isEqualToString:_deviceDescription[@"app_version"]]); +} + +- (void)testBrandName { + XCTAssert([@"Apple" isEqualToString:_deviceDescription[@"brand"]]); +} + +- (void)testModelName_Simulator { + // intel processor + bool x86_64 = [@"x86_64" isEqualToString:_deviceDescription[@"model"]]; + + // apple processor + bool arm64 = [@"arm64" isEqualToString:_deviceDescription[@"model"]]; + + XCTAssert(x86_64 || arm64); +} + +- (void)testOSName { + XCTAssertNotNil(_deviceDescription[@"os"]); + XCTAssert([_deviceDescription[@"os"] isEqualToString:[UIDevice currentDevice].systemName]); +} + +- (void)testOSVersion { + XCTAssertNotNil(_deviceDescription[@"os_version"]); + XCTAssert([_deviceDescription[@"os_version"] isEqualToString:[UIDevice currentDevice].systemVersion]); +} + +- (void)testEnvironment { + XCTAssert([@"FULL_APP" isEqualToString:_deviceDescription[@"environment"]]); +} + +- (void)testScreenWidth { + XCTAssert(_deviceDescription[@"screen_width"].intValue > 320); +} + +- (void)testScreenHeight { + XCTAssert(_deviceDescription[@"screen_height"].intValue > 320); +} + +- (void)testScreenScale { + XCTAssert(_deviceDescription[@"screen_dpi"].intValue > 0); +} + +- (void)testCountry { + NSString *locale = [NSLocale currentLocale].localeIdentifier; + XCTAssertNotNil(locale); + XCTAssert([locale containsString:_deviceDescription[@"country"]]); +} + +- (void)testLanguage { + NSString *locale = [NSLocale currentLocale].localeIdentifier; + XCTAssertNotNil(locale); + XCTAssert([locale containsString:_deviceDescription[@"language"]]); +} + +- (void)testLocalIPAddress { + NSString *address = _deviceDescription[@"local_ip"]; + XCTAssertNotNil(address); + + // shortest ipv4 is 7 + XCTAssert(address.length >= 7); +} + +@end diff --git a/BranchSDKTests/BranchQRCodeTests.m b/BranchSDKTests/BranchQRCodeTests.m new file mode 100644 index 000000000..3d1e1ffc6 --- /dev/null +++ b/BranchSDKTests/BranchQRCodeTests.m @@ -0,0 +1,154 @@ +// +// BranchQRCodeTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 4/14/22. +// Copyright © 2022 Branch, Inc. All rights reserved. +// + +#import +#import "Branch.h" +#import "BranchQRCode.h" +#import "BNCQRCodeCache.h" + +@interface BranchQRCodeTests : XCTestCase + +@end + +@implementation BranchQRCodeTests + +- (void)testNormalQRCodeDataWithAllSettings { + XCTestExpectation *expectation = [self expectationWithDescription:@"Fetching QR Code"]; + + BranchQRCode *qrCode = [BranchQRCode new]; + qrCode.width = @(1000); + qrCode.margin = @(1); + qrCode.codeColor = [UIColor blueColor]; + qrCode.backgroundColor = [UIColor whiteColor]; + qrCode.centerLogo = @"https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg"; + qrCode.imageFormat = BranchQRCodeImageFormatPNG; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + BranchLinkProperties *lp = [BranchLinkProperties new]; + + [qrCode getQRCodeAsData:buo linkProperties:lp completion:^(NSData * _Nonnull qrCode, NSError * _Nonnull error) { + XCTAssertNil(error); + XCTAssertNotNil(qrCode); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:10 handler:^(NSError *error) { + if (error) { + NSLog(@"Error Testing QR Code Cache: %@", error); + XCTFail(); + } + }]; +} + +- (void)testNormalQRCodeAsDataWithNoSettings { + XCTestExpectation *expectation = [self expectationWithDescription:@"Fetching QR Code"]; + + BranchQRCode *qrCode = [BranchQRCode new]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + BranchLinkProperties *lp = [BranchLinkProperties new]; + + [qrCode getQRCodeAsData:buo linkProperties:lp completion:^(NSData * _Nonnull qrCode, NSError * _Nonnull error) { + XCTAssertNil(error); + XCTAssertNotNil(qrCode); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:10 handler:^(NSError *error) { + if (error) { + NSLog(@"Error Testing QR Code Cache: %@", error); + XCTFail(); + } + }]; +} + +- (void)testNormalQRCodeWithInvalidLogoURL { + XCTestExpectation *expectation = [self expectationWithDescription:@"Fetching QR Code"]; + + BranchQRCode *qrCode = [BranchQRCode new]; + qrCode.centerLogo = @"https://branch.branch/notARealImageURL.jpg"; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + BranchLinkProperties *lp = [BranchLinkProperties new]; + + [qrCode getQRCodeAsData:buo linkProperties:lp completion:^(NSData * _Nonnull qrCode, NSError * _Nonnull error) { + XCTAssertNil(error); + XCTAssertNotNil(qrCode); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:10 handler:^(NSError *error) { + if (error) { + NSLog(@"Error Testing QR Code Cache: %@", error); + XCTFail(); + } + }]; +} + +- (void)testNormalQRCodeAsImage { + XCTestExpectation *expectation = [self expectationWithDescription:@"Fetching QR Code"]; + + BranchQRCode *qrCode = [BranchQRCode new]; + + BranchUniversalObject *buo = [BranchUniversalObject new]; + BranchLinkProperties *lp = [BranchLinkProperties new]; + + [qrCode getQRCodeAsImage:buo linkProperties:lp completion:^(UIImage * _Nonnull qrCode, NSError * _Nonnull error) { + XCTAssertNil(error); + XCTAssertNotNil(qrCode); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:10 handler:^(NSError *error) { + if (error) { + NSLog(@"Error Testing QR Code Cache: %@", error); + XCTFail(); + } + }]; +} + +- (void)testQRCodeCache { + XCTestExpectation *expectation = [self expectationWithDescription:@"Fetching QR Code"]; + + BranchQRCode *myQRCode = [BranchQRCode new]; + BranchUniversalObject *buo = [BranchUniversalObject new]; + BranchLinkProperties *lp = [BranchLinkProperties new]; + + [myQRCode getQRCodeAsData:buo linkProperties:lp completion:^(NSData * _Nonnull qrCode, NSError * _Nonnull error) { + + XCTAssertNil(error); + XCTAssertNotNil(qrCode); + + NSMutableDictionary *parameters = [NSMutableDictionary new]; + NSMutableDictionary *settings = [NSMutableDictionary new]; + + settings[@"image_format"] = @"PNG"; + settings[@"width"] = @(300); + settings[@"margin"] = @(1); + + parameters[@"qr_code_settings"] = settings; + parameters[@"data"] = [NSMutableDictionary new]; + parameters[@"branch_key"] = [Branch branchKey]; + + + NSData *cachedQRCode = [[BNCQRCodeCache sharedInstance] checkQRCodeCache:parameters]; + + XCTAssertEqual(cachedQRCode, qrCode); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:10 handler:^(NSError *error) { + if (error) { + NSLog(@"Error Testing QR Code Cache: %@", error); + XCTFail(); + } + }]; +} + + +@end diff --git a/BranchSDKTests/BranchSDKTests.m b/BranchSDKTests/BranchSDKTests.m new file mode 100644 index 000000000..7a37169d8 --- /dev/null +++ b/BranchSDKTests/BranchSDKTests.m @@ -0,0 +1,36 @@ +// +// BranchSDKTests.m +// BranchSDKTests +// +// Created by Nidhi Dixit on 7/6/25. +// + +#import + +@interface BranchSDKTests : XCTestCase + +@end + +@implementation BranchSDKTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/BranchSDKTests/BranchSDKTests.xctestplan b/BranchSDKTests/BranchSDKTests.xctestplan new file mode 100644 index 000000000..d72383a21 --- /dev/null +++ b/BranchSDKTests/BranchSDKTests.xctestplan @@ -0,0 +1,33 @@ +{ + "configurations" : [ + { + "id" : "9EAB663B-7807-43EE-AD4E-658E8734932A", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "targetForVariableExpansion" : { + "containerPath" : "container:BranchSDK.xcodeproj", + "identifier" : "E7CA755E2E1B59F5002EFB40", + "name" : "BranchSDKTestsHostApp" + } + }, + "testTargets" : [ + { + "parallelizable" : false, + "skippedTests" : [ + "BNCDisableAdNetworkCalloutsTests", + "BranchActivityItemTests" + ], + "target" : { + "containerPath" : "container:BranchSDK.xcodeproj", + "identifier" : "E7CA74EA2E1B4F75002EFB40", + "name" : "BranchSDKTests" + } + } + ], + "version" : 1 +} diff --git a/BranchSDKTests/BranchShareLinkTests.m b/BranchSDKTests/BranchShareLinkTests.m new file mode 100644 index 000000000..c41fdb618 --- /dev/null +++ b/BranchSDKTests/BranchShareLinkTests.m @@ -0,0 +1,38 @@ +// +// BranchShareLinkTests.m +// Branch-SDK-Tests +// +// Created by Nipun Singh on 5/5/22. +// Copyright © 2022 Branch, Inc. All rights reserved. +// + +#import +#import "BranchShareLink.h" +#import "BranchLinkProperties.h" +#import "Branch.h" + +@interface BranchShareLinkTests : XCTestCase + +@end + +@implementation BranchShareLinkTests + +- (void)testAddLPLinkMetadata { + BranchUniversalObject *buo = [[BranchUniversalObject alloc] initWithCanonicalIdentifier:@"test/001"]; + BranchLinkProperties *lp = [[BranchLinkProperties alloc] init]; + + BranchShareLink *bsl = [[BranchShareLink alloc] initWithUniversalObject:buo linkProperties:lp]; + + if (@available(iOS 13.0, macCatalyst 13.1, *)) { + NSURL *imageURL = [NSURL URLWithString:@"https://cdn.branch.io/branch-assets/1598575682753-og_image.png"]; + NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; + UIImage *iconImage = [UIImage imageWithData:imageData]; + + [bsl addLPLinkMetadata:@"Test Preview Title" icon:iconImage]; + XCTAssertNotNil([bsl lpMetaData]); + } else { + XCTAssertTrue(true); + } +} + +@end diff --git a/BranchSDKTests/BranchUniversalObjectTests.m b/BranchSDKTests/BranchUniversalObjectTests.m new file mode 100644 index 000000000..9a7112e31 --- /dev/null +++ b/BranchSDKTests/BranchUniversalObjectTests.m @@ -0,0 +1,446 @@ +// +// BranchUniversalObjectTests.m +// Branch-TestBed +// +// Created by Edward Smith on 8/15/17. +// Copyright © 2017 Branch Metrics. All rights reserved. +// + +#import +#import "BranchUniversalObject.h" + +@interface BranchUniversalObjectTests : XCTestCase +@end + +@implementation BranchUniversalObjectTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +#pragma mark BranchContentMetadata tests + +- (BranchContentMetadata *)branchContentMetadataWithAllPropertiesSet { + BranchContentMetadata *metadata = [BranchContentMetadata new]; + metadata.contentSchema = BranchContentSchemaOther; + metadata.quantity = 10; + metadata.price = [[NSDecimalNumber alloc] initWithDouble:5.5]; + metadata.currency = BNCCurrencyUSD; + metadata.sku = @"testSKU"; + metadata.productName = @"testProductName"; + metadata.productBrand = @"testProductBrand"; + metadata.productCategory = BNCProductCategoryApparel; + metadata.productVariant = @"testProductVariant"; + metadata.condition = BranchConditionNew; + metadata.ratingAverage = 3.5; + metadata.ratingCount = 2; + metadata.ratingMax = 4; + metadata.rating = 3; + metadata.addressStreet = @"195 Page Mill Road"; + metadata.addressCity = @"Palo Alto"; + metadata.addressRegion = @"CA"; + metadata.addressCountry = @"USA"; + metadata.addressPostalCode = @"94306"; + metadata.latitude = 37.426; + metadata.longitude = -122.138; + + metadata.imageCaptions = @[ + @"Hello World", + @"Goodbye World" + ].mutableCopy; + + metadata.customMetadata = @{ + @"custom0": @"custom data 0" + }.mutableCopy; + + return metadata; +} + +- (void)verifyBranchContentMetadataWithAllProperties:(BranchContentMetadata *)metadata { + XCTAssertNotNil(metadata); + + XCTAssertEqual(BranchContentSchemaOther, metadata.contentSchema); + XCTAssertTrue([@(10) isEqualToNumber:@(metadata.quantity)]); + XCTAssertTrue([[[NSDecimalNumber alloc] initWithDouble:5.5] isEqualToNumber:metadata.price]); + + XCTAssertEqual(BNCCurrencyUSD, metadata.currency); + XCTAssertTrue([@"testSKU" isEqualToString:metadata.sku]); + XCTAssertTrue([@"testProductName" isEqualToString:metadata.productName]); + XCTAssertTrue([@"testProductBrand" isEqualToString:metadata.productBrand]); + XCTAssertEqual(BNCProductCategoryApparel, metadata.productCategory); + XCTAssertTrue([@"testProductVariant" isEqualToString:metadata.productVariant]); + XCTAssertEqual(BranchConditionNew, metadata.condition); + XCTAssertTrue([@(3.5) isEqualToNumber:@(metadata.ratingAverage)]); + XCTAssertTrue([@(2) isEqualToNumber:@(metadata.ratingCount)]); + XCTAssertTrue([@(4) isEqualToNumber:@(metadata.ratingMax)]); + XCTAssertTrue([@(3) isEqualToNumber:@(metadata.rating)]); + + XCTAssertTrue([@"195 Page Mill Road" isEqualToString:metadata.addressStreet]); + XCTAssertTrue([@"Palo Alto" isEqualToString:metadata.addressCity]); + XCTAssertTrue([@"CA" isEqualToString:metadata.addressRegion]); + XCTAssertTrue([@"USA" isEqualToString:metadata.addressCountry]); + XCTAssertTrue([@"94306" isEqualToString:metadata.addressPostalCode]); + + XCTAssertTrue([@(37.426) isEqualToNumber:@(metadata.latitude)]); + XCTAssertTrue([@(-122.138) isEqualToNumber:@(metadata.longitude)]); + + XCTAssertNotNil(metadata.imageCaptions); + XCTAssertTrue(metadata.imageCaptions.count == 2); + XCTAssertTrue([metadata.imageCaptions[0] isEqualToString:@"Hello World"]); + XCTAssertTrue([metadata.imageCaptions[1] isEqualToString:@"Goodbye World"]); + + XCTAssertNotNil(metadata.customMetadata); + XCTAssertTrue(metadata.customMetadata.allKeys.count == 1); +} + +- (void)verifyBranchContentMetadataDictionaryWithAllPropertiesSet:(NSDictionary *)dict { + XCTAssertTrue([@"OTHER" isEqualToString:dict[@"$content_schema"]]); + XCTAssertTrue([@(10) isEqualToNumber:dict[@"$quantity"]]); + XCTAssertTrue([[[NSDecimalNumber alloc] initWithDouble:5.5] isEqualToNumber:dict[@"$price"]]); + XCTAssertTrue([@"USD" isEqualToString:dict[@"$currency"]]); + XCTAssertTrue([@"testSKU" isEqualToString:dict[@"$sku"]]); + XCTAssertTrue([@"testProductName" isEqualToString:dict[@"$product_name"]]); + XCTAssertTrue([@"testProductBrand" isEqualToString:dict[@"$product_brand"]]); + XCTAssertTrue([@"Apparel & Accessories" isEqualToString:dict[@"$product_category"]]); + XCTAssertTrue([@"testProductVariant" isEqualToString:dict[@"$product_variant"]]); + XCTAssertTrue([@"NEW" isEqualToString:dict[@"$condition"]]); + + XCTAssertTrue([@(3.5) isEqualToNumber:dict[@"$rating_average"]]); + XCTAssertTrue([@(2) isEqualToNumber:dict[@"$rating_count"]]); + XCTAssertTrue([@(4) isEqualToNumber:dict[@"$rating_max"]]); + XCTAssertTrue([@(3) isEqualToNumber:dict[@"$rating"]]); + + XCTAssertTrue([@"195 Page Mill Road" isEqualToString:dict[@"$address_street"]]); + XCTAssertTrue([@"Palo Alto" isEqualToString:dict[@"$address_city"]]); + XCTAssertTrue([@"CA" isEqualToString:dict[@"$address_region"]]); + XCTAssertTrue([@"USA" isEqualToString:dict[@"$address_country"]]); + XCTAssertTrue([@"94306" isEqualToString:dict[@"$address_postal_code"]]); + + XCTAssertTrue([@(37.426) isEqualToNumber:dict[@"$latitude"]]); + XCTAssertTrue([@(-122.138) isEqualToNumber:dict[@"$longitude"]]); + + XCTAssertTrue([dict[@"$image_captions"] isKindOfClass:NSArray.class]); + NSArray *tmp = dict[@"$image_captions"]; + XCTAssertTrue(tmp.count == 2); + XCTAssertTrue([tmp[0] isEqualToString:@"Hello World"]); + XCTAssertTrue([tmp[1] isEqualToString:@"Goodbye World"]); + + XCTAssertTrue([@"custom data 0" isEqualToString:dict[@"custom0"]]); +} + +- (void)testBranchContentMetadataCreation_NoPropertiesSet { + BranchContentMetadata *metadata = [BranchContentMetadata new]; + + // most properties default to nil. primitives default to 0 + XCTAssertNil(metadata.contentSchema); + XCTAssertEqual(0, metadata.quantity); + XCTAssertNil(metadata.price); + XCTAssertNil(metadata.currency); + XCTAssertNil(metadata.sku); + XCTAssertNil(metadata.productName); + XCTAssertNil(metadata.productBrand); + XCTAssertNil(metadata.productCategory); + XCTAssertNil(metadata.productVariant); + XCTAssertNil(metadata.condition); + XCTAssertEqual(0, metadata.ratingAverage); + XCTAssertEqual(0, metadata.ratingCount); + XCTAssertEqual(0, metadata.ratingMax); + XCTAssertEqual(0, metadata.rating); + XCTAssertNil(metadata.addressStreet); + XCTAssertNil(metadata.addressCity); + XCTAssertNil(metadata.addressRegion); + XCTAssertNil(metadata.addressCountry); + XCTAssertNil(metadata.addressPostalCode); + XCTAssertEqual(0, metadata.latitude); + XCTAssertEqual(0, metadata.longitude); + + // defaults to an empty array + XCTAssertNotNil(metadata.imageCaptions); + XCTAssertTrue(metadata.imageCaptions.count == 0); + + // defaults to an empty dictionary + XCTAssertNotNil(metadata.customMetadata); + XCTAssertTrue(metadata.customMetadata.allKeys.count == 0); +} + +- (void)testBranchContentMetadataCreation_AllPropertiesSet { + BranchContentMetadata *metadata = [self branchContentMetadataWithAllPropertiesSet]; + [self verifyBranchContentMetadataWithAllProperties:metadata]; +} + +- (void)testBranchContentMetadataDictionary_NoPropertiesSet { + BranchContentMetadata *metadata = [BranchContentMetadata new]; + NSDictionary *dict = metadata.dictionary; + + XCTAssertNotNil(dict); + XCTAssertTrue(dict.allKeys.count == 0); +} + +- (void)testBranchContentMetadataDictionary_AllPropertiesSet { + BranchContentMetadata *metadata = [self branchContentMetadataWithAllPropertiesSet]; + NSDictionary *dict = metadata.dictionary; + + XCTAssertNotNil(dict); + XCTAssertTrue(dict.allKeys.count == 23); + [self verifyBranchContentMetadataDictionaryWithAllPropertiesSet:dict]; +} + +- (void)testBranchContentMetadata_contentMetadataWithDictionary { + BranchContentMetadata *original = [self branchContentMetadataWithAllPropertiesSet]; + NSDictionary *dict = [original dictionary]; + + BranchContentMetadata *metadata = [BranchContentMetadata contentMetadataWithDictionary:dict]; + [self verifyBranchContentMetadataWithAllProperties:metadata]; +} + +- (void)testBranchContentMetadataDescription_NoPropertiesSet { + BranchContentMetadata *metadata = [BranchContentMetadata new]; + NSString *desc = [metadata description]; + XCTAssertTrue([desc containsString:@"BranchContentMetadata"]); + XCTAssertTrue([desc containsString:@"schema: (null) userData: 0 items"]); +} + +- (void)testBranchContentMetadataDescription_AllPropertiesSet { + BranchContentMetadata *metadata = [self branchContentMetadataWithAllPropertiesSet]; + NSString *desc = [metadata description]; + XCTAssertTrue([desc containsString:@"BranchContentMetadata"]); + XCTAssertTrue([desc containsString:@"schema: OTHER userData: 1 items"]); +} + +#pragma mark BranchUniversalObject tests + +- (BranchUniversalObject *)branchUniversalObjectWithAllPropertiesSet { + BranchUniversalObject *buo = [[BranchUniversalObject alloc] initWithCanonicalIdentifier:@"io.branch.testObject"]; + buo.canonicalUrl = @"https://branch.io"; + buo.title = @"Test Title"; + buo.contentDescription = @"the quick brown fox jumps over the lazy dog"; + buo.imageUrl = @"https://branch.io"; + buo.keywords = @[@"keyword1", @"keyword2"]; + buo.expirationDate = [NSDate dateWithTimeIntervalSinceNow:1]; + buo.locallyIndex = YES; + buo.publiclyIndex = YES; + + buo.contentMetadata = [self branchContentMetadataWithAllPropertiesSet]; + return buo; +} + +- (void)verifyBranchUniversalObjectWithAllPropertiesSet:(BranchUniversalObject *)buo { + XCTAssertNotNil(buo); + XCTAssertTrue([@"https://branch.io" isEqualToString:buo.canonicalUrl]); + XCTAssertTrue([@"Test Title" isEqualToString:buo.title]); + XCTAssertTrue([@"the quick brown fox jumps over the lazy dog" isEqualToString:buo.contentDescription]); + XCTAssertTrue([@"https://branch.io" isEqualToString:buo.imageUrl]); + + XCTAssertTrue(buo.keywords.count == 2); + XCTAssertTrue([buo.keywords[0] isEqualToString:@"keyword1"]); + XCTAssertTrue([buo.keywords[1] isEqualToString:@"keyword2"]); + + XCTAssertTrue([buo.creationDate compare:buo.expirationDate] == NSOrderedAscending); + + XCTAssertTrue(buo.locallyIndex); + XCTAssertTrue(buo.publiclyIndex); + + [self verifyBranchContentMetadataWithAllProperties:buo.contentMetadata]; +} + +- (void)verifyBranchUniversalObjectDictionaryWithAllPropertiesSet:(NSDictionary *)dict { + XCTAssertTrue([@"io.branch.testObject" isEqualToString:dict[@"$canonical_identifier"]]); + XCTAssertTrue([@"https://branch.io" isEqualToString:dict[@"$canonical_url"]]); + XCTAssertTrue([@"Test Title" isEqualToString:dict[@"$og_title"]]); + XCTAssertTrue([@"the quick brown fox jumps over the lazy dog" isEqualToString:dict[@"$og_description"]]); + XCTAssertTrue([@"https://branch.io" isEqualToString:dict[@"$og_image_url"]]); + + XCTAssertTrue(dict[@"$locally_indexable"]); + XCTAssertTrue(dict[@"$publicly_indexable"]); + + XCTAssertTrue([dict[@"$creation_timestamp"] isKindOfClass:NSNumber.class]); + XCTAssertTrue([dict[@"$exp_date"] isKindOfClass:NSNumber.class]); + NSNumber *creationDate = dict[@"$creation_timestamp"]; + NSNumber *expirationDate = dict[@"$exp_date"]; + XCTAssertTrue([creationDate compare:expirationDate] == NSOrderedAscending); + + XCTAssertTrue([dict[@"$keywords"] isKindOfClass:NSArray.class]); + NSArray *tmp = dict[@"$keywords"]; + XCTAssertTrue(tmp.count == 2); + XCTAssertTrue([tmp[0] isEqualToString:@"keyword1"]); + XCTAssertTrue([tmp[1] isEqualToString:@"keyword2"]); + + // the BranchContentMetadata dictionary is NOT in a sub dictionary, it is merged in at the top level + [self verifyBranchContentMetadataDictionaryWithAllPropertiesSet:dict]; +} + +- (void)testBranchUniversalObjectCreation { + BranchUniversalObject *buo = [BranchUniversalObject new]; + XCTAssertNotNil(buo); + + XCTAssertNil(buo.canonicalIdentifier); + XCTAssertNil(buo.canonicalUrl); + XCTAssertNil(buo.title); + XCTAssertNil(buo.contentDescription); + XCTAssertNil(buo.imageUrl); + XCTAssertNil(buo.keywords); + XCTAssertNil(buo.creationDate); + XCTAssertNil(buo.expirationDate); + XCTAssertFalse(buo.locallyIndex); + XCTAssertFalse(buo.publiclyIndex); + + XCTAssertNotNil(buo.contentMetadata); +} + +- (void)testBranchUniversalObjectCreation_initWithCanonicalIdentifier { + BranchUniversalObject *buo = [[BranchUniversalObject alloc] initWithCanonicalIdentifier:@"io.branch.testObject"]; + XCTAssertNotNil(buo); + + XCTAssertTrue([@"io.branch.testObject" isEqualToString:buo.canonicalIdentifier]); + XCTAssertNil(buo.canonicalUrl); + XCTAssertNil(buo.title); + XCTAssertNil(buo.contentDescription); + XCTAssertNil(buo.imageUrl); + XCTAssertNil(buo.keywords); + XCTAssertNotNil(buo.creationDate); + XCTAssertNil(buo.expirationDate); + XCTAssertFalse(buo.locallyIndex); + XCTAssertFalse(buo.publiclyIndex); + + XCTAssertNotNil(buo.contentMetadata); +} + +- (void)testBranchUniversalObjectCreation_initWithTitle { + BranchUniversalObject *buo = [[BranchUniversalObject alloc] initWithTitle:@"Test Title"]; + XCTAssertNotNil(buo); + + XCTAssertNil(buo.canonicalIdentifier); + XCTAssertNil(buo.canonicalUrl); + XCTAssertTrue([@"Test Title" isEqualToString:buo.title]); + XCTAssertNil(buo.contentDescription); + XCTAssertNil(buo.imageUrl); + XCTAssertNil(buo.keywords); + XCTAssertNotNil(buo.creationDate); + XCTAssertNil(buo.expirationDate); + XCTAssertFalse(buo.locallyIndex); + XCTAssertFalse(buo.publiclyIndex); + + XCTAssertNotNil(buo.contentMetadata); +} + +- (void)testBranchUniversalObject_AllPropertiesSet { + BranchUniversalObject *buo = [self branchUniversalObjectWithAllPropertiesSet]; + [self verifyBranchUniversalObjectWithAllPropertiesSet:buo]; +} + +- (void)testBranchUniversalObjectDescription_AllPropertiesSet { + BranchUniversalObject *buo = [self branchUniversalObjectWithAllPropertiesSet]; + NSString *desc = buo.description; + + // Verifies a few properties used to generate the description string + XCTAssertTrue([desc containsString:@"BranchUniversalObject"]); + XCTAssertTrue([desc containsString:@"canonicalIdentifier: io.branch.testObject"]); + XCTAssertTrue([desc containsString:@"title: Test Title"]); + XCTAssertTrue([desc containsString:@"contentDescription: the quick brown fox jumps over the lazy dog"]); +} + +- (void)testBranchUniversalObjectDictionary_AllPropertiesSet { + BranchUniversalObject *buo = [self branchUniversalObjectWithAllPropertiesSet]; + NSDictionary *dict = buo.dictionary; + + XCTAssertNotNil(dict); + XCTAssertTrue(dict.allKeys.count == 33); + [self verifyBranchUniversalObjectDictionaryWithAllPropertiesSet:dict]; +} + +- (void)testBranchUniversalObject_objectWithDictionary { + BranchUniversalObject *original = [self branchUniversalObjectWithAllPropertiesSet]; + NSDictionary *dict = [original dictionary]; + + BranchUniversalObject *buo = [BranchUniversalObject objectWithDictionary:dict]; + [self verifyBranchUniversalObjectWithAllPropertiesSet:buo]; +} + +- (void)testBranchUniversalObject_getDictionaryWithCompleteLinkProperties_NoLinkPropertiesSet { + BranchUniversalObject *original = [self branchUniversalObjectWithAllPropertiesSet]; + BranchLinkProperties *linkProperties = [BranchLinkProperties new]; + NSDictionary *dict = [original getDictionaryWithCompleteLinkProperties:linkProperties]; + + XCTAssertNotNil(dict); + XCTAssertTrue([@(0) isEqualToNumber:dict[@"~duration"]]); +} + +- (void)testBranchUniversalObject_getDictionaryWithCompleteLinkProperties_AllLinkPropertiesSet { + BranchUniversalObject *original = [self branchUniversalObjectWithAllPropertiesSet]; + BranchLinkProperties *linkProperties = [BranchLinkProperties new]; + + linkProperties.tags = @[@"tag1", @"tag2"]; + linkProperties.feature = @"test feature"; + linkProperties.alias = @"test alias"; + linkProperties.channel = @"test channel"; + linkProperties.matchDuration = 10; + + // BranchUniversalObject.controlParams overwrites BranchContentMetadata.customMetadata + linkProperties.controlParams = @{ + @"testControlParam": @"test control param", + //@"custom0": @"test control param" + }; + + NSDictionary *dict = [original getDictionaryWithCompleteLinkProperties:linkProperties]; + XCTAssertNotNil(dict); + XCTAssertTrue(dict.allKeys.count == 39); + + XCTAssertTrue([@(10) isEqualToNumber:dict[@"~duration"]]); + XCTAssertTrue([@"test alias" isEqualToString:dict[@"~alias"]]); + XCTAssertTrue([@"test channel" isEqualToString:dict[@"~channel"]]); + XCTAssertTrue([@"test feature" isEqualToString:dict[@"~feature"]]); + + XCTAssertTrue([@"test control param" isEqualToString:dict[@"testControlParam"]]); + + // BranchUniversalObject fields are at the top level of the dictionary + [self verifyBranchUniversalObjectDictionaryWithAllPropertiesSet:dict]; +} + +- (void)testBranchUniversalObject_getParamsForServerRequestWithAddedLinkProperties_NoLinkPropertiesSet { + BranchUniversalObject *original = [self branchUniversalObjectWithAllPropertiesSet]; + BranchLinkProperties *linkProperties = [BranchLinkProperties new]; + + // Nothing is added with this call + NSDictionary *dict = [original getParamsForServerRequestWithAddedLinkProperties:linkProperties]; + + XCTAssertNotNil(dict); + + // BranchUniversalObject fields are at the top level of the dictionary + [self verifyBranchUniversalObjectDictionaryWithAllPropertiesSet:dict]; +} + +- (void)testBranchUniversalObject_getParamsForServerRequestWithAddedLinkProperties_AllLinkPropertiesSet { + BranchUniversalObject *original = [self branchUniversalObjectWithAllPropertiesSet]; + BranchLinkProperties *linkProperties = [BranchLinkProperties new]; + linkProperties.tags = @[@"tag1", @"tag2"]; + linkProperties.feature = @"test feature"; + linkProperties.alias = @"test alias"; + linkProperties.channel = @"test channel"; + linkProperties.matchDuration = 10; + + // BranchUniversalObject.controlParams overwrites BranchContentMetadata.customMetadata + linkProperties.controlParams = @{ + @"testControlParam": @"test control param", + //@"custom0": @"test control param" + }; + + NSDictionary *dict = [original getParamsForServerRequestWithAddedLinkProperties:linkProperties]; + XCTAssertNotNil(dict); + XCTAssertTrue(dict.allKeys.count == 34); + + // only the control parameters are in the dictionary + XCTAssertTrue([@"test control param" isEqualToString:dict[@"testControlParam"]]); + XCTAssertNil(dict[@"~duration"]); + XCTAssertNil(dict[@"~alias"]); + XCTAssertNil(dict[@"~channel"]); + XCTAssertNil(dict[@"~feature"]); + + // BranchUniversalObject fields are at the top level of the dictionary + [self verifyBranchUniversalObjectDictionaryWithAllPropertiesSet:dict]; +} + +@end diff --git a/BranchSDKTests/DispatchToIsolationQueueTests.m b/BranchSDKTests/DispatchToIsolationQueueTests.m new file mode 100644 index 000000000..89043aa94 --- /dev/null +++ b/BranchSDKTests/DispatchToIsolationQueueTests.m @@ -0,0 +1,76 @@ +// +// BNCPreInitBlockTests.m +// Branch-SDK-Tests +// +// Created by Benas Klastaitis on 12/9/19. +// Copyright © 2019 Branch, Inc. All rights reserved. +// + +#import +// #import "Branch.h" + +@interface DispatchToIsolationQueueTests : XCTestCase +// @property (nonatomic, strong, readwrite) Branch *branch; +// @property (nonatomic, strong, readwrite) BNCPreferenceHelper *prefHelper; +@end + +// this is an integration test, needs to be moved to a new target +@implementation DispatchToIsolationQueueTests + +- (void)setUp { + // self.branch = [Branch getInstance]; + // self.prefHelper = [[BNCPreferenceHelper alloc] init]; +} + +- (void)tearDown { + // [self.prefHelper setRequestMetadataKey:@"$marketing_cloud_visitor_id" value:@"dummy"]; +} + +- (void)testPreInitBlock { + // __block XCTestExpectation *expectation = [self expectationWithDescription:@""]; + + // [self.branch dispatchToIsolationQueue:^{ + // dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + // [self.branch setRequestMetadataKey:@"$marketing_cloud_visitor_id" value:@"adobeID123"]; + // dispatch_semaphore_signal(semaphore); + // }); + // dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + // }]; + + // [self.branch initSessionWithLaunchOptions:nil andRegisterDeepLinkHandlerUsingBranchUniversalObject: + // ^ (BranchUniversalObject * _Nullable universalObject, + // BranchLinkProperties * _Nullable linkProperties, + // NSError * _Nullable error) { + // [expectation fulfill]; + // }]; + + // // test that session initialization blocking works + // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + // id initializationStatus = [self.branch valueForKey:@"initializationStatus"]; + // XCTAssertTrue([self enumIntValueFromId:initializationStatus] == 0);// uninitialized + // XCTAssertNil([[self.prefHelper requestMetadataDictionary] objectForKey:@"$marketing_cloud_visitor_id"]); + // }); + + // // test that initialization does happen afterwards and that pre init block was executed + // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + // id initializationStatus = [self.branch valueForKey:@"initializationStatus"]; + // XCTAssertTrue([self enumIntValueFromId:initializationStatus] == 2);// initialized + + // XCTAssertTrue([[[self.prefHelper requestMetadataDictionary] objectForKey:@"$marketing_cloud_visitor_id"] isEqualToString:@"adobeID123"]); + // }); + + + // [self waitForExpectationsWithTimeout:6 handler:^(NSError * _Nullable error) { + // NSLog(@"%@", error); + // }]; +} + +// -(int)enumIntValueFromId:(id)enumValueId { +// if (![enumValueId respondsToSelector:@selector(intValue)]) +// return -1; + +// return [enumValueId intValue]; +// } + +@end diff --git a/BranchSDKTests/Info.plist b/BranchSDKTests/Info.plist new file mode 100644 index 000000000..ba72822e8 --- /dev/null +++ b/BranchSDKTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/BranchSDKTests/NSErrorBranchTests.m b/BranchSDKTests/NSErrorBranchTests.m new file mode 100644 index 000000000..4399b7484 --- /dev/null +++ b/BranchSDKTests/NSErrorBranchTests.m @@ -0,0 +1,54 @@ +/** + @file NSErrorBranchCategoryTests.m + @package Branch-SDK + @brief Branch error tests. + + @author Edward Smith + @date August 2017 + @copyright Copyright © 2017 Branch. All rights reserved. +*/ + +#import +#import "NSError+Branch.h" + +@interface NSErrorBranchTests : XCTestCase +@end + +@implementation NSErrorBranchTests + +- (void)testErrorDomain { + XCTAssertTrue([@"io.branch.sdk.error" isEqualToString:[NSError bncErrorDomain]]); +} + +- (void)testError { + NSError *error = [NSError branchErrorWithCode:BNCInitError]; + XCTAssert(error.domain == [NSError bncErrorDomain]); + XCTAssert(error.code == BNCInitError); + XCTAssert([error.localizedDescription isEqualToString: + @"The Branch user session has not been initialized."] + ); +} + +- (void)testErrorWithUnderlyingError { + NSError *underlyingError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo:nil]; + NSError *error = [NSError branchErrorWithCode:BNCServerProblemError error:underlyingError]; + + XCTAssert(error.domain == [NSError bncErrorDomain]); + XCTAssert(error.code == BNCServerProblemError); + XCTAssert([error.localizedDescription isEqualToString: @"Trouble reaching the Branch servers, please try again shortly."]); + + XCTAssert(error.userInfo[NSUnderlyingErrorKey] == underlyingError); + XCTAssert([error.localizedFailureReason isEqualToString:@"The file doesn’t exist."]); +} + +- (void)testErrorWithMessage { + NSString *message = [NSString stringWithFormat:@"Network operation of class '%@' does not conform to the BNCNetworkOperationProtocol.", NSStringFromClass([self class])]; + NSError *error = [NSError branchErrorWithCode:BNCNetworkServiceInterfaceError localizedMessage:message]; + + XCTAssert(error.domain == [NSError bncErrorDomain]); + XCTAssert(error.code == BNCNetworkServiceInterfaceError); + XCTAssert([error.localizedDescription isEqualToString: @"The underlying network service does not conform to the BNCNetworkOperationProtocol."]); + XCTAssert([error.localizedFailureReason isEqualToString: @"Network operation of class 'NSErrorBranchTests' does not conform to the BNCNetworkOperationProtocol."]); +} + +@end diff --git a/BranchSDKTests/NSMutableDictionaryBranchTests.m b/BranchSDKTests/NSMutableDictionaryBranchTests.m new file mode 100644 index 000000000..9fadbefb2 --- /dev/null +++ b/BranchSDKTests/NSMutableDictionaryBranchTests.m @@ -0,0 +1,389 @@ +// +// NSMutableDictionaryBranchTests.m +// Branch-SDK-Tests +// +// Created by Ernest Cho on 2/9/24. +// Copyright © 2024 Branch, Inc. All rights reserved. +// + +#import +#import "NSMutableDictionary+Branch.h" + +@interface NSMutableDictionaryBranchTests : XCTestCase + +@end + +@implementation NSMutableDictionaryBranchTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testSafeSetObject_StringString { + NSMutableDictionary *dict = [NSMutableDictionary new]; + + [dict bnc_safeSetObject:@"foo" forKey:@"bar"]; + XCTAssertTrue(dict.count == 1); + XCTAssertTrue([@"foo" isEqualToString:dict[@"bar"]]); +} + +- (void)testSafeSetObject_NilString { + NSMutableDictionary *dict = [NSMutableDictionary new]; + + [dict bnc_safeSetObject:nil forKey:@"bar"]; + XCTAssertTrue(dict.count == 0); +} + +- (void)testSafeSetObject_StringNil { + NSMutableDictionary *dict = [NSMutableDictionary new]; + + [dict bnc_safeSetObject:@"foo" forKey:nil]; + XCTAssertTrue(dict.count == 0); +} + +- (void)testSafeAddEntriesFromDictionary { + + // NSStrings are never copied, so use NSMutableStrings + NSMutableDictionary *other = [NSMutableDictionary new]; + other[@"foo"] = [[NSMutableString alloc] initWithString:@"bar"]; + other[@"hello"] = [[NSMutableString alloc] initWithString:@"world"]; + + NSMutableDictionary *dict = [NSMutableDictionary new]; + [dict bnc_safeAddEntriesFromDictionary:other]; + + NSArray *keyset = [other allKeys]; + for (id key in keyset) { + id original = other[key]; + id copy = dict[key]; + + // same object value + XCTAssertTrue([original isEqual:copy]); + + // different object instance + XCTAssertTrue(original != copy); + } +} + +- (void)testSafeAddEntriesFromDictionary_NestedArray { + + // NSStrings are never copied, so use NSMutableStrings + NSMutableDictionary *other = [NSMutableDictionary new]; + other[@"foo"] = [[NSMutableString alloc] initWithString:@"bar"]; + other[@"hello"] = [[NSMutableString alloc] initWithString:@"world"]; + + NSMutableArray *array = [NSMutableArray new]; + [array addObject:[[NSMutableString alloc] initWithString:@"dog"]]; + [array addObject:[[NSMutableString alloc] initWithString:@"cat"]]; + [array addObject:[[NSMutableString alloc] initWithString:@"child"]]; + + other[@"good"] = array; + + NSMutableDictionary *dict = [NSMutableDictionary new]; + [dict bnc_safeAddEntriesFromDictionary:other]; + + NSArray *keyset = [other allKeys]; + for (id key in keyset) { + id original = other[key]; + id copy = dict[key]; + + // same object value + XCTAssertTrue([original isEqual:copy]); + + // different object instance + XCTAssertTrue(original != copy); + } + + // confirm that copyItems is a one layer deep copy + NSArray *arrayCopy = dict[@"good"]; + XCTAssertTrue(array.count == arrayCopy.count); + XCTAssertTrue(array != arrayCopy); + + for (int i=0; i +#import "NSString+Branch.h" + +#define _countof(array) (sizeof(array)/sizeof(array[0])) + +@interface NSStringBranchTests : XCTestCase +@end + +@implementation NSStringBranchTests + +- (void)testMaskEqual { + XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"0123"]); + XCTAssertFalse([@"0123" bnc_isEqualToMaskedString:@"012"]); + XCTAssertFalse([@"0123" bnc_isEqualToMaskedString:@"01234"]); + XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"01*3"]); + XCTAssertFalse([@"0123" bnc_isEqualToMaskedString:@"01*4"]); + XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"*123"]); + XCTAssertTrue([@"0123" bnc_isEqualToMaskedString:@"012*"]); + XCTAssertTrue([@"日本語123日本語" bnc_isEqualToMaskedString:@"日本語123日本語"]); + XCTAssertFalse([@"日本語123日本語" bnc_isEqualToMaskedString:@"日本語1234本語"]); + XCTAssertTrue([@"日本語123日本語" bnc_isEqualToMaskedString:@"日本語***日本語"]); + XCTAssertTrue([@"日本語123日本語" bnc_isEqualToMaskedString:@"***123日本語"]); +} + +@end diff --git a/Package.swift b/Package.swift index 0ae916e32..9def3332c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -9,6 +9,7 @@ let package = Package( .tvOS(.v12), ], products: [ + // Main product that clients will import .library( name: "BranchSDK", targets: ["BranchSDK"]), @@ -16,18 +17,15 @@ let package = Package( dependencies: [ ], targets: [ + // Main Objective-C SDK target with modern NSOperationQueue implementation .target( name: "BranchSDK", - path: "Sources", - sources: [ - "BranchSDK/" - ], - resources: [ - .copy("Resources/PrivacyInfo.xcprivacy"), - ], - publicHeadersPath: "BranchSDK/Public/", + dependencies: [], + path: "Sources/BranchSDK", + publicHeadersPath: "Public", cSettings: [ - .headerSearchPath("BranchSDK/Private"), + .headerSearchPath("Private"), + .define("SWIFT_PACKAGE") ], linkerSettings: [ .linkedFramework("CoreServices"), @@ -36,6 +34,6 @@ let package = Package( .linkedFramework("CoreSpotlight", .when(platforms: [.iOS])), .linkedFramework("AdServices", .when(platforms: [.iOS])) ] - ), + ) ] ) diff --git a/Sources/BranchSDK/BNCServerRequestOperation.m b/Sources/BranchSDK/BNCServerRequestOperation.m new file mode 100644 index 000000000..c06159fbf --- /dev/null +++ b/Sources/BranchSDK/BNCServerRequestOperation.m @@ -0,0 +1,294 @@ +// +// BNCServerRequestOperation.m +// BranchSDK +// +// Created by Nidhi Dixit on 7/22/25. +// Updated: Modern Swift Concurrency bridge +// + +#import "Private/BNCServerRequestOperation.h" +#import "BranchOpenRequest.h" +#import "BranchInstallRequest.h" +#import "BranchEvent.h" +#import "BranchLogger.h" +#import "NSError+Branch.h" +#import "BNCCallbackMap.h" + +// Swift integration - BranchSwiftSDK module is loaded dynamically at runtime +// No compile-time import needed to avoid circular dependencies in SPM +#if !SWIFT_PACKAGE +// Swift bridging header - auto-generated by Xcode when Swift files are present +#if __has_include("BranchSDK/BranchSDK-Swift.h") +#import "BranchSDK/BranchSDK-Swift.h" +#endif +#endif + +@interface BNCServerRequestOperation () +@property (nonatomic, assign, readwrite, getter = isExecuting) BOOL executing; +@property (nonatomic, assign, readwrite, getter = isFinished) BOOL finished; +@property (nonatomic, strong, nullable) id swiftOperation; // BranchRequestOperation for iOS 13+ +@end + +@implementation BNCServerRequestOperation { + BNCServerRequest *_request; +} + +@synthesize executing = _executing; +@synthesize finished = _finished; + +- (instancetype)initWithRequest:(BNCServerRequest *)request { + self = [super init]; + if (self) { + _request = request; + _executing = NO; + _finished = NO; + } + return self; +} + +- (BOOL)isAsynchronous { + return YES; +} + + /*TODO - This can be used for initSafetyCheck and adding dependencies +- (BOOL)isReady { + BOOL ready = [super isReady]; + if (ready) { + + } + return ready; +}*/ + +- (void)setExecuting:(BOOL)executing { + [self willChangeValueForKey:@"isExecuting"]; + _executing = executing; + [self didChangeValueForKey:@"isExecuting"]; +} + +- (void)setFinished:(BOOL)finished { + [self willChangeValueForKey:@"isFinished"]; + _finished = finished; + [self didChangeValueForKey:@"isFinished"]; +} + +- (void)start { + // Use modern Swift Concurrency implementation on iOS 13+ + if (@available(iOS 13.0, tvOS 13.0, *)) { + if ([self shouldUseSwiftImplementation]) { + [self startWithSwiftOperation]; + return; + } + } + + // Fallback to legacy Objective-C implementation + [self startObjectiveCOperation]; +} + +#pragma mark - Swift Concurrency Bridge (iOS 13+) + +- (BOOL)shouldUseSwiftImplementation API_AVAILABLE(ios(13.0), tvos(13.0)) { + // Enable Swift implementation by default on supported platforms + // Can be controlled by feature flag if needed + return YES; +} + +- (void)startWithSwiftOperation API_AVAILABLE(ios(13.0), tvos(13.0)) { + // Create Swift operation with modern async/await + // Try module-qualified name first, then unqualified name + Class swiftOperationClass = NSClassFromString(@"BranchRequestOperation"); + if (!swiftOperationClass) { + swiftOperationClass = NSClassFromString(@"Branch.BranchRequestOperation"); + } + if (!swiftOperationClass) { + swiftOperationClass = NSClassFromString(@"BranchSDK.BranchRequestOperation"); + } + if (!swiftOperationClass) { + [[BranchLogger shared] logWarning:@"BranchRequestOperation (Swift) not available. Falling back to Objective-C implementation." error:nil]; + [self startObjectiveCOperation]; + return; + } + + // Initialize Swift operation + SEL initSelector = NSSelectorFromString(@"initWithRequest:serverInterface:branchKey:preferenceHelper:"); + + // Allocate instance first + id swiftInstance = [swiftOperationClass alloc]; + + // Check if instance responds to initializer + if (![swiftInstance respondsToSelector:initSelector]) { + [[BranchLogger shared] logWarning:@"BranchRequestOperation initializer not found. Falling back to Objective-C implementation." error:nil]; + [self startObjectiveCOperation]; + return; + } + + BNCPreferenceHelper *preferenceHelper = self.preferenceHelper ?: [BNCPreferenceHelper sharedInstance]; + + // Create Swift operation using dynamic invocation + NSMethodSignature *signature = [swiftInstance methodSignatureForSelector:initSelector]; + if (!signature) { + [[BranchLogger shared] logWarning:@"Could not get method signature for Swift initializer. Falling back to Objective-C implementation." error:nil]; + [self startObjectiveCOperation]; + return; + } + + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setSelector:initSelector]; + [invocation setTarget:swiftInstance]; + [invocation setArgument:&_request atIndex:2]; + [invocation setArgument:&_serverInterface atIndex:3]; + [invocation setArgument:&_branchKey atIndex:4]; + [invocation setArgument:&preferenceHelper atIndex:5]; + [invocation invoke]; + + id __unsafe_unretained tempOperation; + [invocation getReturnValue:&tempOperation]; + self.swiftOperation = tempOperation; + + if (!self.swiftOperation) { + [[BranchLogger shared] logWarning:@"Failed to create Swift operation. Falling back to Objective-C implementation." error:nil]; + [self startObjectiveCOperation]; + return; + } + + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Using Swift Concurrency implementation for request: %@", self.request.requestUUID] error:nil]; + + // Forward operation lifecycle to Swift implementation + SEL startSelector = NSSelectorFromString(@"start"); + if ([self.swiftOperation respondsToSelector:startSelector]) { + [self.swiftOperation performSelector:startSelector]; + } + + // Monitor Swift operation state and reflect in Objective-C operation + [self observeSwiftOperation]; +} + +- (void)observeSwiftOperation API_AVAILABLE(ios(13.0), tvos(13.0)) { + // Add KVO observers for Swift operation state + [self.swiftOperation addObserver:self + forKeyPath:@"isExecuting" + options:NSKeyValueObservingOptionNew + context:nil]; + [self.swiftOperation addObserver:self + forKeyPath:@"isFinished" + options:NSKeyValueObservingOptionNew + context:nil]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (object == self.swiftOperation) { + if ([keyPath isEqualToString:@"isExecuting"]) { + self.executing = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; + } else if ([keyPath isEqualToString:@"isFinished"]) { + self.finished = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; + // Clean up observers when finished + [self.swiftOperation removeObserver:self forKeyPath:@"isExecuting"]; + [self.swiftOperation removeObserver:self forKeyPath:@"isFinished"]; + } + } +} + +#pragma mark - Legacy Objective-C Implementation + +- (void)startObjectiveCOperation { + if (self.isCancelled) { + [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"Operation cancelled before starting: %@", self.request.requestUUID] error:nil]; + self.finished = YES; + return; + } + + self.executing = YES; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"BNCServerRequestOperation (Objective-C) starting for request: %@", self.request.requestUUID] error:nil]; + + // Check if tracking is disabled + if (Branch.trackingDisabled) { + [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"Tracking disabled. Skipping request: %@", self.request.requestUUID] error:nil]; + self.executing = NO; + self.finished = YES; + return; + } + + BNCPreferenceHelper *preferenceHelper = self.preferenceHelper ?: [BNCPreferenceHelper sharedInstance]; + + // Session validation for requests + if (!([self.request isKindOfClass:[BranchInstallRequest class]])) { + if (!preferenceHelper.randomizedBundleToken) { + [[BranchLogger shared] logError:[NSString stringWithFormat:@"User session not initialized (missing bundle token). Dropping request: %@", self.request.requestUUID] error:nil]; + BNCPerformBlockOnMainThreadSync(^{ + [self.request processResponse:nil error:[NSError branchErrorWithCode:BNCInitError]]; + }); + self.executing = NO; + self.finished = YES; + return; + } + } else if (!([self.request isKindOfClass:[BranchOpenRequest class]])) { + if (!preferenceHelper.randomizedDeviceToken || !preferenceHelper.sessionID || !preferenceHelper.randomizedBundleToken) { + [[BranchLogger shared] logError:[NSString stringWithFormat:@"Missing session items (device token or session ID or bundle token). Dropping request: %@", self.request.requestUUID] error:nil]; + BNCPerformBlockOnMainThreadSync(^{ + [self.request processResponse:nil error:[NSError branchErrorWithCode:BNCInitError]]; + }); + self.executing = NO; + self.finished = YES; + return; + } + } + + // TODO: Handle specific `BranchOpenRequest` lock + // `waitForOpenResponseLock` will block the current thread (the NSOperation's background thread) + // until the global open response lock is released. This ensures proper sequencing + // if another init session is in progress. + /* if ([self.request isKindOfClass:[BranchOpenRequest class]]) { + [[BranchLogger shared] logDebug:[NSString stringWithFormat:@"BranchOpenRequest detected. Waiting for open response lock for %@", self.request.requestUUID] error:nil]; + [BranchOpenRequest waitForOpenResponseLock]; + }*/ + + [self.request makeRequest:self.serverInterface + key:self.branchKey + callback:^(BNCServerResponse *response, NSError *error) { + BNCPerformBlockOnMainThreadSync(^{ + [self.request processResponse:response error:error]; + if ([self.request isKindOfClass:[BranchEventRequest class]]) { + [[BNCCallbackMap shared] callCompletionForRequest:self.request withSuccessStatus:(error == nil) error:error]; + } + }); + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"BNCServerRequestOperation (Objective-C) finished for request: %@", self.request.requestUUID] error:nil]; + self.executing = NO; + self.finished = YES; + + }]; +} + +- (void)cancel { + [super cancel]; // Sets `isCancelled` to YES + + // Forward cancellation to Swift operation if active + if (@available(iOS 13.0, tvOS 13.0, *)) { + if (self.swiftOperation) { + SEL cancelSelector = NSSelectorFromString(@"cancel"); + if ([self.swiftOperation respondsToSelector:cancelSelector]) { + [self.swiftOperation performSelector:cancelSelector]; + } + } + } + + if (!self.isExecuting) { + self.finished = YES; + [[BranchLogger shared] logWarning:[NSString stringWithFormat:@"BNCServerRequestOperation cancelled before execution for request: %@", self.request.requestUUID] error:nil]; + } else { + [[BranchLogger shared] logWarning:[NSString stringWithFormat:@"BNCServerRequestOperation cancelled during execution for request: %@", self.request.requestUUID] error:nil]; + } +} + +static inline void BNCPerformBlockOnMainThreadSync(dispatch_block_t block) { + if (block) { + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } + } +} + +@end diff --git a/Sources/BranchSDK/BNCServerRequestQueue.m b/Sources/BranchSDK/BNCServerRequestQueue.m old mode 100755 new mode 100644 index c518fccce..097012ea0 --- a/Sources/BranchSDK/BNCServerRequestQueue.m +++ b/Sources/BranchSDK/BNCServerRequestQueue.m @@ -9,143 +9,116 @@ #import "BNCServerRequestQueue.h" #import "BNCPreferenceHelper.h" - -// Analytics requests #import "BranchInstallRequest.h" #import "BranchOpenRequest.h" #import "BranchEvent.h" - #import "BranchLogger.h" +#import "Private/BNCServerRequestOperation.h" +#import "Branch.h" -@interface BNCServerRequestQueue() -@property (strong, nonatomic) NSMutableArray *queue; -@end +@interface BNCServerRequestQueue () +@property (strong, nonatomic) NSOperationQueue *operationQueue; +@property (strong, nonatomic) BNCServerInterface *serverInterface; +@property (copy, nonatomic) NSString *branchKey; +@property (strong, nonatomic) BNCPreferenceHelper *preferenceHelper; + +@end + @implementation BNCServerRequestQueue - (instancetype)init { self = [super init]; - if (!self) return self; - - self.queue = [NSMutableArray new]; + if (self) { + self.operationQueue = [NSOperationQueue new]; + // Set maxConcurrentOperationCount to 1 for serial execution + self.operationQueue.maxConcurrentOperationCount = 1; + self.operationQueue.name = @"com.branch.sdk.serverRequestQueue"; + } return self; } -- (void)enqueue:(BNCServerRequest *)request { - @synchronized (self) { - if (request) { - [self.queue addObject:request]; - } - } +- (void)configureWithServerInterface:(BNCServerInterface *)serverInterface + branchKey:(NSString *)branchKey + preferenceHelper:(BNCPreferenceHelper *)preferenceHelper { + self.serverInterface = serverInterface; + self.branchKey = branchKey; + self.preferenceHelper = preferenceHelper; } -- (void)insert:(BNCServerRequest *)request at:(NSUInteger)index { - @synchronized (self) { - if (index > self.queue.count) { - [[BranchLogger shared] logError:@"Invalid queue operation: index out of bound!" error:nil]; - return; - } - if (request) { - [self.queue insertObject:request atIndex:index]; - } - } +- (void)enqueue:(BNCServerRequest *)request{ + [self enqueue:request withPriority:NSOperationQueuePriorityNormal]; } -- (BNCServerRequest *)dequeue { - @synchronized (self) { - BNCServerRequest *request = nil; - if (self.queue.count > 0) { - request = [self.queue objectAtIndex:0]; - [self.queue removeObjectAtIndex:0]; - } - return request; +- (void)enqueue:(BNCServerRequest *)request withPriority:(NSOperationQueuePriority)priority{ + if (!request) { + [[BranchLogger shared] logError:@"Attempted to enqueue nil request." error:nil]; + return; } -} + + BNCServerRequestOperation *operation = [[BNCServerRequestOperation alloc] initWithRequest:request]; + + operation.serverInterface = self.serverInterface; + operation.branchKey = self.branchKey; + operation.preferenceHelper = self.preferenceHelper; + operation.queuePriority = priority; -- (BNCServerRequest *)removeAt:(NSUInteger)index { - @synchronized (self) { - BNCServerRequest *request = nil; - if (index >= self.queue.count) { - [[BranchLogger shared] logError:@"Invalid queue operation: index out of bound!" error:nil]; - return nil; - } - request = [self.queue objectAtIndex:index]; - [self.queue removeObjectAtIndex:index]; - return request; - } -} + [self.operationQueue addOperation:operation]; -- (void)remove:(BNCServerRequest *)request { - @synchronized (self) { - [self.queue removeObject:request]; - } -} - -- (BNCServerRequest *)peek { - @synchronized (self) { - return [self peekAt:0]; - } -} - -- (BNCServerRequest *)peekAt:(NSUInteger)index { - @synchronized (self) { - if (index >= self.queue.count) { - [[BranchLogger shared] logError:@"Invalid queue operation: index out of bound!" error:nil]; - return nil; - } - BNCServerRequest *request = nil; - request = [self.queue objectAtIndex:index]; - return request; - } + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Enqueued request: %@. Current queue depth: %lu", request.requestUUID, (unsigned long)self.operationQueue.operationCount] error:nil]; } - (NSInteger)queueDepth { - @synchronized (self) { - return (NSInteger) self.queue.count; - } -} - -- (NSString *)description { - @synchronized(self) { - return [self.queue description]; - } + return (NSInteger) self.operationQueue.operationCount; } + - (void)clearQueue { - @synchronized (self) { - [self.queue removeAllObjects]; - } + [[BranchLogger shared] logDebug:@"Clearing all pending operations from the queue." error:nil]; + [self.operationQueue cancelAllOperations]; } +// These methods now need to iterate through the operations in the NSOperationQueue. - (BOOL)containsInstallOrOpen { - @synchronized (self) { - for (NSUInteger i = 0; i < self.queue.count; i++) { - BNCServerRequest *req = [self.queue objectAtIndex:i]; - // Install extends open, so only need to check open. - if ([req isKindOfClass:[BranchOpenRequest class]]) { + for (NSOperation *op in self.operationQueue.operations) { + if ([op isKindOfClass:[BNCServerRequestOperation class]]) { + BNCServerRequestOperation *requestOp = (BNCServerRequestOperation *)op; + if ([requestOp.request isKindOfClass:[BranchOpenRequest class]]) { return YES; } } - return NO; } + return NO; } - (BranchOpenRequest *)findExistingInstallOrOpen { - @synchronized (self) { - for (NSUInteger i = 0; i < self.queue.count; i++) { - BNCServerRequest *request = [self.queue objectAtIndex:i]; - - // Install subclasses open, so only need to check open - // Request should not be the one added from archived queue - if ([request isKindOfClass:[BranchOpenRequest class]] && !((BranchOpenRequest *)request).isFromArchivedQueue) { - return (BranchOpenRequest *)request; + for (NSOperation *op in self.operationQueue.operations) { + if ([op isKindOfClass:[BNCServerRequestOperation class]]) { + BNCServerRequestOperation *requestOp = (BNCServerRequestOperation *)op; + BNCServerRequest *request = requestOp.request; + if ([request isKindOfClass:[BranchOpenRequest class]]) { + BranchOpenRequest *openRequest = (BranchOpenRequest *)request; + return openRequest; } } - return nil; } + return nil; } +- (NSString *)description { + NSMutableArray *requestUUIDs = [NSMutableArray array]; + for (NSOperation *op in self.operationQueue.operations) { + if ([op isKindOfClass:[BNCServerRequestOperation class]]) { + if (!op.isFinished && !op.isCancelled) { + [requestUUIDs addObject:((BNCServerRequestOperation *)op).request.requestUUID]; + } else { + [requestUUIDs addObject:[NSString stringWithFormat:@"(Completed/Cancelled: %@)", ((BNCServerRequestOperation *)op).request.requestUUID]]; + } + } + } + return [NSString stringWithFormat:@" Operations (%ld): %@", self, (long)self.queueDepth, [requestUUIDs description]]; +} + (instancetype)getInstance { static BNCServerRequestQueue *sharedQueue = nil; diff --git a/Sources/BranchSDK/Branch.m b/Sources/BranchSDK/Branch.m index e0977f8df..32ba64178 100644 --- a/Sources/BranchSDK/Branch.m +++ b/Sources/BranchSDK/Branch.m @@ -45,7 +45,18 @@ #import "BNCServerAPI.h" #import "BranchPluginSupport.h" #import "BranchLogger.h" -#import "BranchConfigurationController.h" +#import "Private/BranchConfigurationController.h" + + + +// Swift integration - BranchSwiftSDK module is loaded dynamically at runtime +// No compile-time import needed to avoid circular dependencies in SPM +#if !SWIFT_PACKAGE +// Swift bridging header - auto-generated by Xcode when Swift files are present +#if __has_include("BranchSDK-Swift.h") +#import "BranchSDK-Swift.h" +#endif +#endif #if !TARGET_OS_TV #import "BNCUserAgentCollector.h" @@ -249,7 +260,7 @@ - (id)initWithInterface:(BNCServerInterface *)interface BranchJsonConfig *config = BranchJsonConfig.instance; self.deferInitForPluginRuntime = config.deferInitForPluginRuntime; [BranchConfigurationController sharedInstance].deferInitForPluginRuntime = self.deferInitForPluginRuntime; - + if (config.apiUrl) { [Branch setAPIUrl:config.apiUrl]; } @@ -276,6 +287,7 @@ - (id)initWithInterface:(BNCServerInterface *)interface } } + [self.requestQueue configureWithServerInterface:_serverInterface branchKey:key preferenceHelper:preferenceHelper]; return self; } @@ -1144,7 +1156,6 @@ - (void)sendServerRequest:(BNCServerRequest*)request { [self initSafetyCheck]; dispatch_async(self.isolationQueue, ^(){ [self.requestQueue enqueue:request]; - [self processNextQueueItem]; }); } @@ -1344,7 +1355,6 @@ - (void)getSpotlightUrlWithParams:(NSDictionary *)params callback:(callbackWithP dispatch_async(self.isolationQueue, ^(){ BranchSpotlightUrlRequest *req = [[BranchSpotlightUrlRequest alloc] initWithParams:params callback:callback]; [self.requestQueue enqueue:req]; - [self processNextQueueItem]; }); } @@ -1640,7 +1650,6 @@ - (void)generateShortUrl:(NSArray *)tags linkCache:self.linkCache callback:callback]; [self.requestQueue enqueue:req]; - [self processNextQueueItem]; }); } @@ -1869,14 +1878,6 @@ - (void)setNetworkCount:(NSInteger)networkCount { } } -- (void)insertRequestAtFront:(BNCServerRequest *)req { - if (self.networkCount == 0) { - [self.requestQueue insert:req at:0]; - } else { - [self.requestQueue insert:req at:1]; - } -} - static inline void BNCPerformBlockOnMainThreadSync(dispatch_block_t block) { if (block) { if ([NSThread isMainThread]) { @@ -1896,7 +1897,7 @@ - (void) processRequest:(BNCServerRequest*)req error:(NSError*)error { // If the request was successful, or was a bad user request, continue processing. - if (!error || + /* if (!error || error.code == BNCTrackingDisabledError || error.code == BNCBadRequestError || error.code == BNCDuplicateResourceError) { @@ -1950,7 +1951,7 @@ - (void) processRequest:(BNCServerRequest*)req } }); } - } + }*/ } - (BOOL)isReplayableRequest:(BNCServerRequest *)request { @@ -1974,6 +1975,7 @@ - (BOOL)isReplayableRequest:(BNCServerRequest *)request { } - (void)processNextQueueItem { + /* dispatch_semaphore_wait(self.processing_sema, DISPATCH_TIME_FOREVER); [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Processing next queue item. Network Count: %ld. Queue depth: %ld", (long)self.networkCount, (long)self.requestQueue.queueDepth] error:nil]; @@ -2021,7 +2023,7 @@ - (void)processNextQueueItem { } else { dispatch_semaphore_signal(self.processing_sema); - } + }*/ } - (void)clearNetworkQueue { @@ -2160,7 +2162,7 @@ - (void)initializeSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSS req.callback = initSessionCallback; req.urlString = urlString; - [self.requestQueue insert:req at:0]; + [self.requestQueue enqueue:req withPriority:NSOperationQueuePriorityHigh]; NSString *message = [NSString stringWithFormat:@"Request %@ callback %@ link %@", req, req.callback, req.urlString]; [[BranchLogger shared] logDebug:message error:nil]; @@ -2174,7 +2176,7 @@ - (void)initializeSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSS req.urlString = urlString; // put it behind the one that's already on queue - [self.requestQueue insert:req at:1]; + [self.requestQueue enqueue:req withPriority:NSOperationQueuePriorityHigh]; [[BranchLogger shared] logDebug:@"Link resolution request" error:nil]; NSString *message = [NSString stringWithFormat:@"Request %@ callback %@ link %@", req, req.callback, req.urlString]; @@ -2184,8 +2186,6 @@ - (void)initializeSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSS self.initializationStatus = BNCInitStatusInitializing; [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"initializationStatus %ld", self.initializationStatus] error:nil]; - - [self processNextQueueItem]; }); } } diff --git a/Sources/BranchSDK/BranchConfigurationController.m b/Sources/BranchSDK/BranchConfigurationController.m index e9a0f35dc..e2d16c29c 100644 --- a/Sources/BranchSDK/BranchConfigurationController.m +++ b/Sources/BranchSDK/BranchConfigurationController.m @@ -5,7 +5,7 @@ // Created by Nidhi Dixit on 6/2/25. // -#import "BranchConfigurationController.h" +#import "Private/BranchConfigurationController.h" #import "BNCPreferenceHelper.h" #import "BranchLogger.h" #import "BranchConstants.h" diff --git a/Sources/BranchSDK/BranchQRCode.m b/Sources/BranchSDK/BranchQRCode.m index 32182dec4..68d1c6096 100644 --- a/Sources/BranchSDK/BranchQRCode.m +++ b/Sources/BranchSDK/BranchQRCode.m @@ -8,6 +8,7 @@ #import #import "BranchQRCode.h" #import "Branch.h" +#import "BNCServerRequest.h" #import "BNCQRCodeCache.h" #import "BNCConfig.h" #import "BranchConstants.h" diff --git a/Sources/BranchSDK/Private/BNCServerRequestOperation.h b/Sources/BranchSDK/Private/BNCServerRequestOperation.h new file mode 100644 index 000000000..41bc1e074 --- /dev/null +++ b/Sources/BranchSDK/Private/BNCServerRequestOperation.h @@ -0,0 +1,25 @@ +// +// BNCServerRequestOperation.h +// BranchSDK +// +// Created by Nidhi Dixit on 7/22/25. +// + + +#import +#import "BNCServerRequest.h" +#import "BNCCallbacks.h" + + +@interface BNCServerRequestOperation : NSOperation + +@property (nonatomic, strong, readonly) BNCServerRequest *request; + +@property (nonatomic, strong) BNCServerInterface *serverInterface; +@property (nonatomic, copy) NSString *branchKey; +@property (nonatomic, strong) BNCPreferenceHelper *preferenceHelper; + +- (instancetype)initWithRequest:(BNCServerRequest *)request NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; // Prevent calling default init + +@end diff --git a/Sources/BranchSDK/Public/BNCServerRequestQueue.h b/Sources/BranchSDK/Public/BNCServerRequestQueue.h old mode 100755 new mode 100644 index 1c9d9391f..81d85192d --- a/Sources/BranchSDK/Public/BNCServerRequestQueue.h +++ b/Sources/BranchSDK/Public/BNCServerRequestQueue.h @@ -7,23 +7,22 @@ // #import "BNCServerRequest.h" +#import +#import "BNCServerRequest.h" + @class BranchOpenRequest; @interface BNCServerRequestQueue : NSObject ++ (instancetype)getInstance; + +- (void)configureWithServerInterface:(BNCServerInterface *)serverInterface + branchKey:(NSString *)branchKey + preferenceHelper:(BNCPreferenceHelper *)preferenceHelper; - (void)enqueue:(BNCServerRequest *)request; -- (BNCServerRequest *)dequeue; -- (BNCServerRequest *)peek; -- (BNCServerRequest *)peekAt:(NSUInteger)index; -- (void)insert:(BNCServerRequest *)request at:(NSUInteger)index; -- (BNCServerRequest *)removeAt:(NSUInteger)index; -- (void)remove:(BNCServerRequest *)request; +- (void)enqueue:(BNCServerRequest *)request withPriority:(NSOperationQueuePriority)priority; - (void)clearQueue; -- (NSInteger)queueDepth; - - (BOOL)containsInstallOrOpen; - - (BranchOpenRequest *)findExistingInstallOrOpen; -+ (id)getInstance; @end diff --git a/Sources/BranchSDK/Public/Branch.h b/Sources/BranchSDK/Public/Branch.h index 75ca5f03a..27f1c1c42 100644 --- a/Sources/BranchSDK/Public/Branch.h +++ b/Sources/BranchSDK/Public/Branch.h @@ -37,7 +37,6 @@ #import "BNCLinkCache.h" #import "BNCPreferenceHelper.h" #import "BNCServerInterface.h" -#import "BNCServerRequestQueue.h" #import "BranchLogger.h" // Not used by Branch singleton public API @@ -151,6 +150,10 @@ extern NSString * __nonnull const BNCShareCompletedEvent; // Spotlight Constant extern NSString * __nonnull const BNCSpotlightFeature; +// Forward declarations for types used in testing APIs +@class BNCServerRequestQueue; +@class BNCServerRequest; + #pragma mark - BranchLink @interface BranchLink : NSObject diff --git a/Sources/BranchSDK/Public/BranchEvent.h b/Sources/BranchSDK/Public/BranchEvent.h index 484fb529e..b51ef22d4 100644 --- a/Sources/BranchSDK/Public/BranchEvent.h +++ b/Sources/BranchSDK/Public/BranchEvent.h @@ -7,6 +7,7 @@ // #import "Branch.h" +#import "BNCServerRequest.h" #import "BranchUniversalObject.h" #import diff --git a/Sources/BranchSDK/Public/BranchSDK.h b/Sources/BranchSDK/Public/BranchSDK.h index e4914c80a..4a54d863b 100644 --- a/Sources/BranchSDK/Public/BranchSDK.h +++ b/Sources/BranchSDK/Public/BranchSDK.h @@ -19,6 +19,7 @@ FOUNDATION_EXPORT const unsigned char BranchSDKVersionString[]; #import "BranchScene.h" #import "BranchDelegate.h" +#import "BNCConfig.h" #import "BranchEvent.h" #import "BranchLinkProperties.h" @@ -58,3 +59,5 @@ FOUNDATION_EXPORT const unsigned char BranchSDKVersionString[]; // BNCLinkCache.h uses BNCLinkData.h #import "BNCLinkData.h" + +#import "BranchConstants.h"