diff --git a/.github/workflows/automation-trigger-test.yml b/.github/workflows/automation-trigger-test.yml index eb77a8987..ef927ea21 100644 --- a/.github/workflows/automation-trigger-test.yml +++ b/.github/workflows/automation-trigger-test.yml @@ -78,7 +78,7 @@ jobs: distribution: 'temurin' cache: maven - name: Setup Node.js environment - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.3 - name: Build with Maven id: build_maven run: | diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCRequestFactoryTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCRequestFactoryTests.m index c18505774..c28d3b6e0 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCRequestFactoryTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCRequestFactoryTests.m @@ -8,15 +8,19 @@ #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 { @@ -24,32 +28,40 @@ - (void)tearDown { } - (void)testInitWithBranchKeyNil { - BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:nil]; + 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:@""]; + 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"]; + 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"]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; NSDictionary *json = [factory dataForInstallWithURLString:@"https://branch.io"]; XCTAssertNotNil(json); @@ -60,10 +72,13 @@ - (void)testDataForInstall { // 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"]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; NSDictionary *json = [factory dataForOpenWithURLString:@"https://branch.io"]; XCTAssertNotNil(json); @@ -75,12 +90,15 @@ - (void)testDataForOpen { // 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"]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; XCTAssertNotNil(json); @@ -89,6 +107,9 @@ - (void)testDataForEvent { 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 { @@ -104,7 +125,7 @@ - (void)testDataForEventWithContentItem { ] }; - BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd"]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; XCTAssertNotNil(json); @@ -117,6 +138,9 @@ - (void)testDataForEventWithContentItem { 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 { @@ -138,7 +162,7 @@ - (void)testDataForEventWithTwoContentItem { ] }; - BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd"]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; XCTAssertNotNil(json); @@ -151,12 +175,15 @@ - (void)testDataForEventWithTwoContentItem { 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"]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; NSDictionary *json = [factory dataForEventWithEventDictionary:[event mutableCopy]]; XCTAssertNotNil(json); @@ -165,10 +192,13 @@ - (void)testDataForEventEmpty { 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"]; + BNCRequestFactory *factory = [[BNCRequestFactory alloc] initWithBranchKey:@"key_abcd" UUID:self.requestUUID TimeStamp:self.requestCreationTimeStamp]; NSDictionary *json = [factory dataForEventWithEventDictionary:nil]; XCTAssertNotNil(json); @@ -177,19 +207,28 @@ - (void)testDataForEventNil { 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"]; + 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"]; + 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/Branch-TestBed/Branch-SDK-Tests/BNCServerRequestQueueTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCServerRequestQueueTests.m deleted file mode 100644 index 4d8664b82..000000000 --- a/Branch-TestBed/Branch-SDK-Tests/BNCServerRequestQueueTests.m +++ /dev/null @@ -1,160 +0,0 @@ -// -// BNCServerRequestQueueTests.m -// Branch-SDK-Tests -// -// Created by Ernest Cho on 5/14/21. -// Copyright © 2021 Branch, Inc. All rights reserved. -// - -#import -#import "BNCServerRequestQueue.h" -#import "BNCServerRequest.h" - -// Analytics requests -#import "BranchInstallRequest.h" -#import "BranchOpenRequest.h" -#import "BranchEvent.h" - -@interface BNCServerRequestQueue () -- (NSData *)archiveQueue:(NSArray *)queue; -- (NSMutableArray *)unarchiveQueueFromData:(NSData *)data; - -- (NSData *)archiveObject:(NSObject *)object; -- (id)unarchiveObjectFromData:(NSData *)data; - -// returns data in the legacy format -- (NSData *)oldArchiveQueue:(NSArray *)queue; - -@end - -@interface BNCServerRequestQueueTests : XCTestCase -@property (nonatomic, strong, readwrite) BNCServerRequestQueue *queue; -@end - -@implementation BNCServerRequestQueueTests - -- (void)setUp { - self.queue = [BNCServerRequestQueue new]; -} - -- (void)tearDown { - self.queue = nil; -} - -- (void)testArchiveNil { - NSString *object = nil; - - NSData *archived = [self.queue archiveObject:object]; - XCTAssertNotNil(archived); - - NSString *unarchived = [self.queue unarchiveObjectFromData:archived]; - XCTAssertNil(unarchived); -} - -- (void)testArchiveString { - NSString *object = @"Hello World"; - - NSData *archived = [self.queue archiveObject:object]; - XCTAssertNotNil(archived); - - NSString *unarchived = [self.queue unarchiveObjectFromData:archived]; - XCTAssertNotNil(unarchived); - XCTAssert([object isEqual:unarchived]); -} - -- (void)testArchiveInstallRequest { - BranchInstallRequest *object = [BranchInstallRequest new]; - - NSData *archived = [self.queue archiveObject:object]; - XCTAssertNotNil(archived); - - BranchInstallRequest *unarchived = [self.queue unarchiveObjectFromData:archived]; - XCTAssertNotNil(unarchived); - XCTAssert([unarchived isKindOfClass:[BranchInstallRequest class]]); - - // The request object is not very test friendly, so comparing the two is not helpful at the moment -} - -- (void)testArchiveOpenRequest { - BranchOpenRequest *object = [BranchOpenRequest new]; - - NSData *archived = [self.queue archiveObject:object]; - XCTAssertNotNil(archived); - - BranchOpenRequest *unarchived = [self.queue unarchiveObjectFromData:archived]; - XCTAssertNotNil(unarchived); - XCTAssert([unarchived isKindOfClass:[BranchOpenRequest class]]); - - // The request object is not very test friendly, so comparing the two is not helpful at the moment -} - -- (void)testArchiveEventRequest { - BranchEventRequest *object = [BranchEventRequest new]; - - NSData *archived = [self.queue archiveObject:object]; - XCTAssertNotNil(archived); - - BranchEventRequest *unarchived = [self.queue unarchiveObjectFromData:archived]; - XCTAssertNotNil(unarchived); - XCTAssert([unarchived isKindOfClass:[BranchEventRequest class]]); - - // The request object is not very test friendly, so comparing the two is not helpful at the moment -} - -- (void)testArchiveArrayOfRequests { - NSMutableArray *tmp = [NSMutableArray new]; - [tmp addObject:[BranchOpenRequest new]]; - [tmp addObject:[BranchEventRequest new]]; - - NSData *data = [self.queue archiveQueue:tmp]; - XCTAssertNotNil(data); - - NSMutableArray *unarchived = [self.queue unarchiveQueueFromData:data]; - XCTAssertNotNil(unarchived); - XCTAssert(unarchived.count == 2); -} - -- (void)testOldArchiveArrayOfRequests { - NSMutableArray *tmp = [NSMutableArray new]; - [tmp addObject:[BranchOpenRequest new]]; - [tmp addObject:[BranchEventRequest new]]; - - NSData *data = [self.queue oldArchiveQueue:tmp]; - XCTAssertNotNil(data); - - NSMutableArray *unarchived = [self.queue unarchiveQueueFromData:data]; - XCTAssertNotNil(unarchived); - XCTAssert(unarchived.count == 2); -} - -- (void)testArchiveArrayOfInvalidObjects { - NSMutableArray *tmp = [NSMutableArray new]; - [tmp addObject:[BranchOpenRequest new]]; - [tmp addObject:@"Hello World"]; - [tmp addObject:[BranchEventRequest new]]; - - NSData *data = [self.queue archiveQueue:tmp]; - XCTAssertNotNil(data); - - NSMutableArray *unarchived = [self.queue unarchiveQueueFromData:data]; - - XCTAssertNotNil(unarchived); - XCTAssert(unarchived.count == 2); -} - -- (void)testOldArchiveArrayOfInvalidObjects { - NSMutableArray *tmp = [NSMutableArray new]; - [tmp addObject:[BranchOpenRequest new]]; - [tmp addObject:@"Hello World"]; - [tmp addObject:[BranchEventRequest new]]; - - NSData *data = [self.queue oldArchiveQueue:tmp]; - XCTAssertNotNil(data); - - NSMutableArray *unarchived = [self.queue unarchiveQueueFromData:data]; - - XCTAssertNotNil(unarchived); - XCTAssert(unarchived.count == 2); -} - -@end diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj index 23e32a6c7..ef254bdf8 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj @@ -188,7 +188,6 @@ 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 */; }; - 5F83B9ED2433BAAA0054A022 /* BNCServerInterface.Test.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D16837E2098C901008819E3 /* BNCServerInterface.Test.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 */; }; @@ -200,7 +199,6 @@ 5F92B2362383644C00CA909B /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F92B2352383644C00CA909B /* SystemConfiguration.framework */; }; 5F92B242238752A500CA909B /* BNCDeviceInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F92B241238752A500CA909B /* BNCDeviceInfoTests.m */; }; 5FA9112F29BC662000F3D35C /* BNCNetworkInterfaceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FA9112E29BC662000F3D35C /* BNCNetworkInterfaceTests.m */; }; - 5FB6CC13264F0C7C0020E478 /* BNCServerRequestQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FB6CC12264F0C7C0020E478 /* BNCServerRequestQueueTests.m */; }; 5FC20E732A93D85F00D9E1C8 /* BNCRequestFactoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FC20E722A93D85F00D9E1C8 /* BNCRequestFactoryTests.m */; }; 5FC4CF8C24860C440001E701 /* latd.json in Resources */ = {isa = PBXBuildFile; fileRef = 5FC4CF7E24860C320001E701 /* latd.json */; }; 5FC4CF8D24860C440001E701 /* latd_missing_window.json in Resources */ = {isa = PBXBuildFile; fileRef = 5FC4CF7F24860C320001E701 /* latd_missing_window.json */; }; @@ -490,7 +488,6 @@ 5F92B2352383644C00CA909B /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; 5F92B241238752A500CA909B /* BNCDeviceInfoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCDeviceInfoTests.m; sourceTree = ""; }; 5FA9112E29BC662000F3D35C /* BNCNetworkInterfaceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCNetworkInterfaceTests.m; sourceTree = ""; }; - 5FB6CC12264F0C7C0020E478 /* BNCServerRequestQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCServerRequestQueueTests.m; sourceTree = ""; }; 5FC20E722A93D85F00D9E1C8 /* BNCRequestFactoryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCRequestFactoryTests.m; sourceTree = ""; }; 5FC4CF7E24860C320001E701 /* latd.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = latd.json; sourceTree = ""; }; 5FC4CF7F24860C320001E701 /* latd_missing_window.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = latd_missing_window.json; sourceTree = ""; }; @@ -629,7 +626,6 @@ 5F92B23323835FEB00CA909B /* BNCReachabilityTests.m */, C1CC888129BAAFC000BDD2B5 /* BNCReferringURLUtilityTests.m */, 5FC20E722A93D85F00D9E1C8 /* BNCRequestFactoryTests.m */, - 5FB6CC12264F0C7C0020E478 /* BNCServerRequestQueueTests.m */, 5FDB04F324E6156800F2F267 /* BNCSKAdNetworkTests.m */, 4D16838F2098C901008819E3 /* BNCSystemObserverTests.m */, 5FCF7EAC29DC96A7008D629E /* BNCURLFilterSkiplistUpgradeTests.m */, @@ -1386,7 +1382,6 @@ C1CC888229BAAFC000BDD2B5 /* BNCReferringURLUtilityTests.m in Sources */, 5FE694382405FA2700E3AEE2 /* BNCCallbackMapTests.m in Sources */, 5FDB04F424E6156800F2F267 /* BNCSKAdNetworkTests.m in Sources */, - 5FB6CC13264F0C7C0020E478 /* BNCServerRequestQueueTests.m in Sources */, 4D1683AE2098C902008819E3 /* BNCLinkDataTests.m in Sources */, C15CC9E02ABCF8C8003CC339 /* BranchActivityItemTests.m in Sources */, 5F92B23423835FEB00CA909B /* BNCReachabilityTests.m in Sources */, diff --git a/BranchSDK.podspec b/BranchSDK.podspec index 9802021b9..e93954983 100644 --- a/BranchSDK.podspec +++ b/BranchSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "BranchSDK" - s.version = "3.4.4" + s.version = "3.6.5" s.summary = "Create an HTTP URL for any piece of content in your app" s.description = <<-DESC - Want the highest possible conversions on your sharing feature? diff --git a/BranchSDK.xcodeproj/project.pbxproj b/BranchSDK.xcodeproj/project.pbxproj index 2bcc3a773..4472d5212 100644 --- a/BranchSDK.xcodeproj/project.pbxproj +++ b/BranchSDK.xcodeproj/project.pbxproj @@ -1974,7 +1974,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.6.5; OTHER_LDFLAGS = ( "-weak_framework", LinkPresentation, @@ -2009,7 +2009,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.6.5; OTHER_LDFLAGS = ( "-weak_framework", LinkPresentation, @@ -2215,7 +2215,7 @@ "@loader_path/Frameworks", ); MACH_O_TYPE = staticlib; - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.6.5; OTHER_LDFLAGS = ( "-weak_framework", LinkPresentation, @@ -2254,7 +2254,7 @@ "@loader_path/Frameworks", ); MACH_O_TYPE = staticlib; - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.6.5; OTHER_LDFLAGS = ( "-weak_framework", LinkPresentation, @@ -2291,7 +2291,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.6.5; OTHER_LDFLAGS = ( "-weak_framework", LinkPresentation, @@ -2326,7 +2326,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 3.4.4; + MARKETING_VERSION = 3.6.5; OTHER_LDFLAGS = ( "-weak_framework", LinkPresentation, diff --git a/ChangeLog.md b/ChangeLog.md index bf6ae7142..8996557c5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,31 @@ Branch iOS SDK Change Log +v.3.6.5 +- Removed on-disk caching and replay of server request objects. + +v.3.6.4 +- Added 'branch_sdk_request_unique_id' and 'branch_sdk_request_timestamp' to post params of all the SDK requests. + +v.3.6.3 +- Fix for duplicate events created from archived request queue on disk. + +v.3.6.2 +- Fix for issue which was sending an extra open request on cold app launch. +- Updated fix for cold link launch when using deferred initialization and an AppDelegate only app. + +v.3.6.1 +- Fixed issues where external_intent_uri was incorrectly set in certain cases + +v.3.6.0 +- Fixes intermittent issue with universal links on cold opens + +v.3.5.1 +- Minor logging improvement + +v.3.5.0 +- Add additional logging +- Update setRequestMetadata signature to use NSString + v.3.4.4 - Fix for rare case where Branch events were duplicated - Fixed script in "Run Script" phase of static framework targets diff --git a/Sources/BranchSDK/BNCConfig.m b/Sources/BranchSDK/BNCConfig.m index 385949ad5..47cd61fdd 100644 --- a/Sources/BranchSDK/BNCConfig.m +++ b/Sources/BranchSDK/BNCConfig.m @@ -8,7 +8,7 @@ #include "BNCConfig.h" -NSString * const BNC_SDK_VERSION = @"3.4.4"; +NSString * const BNC_SDK_VERSION = @"3.6.5"; NSString * const BNC_LINK_URL = @"https://bnc.lt"; NSString * const BNC_CDN_URL = @"https://cdn.branch.io"; diff --git a/Sources/BranchSDK/BNCPreferenceHelper.m b/Sources/BranchSDK/BNCPreferenceHelper.m index 8f8b91496..1acf209a0 100644 --- a/Sources/BranchSDK/BNCPreferenceHelper.m +++ b/Sources/BranchSDK/BNCPreferenceHelper.m @@ -391,9 +391,11 @@ - (NSString *)sessionParams { - (void)setSessionParams:(NSString *)sessionParams { @synchronized (self) { + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Setting session params %@", sessionParams] error:nil]; if (sessionParams == nil || ![_sessionParams isEqualToString:sessionParams]) { _sessionParams = sessionParams; [self writeObjectToDefaults:BRANCH_PREFS_KEY_SESSION_PARAMS value:sessionParams]; + [[BranchLogger shared] logVerbose:@"Params set" error:nil]; } } } @@ -862,6 +864,7 @@ - (void) clearTrackingInformation { self.userIdentity = nil; self.referringURLQueryParameters = nil; self.anonID = nil; + [[BranchLogger shared] logVerbose:@"Tracking information cleared" error:nil]; } } diff --git a/Sources/BranchSDK/BNCQRCodeCache.m b/Sources/BranchSDK/BNCQRCodeCache.m index b77723e64..eb23f2e84 100644 --- a/Sources/BranchSDK/BNCQRCodeCache.m +++ b/Sources/BranchSDK/BNCQRCodeCache.m @@ -7,6 +7,7 @@ // #import "BNCQRCodeCache.h" +#import "BranchConstants.h" @interface BNCQRCodeCache() @property (nonatomic, strong) NSMutableDictionary *cache; @@ -35,6 +36,8 @@ - (void)addQRCodeToCache:(NSData *)qrCodeData withParams:(NSMutableDictionary *) @synchronized (self) { [self.cache removeAllObjects]; NSMutableDictionary *tempParams = [parameters mutableCopy]; + [tempParams removeObjectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]; + [tempParams removeObjectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]; [tempParams[@"data"] removeObjectForKey:@"$creation_timestamp"]; self.cache[tempParams] = qrCodeData; } @@ -45,6 +48,8 @@ - (NSData *)checkQRCodeCache:(NSMutableDictionary *)parameters { @synchronized (self) { NSMutableDictionary *tempParams = [parameters mutableCopy]; [tempParams[@"data"] removeObjectForKey:@"$creation_timestamp"]; + [tempParams removeObjectForKey:BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP]; + [tempParams removeObjectForKey:BRANCH_REQUEST_KEY_REQUEST_UUID]; qrCode = self.cache[tempParams]; } return qrCode; diff --git a/Sources/BranchSDK/BNCReferringURLUtility.m b/Sources/BranchSDK/BNCReferringURLUtility.m index 3d7b63aac..79ce1cd1f 100644 --- a/Sources/BranchSDK/BNCReferringURLUtility.m +++ b/Sources/BranchSDK/BNCReferringURLUtility.m @@ -46,6 +46,8 @@ - (void)dealloc { - (void)parseReferringURL:(NSURL *)url { + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Parsing URL %@", url] error:nil]; + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; for (NSURLQueryItem *item in components.queryItems) { if ([self isSupportedQueryParameter:item.name]) { diff --git a/Sources/BranchSDK/BNCRequestFactory.m b/Sources/BranchSDK/BNCRequestFactory.m index dbaf88af7..f4e170c57 100644 --- a/Sources/BranchSDK/BNCRequestFactory.m +++ b/Sources/BranchSDK/BNCRequestFactory.m @@ -47,12 +47,14 @@ @interface BNCRequestFactory() @property (nonatomic, strong, readwrite) BNCSKAdNetwork *skAdNetwork; @property (nonatomic, strong, readwrite) BNCAppleReceipt *appleReceipt; @property (nonatomic, strong, readwrite) BNCPasteboard *pasteboard; +@property (nonatomic, strong, readwrite) NSNumber *requestCreationTimeStamp; +@property (nonatomic, strong, readwrite) NSString *requestUUID; @end @implementation BNCRequestFactory -- (instancetype)initWithBranchKey:(NSString *)key { +- (instancetype)initWithBranchKey:(NSString *)key UUID:(NSString *)requestUUID TimeStamp:(NSNumber *)requestTimeStamp { self = [super init]; if (self) { self.branchKey = key; @@ -65,6 +67,8 @@ - (instancetype)initWithBranchKey:(NSString *)key { self.skAdNetwork = [BNCSKAdNetwork sharedInstance]; self.appleReceipt = [BNCAppleReceipt sharedInstance]; self.pasteboard = [BNCPasteboard sharedInstance]; + self.requestUUID = requestUUID; + self.requestCreationTimeStamp = requestTimeStamp; } return self; } @@ -100,8 +104,14 @@ - (NSDictionary *)dataForInstallWithURLString:(NSString *)urlString { [self addAppleReceiptSourceToJSON:json]; [self addTimestampsToJSON:json]; + // Check if the urlString is a valid URL to ensure it's a universal link, not the external intent uri if (urlString) { - [self safeSetValue:urlString forKey:BRANCH_REQUEST_KEY_UNIVERSAL_LINK_URL onDict:json]; + NSURL *url = [NSURL URLWithString:urlString]; + if (url && ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"])) { + [self safeSetValue:urlString forKey:BRANCH_REQUEST_KEY_UNIVERSAL_LINK_URL onDict:json]; + } else { + [self safeSetValue:urlString forKey:BRANCH_REQUEST_KEY_EXTERNAL_INTENT_URI onDict:json]; + } } [self addAppleAttributionTokenToJSON:json]; @@ -149,8 +159,15 @@ - (NSDictionary *)dataForOpenWithURLString:(NSString *)urlString { [self addAppleReceiptSourceToJSON:json]; [self addTimestampsToJSON:json]; + + // Check if the urlString is a valid URL to ensure it's a universal link, not the external intent uri if (urlString) { - [self safeSetValue:urlString forKey:BRANCH_REQUEST_KEY_UNIVERSAL_LINK_URL onDict:json]; + NSURL *url = [NSURL URLWithString:urlString]; + if (url && ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"])) { + [self safeSetValue:urlString forKey:BRANCH_REQUEST_KEY_UNIVERSAL_LINK_URL onDict:json]; + } else { + [self safeSetValue:urlString forKey:BRANCH_REQUEST_KEY_EXTERNAL_INTENT_URI onDict:json]; + } } // Usually sent with install, but retry on open if it didn't get sent @@ -416,6 +433,8 @@ - (void)addAppClipDataToJSON:(NSMutableDictionary *)json { - (void)addDefaultRequestDataToJSON:(NSMutableDictionary *)json { json[@"branch_key"] = self.branchKey; + json[BRANCH_REQUEST_KEY_REQUEST_UUID] = self.requestUUID; + json[BRANCH_REQUEST_KEY_REQUEST_CREATION_TIME_STAMP] = self.requestCreationTimeStamp; // omit field if value is NO if ([self isTrackingDisabled]) { diff --git a/Sources/BranchSDK/BNCServerInterface.m b/Sources/BranchSDK/BNCServerInterface.m index 63559c3a8..2fc2e29f8 100644 --- a/Sources/BranchSDK/BNCServerInterface.m +++ b/Sources/BranchSDK/BNCServerInterface.m @@ -61,8 +61,27 @@ - (void)postRequest:(NSDictionary *)post url:(NSString *)url key:(NSString *)key - (void)postRequest:(NSDictionary *)post url:(NSString *)url retryNumber:(NSInteger)retryNumber key:(NSString *)key callback:(BNCServerCallback)callback { + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"retryNumber %ld", retryNumber] error:nil]; + // TODO: confirm it's ok to send full URL instead of with the domain trimmed off self.requestEndpoint = url; + + // Drops non-linking requests when tracking is disabled + if (Branch.trackingDisabled) { + + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Tracking is disabled, checking if %@ is linking request.", url] error:nil]; + + if (![self isLinkingRelatedRequest:url postParams:post]) { + [[BNCPreferenceHelper sharedInstance] clearTrackingInformation]; + NSError *error = [NSError branchErrorWithCode:BNCTrackingDisabledError]; + [[BranchLogger shared] logWarning:[NSString stringWithFormat:@"Dropping non-linking request"] error:error]; + if (callback) { + callback(nil, error); + } + return; + } + } + NSURLRequest *request = [self preparePostRequest:post url:url key:key retryNumber:retryNumber]; [self genericHTTPRequest:request @@ -123,7 +142,11 @@ - (void)genericHTTPRequest:(NSURLRequest *)request retryNumber:(NSInteger)retryN } else { if (status != 200) { if ([NSError branchDNSBlockingError:underlyingError]) { - [[BranchLogger shared] logWarning:[NSString stringWithFormat:@"Possible DNS Ad Blocker. Giving up on request with HTTP status code %ld", (long)status] error:underlyingError]; + NSError *error = [NSError branchErrorWithCode:BNCDNSAdBlockerError]; + [[BranchLogger shared] logError:[NSString stringWithFormat:@"Possible DNS Ad Blocker. Giving up on request with HTTP status code %ld. Underlying error: %@", (long)status, underlyingError] error:error]; + } else if ([NSError branchVPNBlockingError:underlyingError]) { + NSError *error = [NSError branchErrorWithCode:BNCVPNAdBlockerError]; + [[BranchLogger shared] logError:[NSString stringWithFormat:@"Possible VPN Ad Blocker. Giving up on request with HTTP status code %ld. Underlying error: %@", (long)status, underlyingError] error:error]; } else { [[BranchLogger shared] logWarning: [NSString stringWithFormat:@"Giving up on request with HTTP status code %ld", (long)status] error:underlyingError]; } @@ -136,20 +159,7 @@ - (void)genericHTTPRequest:(NSURLRequest *)request retryNumber:(NSInteger)retryN } }; - // Drops non-linking requests when tracking is disabled - if (Branch.trackingDisabled) { - NSString *endpoint = request.URL.absoluteString; - - if (![self isLinkingRelatedRequest:endpoint]) { - [[BNCPreferenceHelper sharedInstance] clearTrackingInformation]; - NSError *error = [NSError branchErrorWithCode:BNCTrackingDisabledError]; - [[BranchLogger shared] logWarning:[NSString stringWithFormat:@"Dropping non-linking request"] error:error]; - if (callback) { - callback(nil, error); - } - return; - } - } + id operation = [self.networkService networkOperationWithURLRequest:request.copy completion:completionHandler]; [operation start]; @@ -165,9 +175,9 @@ - (void)genericHTTPRequest:(NSURLRequest *)request retryNumber:(NSInteger)retryN } } -- (BOOL)isLinkingRelatedRequest:(NSString *)endpoint { - BNCPreferenceHelper *prefs = [BNCPreferenceHelper sharedInstance]; - BOOL hasIdentifier = (prefs.linkClickIdentifier.length > 0 ) || (prefs.spotlightIdentifier.length > 0 ) || (prefs.universalLinkUrl.length > 0); +- (BOOL)isLinkingRelatedRequest:(NSString *)endpoint postParams:(NSDictionary *)post { + + BOOL hasIdentifier = (post[BRANCH_REQUEST_KEY_LINK_IDENTIFIER] != nil ) || (post[BRANCH_REQUEST_KEY_LINK_IDENTIFIER] != nil) || (post[BRANCH_REQUEST_KEY_UNIVERSAL_LINK_URL] != nil); // Allow install to resolve a link. if ([endpoint containsString:@"/v1/install"]) { @@ -290,6 +300,10 @@ - (BNCServerResponse *)processServerResponse:(NSURLResponse *)response data:(NSD NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSString *requestId = httpResponse.allHeaderFields[@"X-Branch-Request-Id"]; + if ([[BranchLogger shared] shouldLog:BranchLogLevelVerbose]) { + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Processing response %@", requestId] error:nil]; + } + if (!error) { serverResponse.statusCode = @([httpResponse statusCode]); serverResponse.data = [BNCEncodingUtils decodeJsonDataToDictionary:data]; diff --git a/Sources/BranchSDK/BNCServerRequest.m b/Sources/BranchSDK/BNCServerRequest.m index decdf92db..d17675581 100644 --- a/Sources/BranchSDK/BNCServerRequest.m +++ b/Sources/BranchSDK/BNCServerRequest.m @@ -8,9 +8,19 @@ #import "BNCServerRequest.h" #import "BranchLogger.h" +#import "BNCEncodingUtils.h" @implementation BNCServerRequest +- (id) init { + if ((self = [super init])) { + NSDate *timeStamp = [NSDate date]; + _requestUUID = [BNCServerRequest generateRequestUUIDFromDate:timeStamp]; + _requestCreationTimeStamp = BNCWireFormatFromDate(timeStamp); + } + return self; +} + - (void)makeRequest:(BNCServerInterface *)serverInterface key:(NSString *)key callback:(BNCServerCallback)callback { [[BranchLogger shared] logError:@"BNCServerRequest subclasses must implement makeRequest:key:callback:." error:nil]; } @@ -20,11 +30,17 @@ - (void)processResponse:(BNCServerResponse *)response error:(NSError *)error { } - (id)initWithCoder:(NSCoder *)aDecoder { - return self = [super init]; + self = [super init]; + if (self) { + self.requestUUID = [aDecoder decodeObjectOfClass:NSString.class forKey:@"requestUUID"]; + self.requestCreationTimeStamp = [aDecoder decodeObjectOfClass:NSNumber.class forKey:@"requestCreationTimeStamp"]; + } + return self; } - (void)encodeWithCoder:(NSCoder *)coder { - // Nothing going on here + [coder encodeObject:self.requestUUID forKey:@"requestUUID"]; + [coder encodeObject:self.requestCreationTimeStamp forKey:@"requestCreationTimeStamp"]; } - (void)safeSetValue:(NSObject *)value forKey:(NSString *)key onDict:(NSMutableDictionary *)dict { @@ -37,4 +53,15 @@ + (BOOL)supportsSecureCoding { return YES; } ++ (NSString *) generateRequestUUIDFromDate:(NSDate *) localDate { + NSString *uuid = [[NSUUID UUID ] UUIDString]; + + NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"'-'yyyyMMddHH"; + [dateFormatter setTimeZone:gmt]; + + return [uuid stringByAppendingString:[dateFormatter stringFromDate:localDate]]; +} + @end diff --git a/Sources/BranchSDK/BNCServerRequestQueue.m b/Sources/BranchSDK/BNCServerRequestQueue.m index 28bdd988a..c518fccce 100755 --- a/Sources/BranchSDK/BNCServerRequestQueue.m +++ b/Sources/BranchSDK/BNCServerRequestQueue.m @@ -17,20 +17,8 @@ #import "BranchLogger.h" - -static NSString * const BRANCH_QUEUE_FILE = @"BNCServerRequestQueue"; -static NSTimeInterval const BATCH_WRITE_TIMEOUT = 3.0; - - -static inline uint64_t BNCNanoSecondsFromTimeInterval(NSTimeInterval interval) { - return interval * ((NSTimeInterval) NSEC_PER_SEC); -} - - @interface BNCServerRequestQueue() @property (strong, nonatomic) NSMutableArray *queue; -@property (strong, nonatomic) dispatch_queue_t asyncQueue; -@property (strong, nonatomic) dispatch_source_t persistTimer; @end @@ -41,26 +29,13 @@ - (instancetype)init { if (!self) return self; self.queue = [NSMutableArray new]; - self.asyncQueue = dispatch_queue_create("io.branch.persist_queue", DISPATCH_QUEUE_SERIAL); return self; } -- (void)dealloc { - @synchronized (self) { - if (self.persistTimer) { - dispatch_source_cancel(self.persistTimer); - self.persistTimer = nil; - } - [self persistImmediately]; - self.queue = nil; - } -} - - (void)enqueue:(BNCServerRequest *)request { @synchronized (self) { if (request) { [self.queue addObject:request]; - [self persistEventually]; } } } @@ -73,7 +48,6 @@ - (void)insert:(BNCServerRequest *)request at:(NSUInteger)index { } if (request) { [self.queue insertObject:request atIndex:index]; - [self persistEventually]; } } } @@ -84,7 +58,6 @@ - (BNCServerRequest *)dequeue { if (self.queue.count > 0) { request = [self.queue objectAtIndex:0]; [self.queue removeObjectAtIndex:0]; - [self persistEventually]; } return request; } @@ -99,7 +72,6 @@ - (BNCServerRequest *)removeAt:(NSUInteger)index { } request = [self.queue objectAtIndex:index]; [self.queue removeObjectAtIndex:index]; - [self persistEventually]; return request; } } @@ -107,7 +79,6 @@ - (BNCServerRequest *)removeAt:(NSUInteger)index { - (void)remove:(BNCServerRequest *)request { @synchronized (self) { [self.queue removeObject:request]; - [self persistEventually]; } } @@ -144,7 +115,6 @@ - (NSString *)description { - (void)clearQueue { @synchronized (self) { [self.queue removeAllObjects]; - [self persistImmediately]; } } @@ -167,7 +137,8 @@ - (BranchOpenRequest *)findExistingInstallOrOpen { BNCServerRequest *request = [self.queue objectAtIndex:i]; // Install subclasses open, so only need to check open - if ([request isKindOfClass:[BranchOpenRequest class]]) { + // Request should not be the one added from archived queue + if ([request isKindOfClass:[BranchOpenRequest class]] && !((BranchOpenRequest *)request).isFromArchivedQueue) { return (BranchOpenRequest *)request; } } @@ -175,219 +146,12 @@ - (BranchOpenRequest *)findExistingInstallOrOpen { } } -#pragma mark - Private Methods - -- (void)persistEventually { - @synchronized (self) { - if (self.persistTimer) return; - - self.persistTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.asyncQueue); - if (!self.persistTimer) return; - - dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, BNCNanoSecondsFromTimeInterval(BATCH_WRITE_TIMEOUT)); - dispatch_source_set_timer( - self.persistTimer, - startTime, - BNCNanoSecondsFromTimeInterval(BATCH_WRITE_TIMEOUT), - BNCNanoSecondsFromTimeInterval(BATCH_WRITE_TIMEOUT / 10.0) - ); - __weak __typeof(self) weakSelf = self; - dispatch_source_set_event_handler(self.persistTimer, ^ { - __strong __typeof(weakSelf) strongSelf = weakSelf; - if (strongSelf) { - [strongSelf cancelTimer]; - [strongSelf persistImmediately]; - } - }); - dispatch_resume(self.persistTimer); - } -} - -- (void)cancelTimer { - @synchronized (self) { - if (self.persistTimer) { - dispatch_source_cancel(self.persistTimer); - self.persistTimer = nil; - } - } -} - -- (void)persistImmediately { - @synchronized (self) { - if (!self.queue || self.queue.count == 0) { - return; - } - NSArray *queueCopy = [self.queue copy]; - - // encode the list of encoded request objects - NSData *data = [self archiveQueue:queueCopy]; - if (data) { - NSError *error = nil; - [data writeToURL:self.class.URLForQueueFile options:NSDataWritingAtomic error:&error]; - - if (error) { - [[BranchLogger shared] logError:[NSString stringWithFormat:@"Failed to persist queue to disk: %@.", error] error:error]; - } - } else { - [[BranchLogger shared] logError:[NSString stringWithFormat:@"Failed to encode queue."] error:nil]; - } - } -} - -// assumes queue no longer mutable -- (NSData *)archiveQueue:(NSArray *)queue { - NSMutableArray *archivedRequests = [NSMutableArray new]; - NSSet *requestClasses = [BNCServerRequestQueue replayableRequestClasses]; - for (BNCServerRequest *request in queue) { - - // only persist analytics requests, skip the rest - if ([requestClasses containsObject:request.class]) { - [archivedRequests addObject:request]; - } - } - return [self archiveObject:archivedRequests]; -} - -// For testing backwards compatibility -// The old version did a double archive and didn't filter replayable requests -- (NSData *)oldArchiveQueue:(NSArray *)queue { - NSMutableArray *archivedRequests = [NSMutableArray new]; - for (BNCServerRequest *request in queue) { - - // archive every request - NSData *encodedRequest = [self archiveObject:request]; - [archivedRequests addObject:encodedRequest]; - } - return [self archiveObject:archivedRequests]; -} - -- (NSData *)archiveObject:(NSObject *)object { - NSData *data = nil; - NSError *error = nil; - data = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:YES error:&error]; - - if (!data && error) { - [[BranchLogger shared] logWarning:@"Failed to archive: %@" error:error]; - } - return data; -} - -// Loads saved requests from disk. Only called on app start. -- (void)retrieve { - @synchronized (self) { - NSError *error = nil; - NSData *data = [NSData dataWithContentsOfURL:self.class.URLForQueueFile options:0 error:&error]; - if (!error && data) { - NSMutableArray *decodedQueue = [self unarchiveQueueFromData:data]; - self.queue = decodedQueue; - } - } -} - -// It's been reported that unarchive can fail in some situations. In that case, remove the queued requests file. -- (void)removeSaveFile { - NSURL *fileURL = [BNCServerRequestQueue URLForQueueFile]; - if (fileURL) { - NSError *error; - [NSFileManager.defaultManager removeItemAtURL:fileURL error:&error]; - - if (error) { - [[BranchLogger shared] logError:@"Failed to remove archived queue" error:error]; - } - } -} - -- (NSMutableArray *)unarchiveQueueFromData:(NSData *)data { - NSMutableArray *queue = [NSMutableArray new]; - - NSArray *requestArray = nil; - id tmp = [self unarchiveObjectFromData:data]; - if ([tmp isKindOfClass:[NSArray class]]) { - requestArray = (NSArray *)tmp; - } - - // validate request array - // There should never be an object that is not a replayable class or NSData - for (id request in requestArray) { - id tmpRequest = request; - - // handle legacy NSData - if ([request isKindOfClass:[NSData class]]) { - tmpRequest = [self unarchiveObjectFromData:request]; - } - - // make sure we didn't unarchive something unexpected - if ([[BNCServerRequestQueue replayableRequestClasses] containsObject:[tmpRequest class]]) { - [queue addObject:tmpRequest]; - } - } - - return queue; -} - -- (id)unarchiveObjectFromData:(NSData *)data { - NSError *error; - id object = [NSKeyedUnarchiver unarchivedObjectOfClasses:[BNCServerRequestQueue encodableClasses] fromData:data error:&error]; - - if (error) { - [[BranchLogger shared] logError:@"Failed to unarchive" error:error]; - [self removeSaveFile]; - } - - return object; -} - -// only replay analytics requests, the others are time sensitive -+ (NSSet *)replayableRequestClasses { - static NSSet *requestClasses = nil; - static dispatch_once_t onceToken = 0; - dispatch_once(&onceToken, ^ { - NSArray *tmp = @[ - [BranchOpenRequest class], - [BranchInstallRequest class], - [BranchEventRequest class] - ]; - requestClasses = [NSSet setWithArray:tmp]; - }); - - return requestClasses; -} - -// encodable classes also includes NSArray and NSData -+ (NSSet *)encodableClasses { - static NSSet *classes = nil; - static dispatch_once_t onceToken = 0; - dispatch_once(&onceToken, ^ { - NSMutableArray *tmp = [NSMutableArray new]; - [tmp addObject:[NSArray class]]; // root object - [tmp addObject:[NSData class]]; // legacy format compatibility - - // add all replayable request objects - [tmp addObjectsFromArray: [[BNCServerRequestQueue replayableRequestClasses] allObjects]]; - - classes = [NSSet setWithArray:tmp]; - }); - - return classes; -} - -+ (NSURL * _Nonnull) URLForQueueFile { - static NSURL *URL = nil; - static dispatch_once_t onceToken = 0; - dispatch_once(&onceToken, ^ { - URL = BNCURLForBranchDirectory(); - URL = [URL URLByAppendingPathComponent:BRANCH_QUEUE_FILE isDirectory:NO]; - }); - return URL; -} + (instancetype)getInstance { static BNCServerRequestQueue *sharedQueue = nil; static dispatch_once_t onceToken = 0; dispatch_once(&onceToken, ^ { sharedQueue = [[BNCServerRequestQueue alloc] init]; - [sharedQueue retrieve]; - [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Retrieved from storage: %@.", sharedQueue] error:nil]; }); return sharedQueue; } diff --git a/Sources/BranchSDK/BNCURLFilter.m b/Sources/BranchSDK/BNCURLFilter.m index 65cf25ff5..387070f4c 100644 --- a/Sources/BranchSDK/BNCURLFilter.m +++ b/Sources/BranchSDK/BNCURLFilter.m @@ -130,9 +130,13 @@ - (BOOL)foundUpdatedURLList:(id)operation { if (statusCode == 404) { [[BranchLogger shared] logDebug:@"No update for URL ignore list found." error:nil]; return NO; - } else if (statusCode != 200 || error != nil || jsonString == nil) { + } else if (statusCode != 200 || error != nil || jsonString == nil) { if ([NSError branchDNSBlockingError:error]) { - [[BranchLogger shared] logWarning:@"Possible DNS Ad Blocker" error:error]; + NSError *dnsError = [NSError branchErrorWithCode:BNCDNSAdBlockerError]; + [[BranchLogger shared] logError:[NSString stringWithFormat:@"Possible DNS Ad Blocker. Giving up on request with HTTP status code %ld. Underlying error: %@", (long)statusCode, error] error:dnsError]; + } else if ([NSError branchVPNBlockingError:error]) { + NSError *vpnError = [NSError branchErrorWithCode:BNCVPNAdBlockerError]; + [[BranchLogger shared] logError:[NSString stringWithFormat:@"Possible VPN Ad Blocker. Giving up on request with HTTP status code %ld. Underlying error: %@", (long)statusCode, error] error:vpnError]; } else { [[BranchLogger shared] logWarning:@"Failed to update URL ignore list" error:operation.error]; } diff --git a/Sources/BranchSDK/Branch.m b/Sources/BranchSDK/Branch.m index 3cae94f25..ed20ff6f9 100644 --- a/Sources/BranchSDK/Branch.m +++ b/Sources/BranchSDK/Branch.m @@ -70,6 +70,7 @@ NSString * const BRANCH_INIT_KEY_IS_FIRST_SESSION = @"+is_first_session"; NSString * const BRANCH_INIT_KEY_CLICKED_BRANCH_LINK = @"+clicked_branch_link"; static NSString * const BRANCH_PUSH_NOTIFICATION_PAYLOAD_KEY = @"branch"; +static NSString * const BRANCH_DEFER_INIT_FOR_PLUGIN_RUNTIME_KEY = @"deferInitForPluginRuntime"; NSString * const BNCCanonicalIdList = @"$canonical_identifier_list"; NSString * const BNCPurchaseAmount = @"$amount"; @@ -497,7 +498,7 @@ - (void)setRetryInterval:(NSTimeInterval)retryInterval { } -- (void)setRequestMetadataKey:(NSString *)key value:(NSObject *)value { +- (void)setRequestMetadataKey:(NSString *)key value:(NSString *)value { [self.preferenceHelper setRequestMetadataKey:key value:value]; } @@ -510,6 +511,8 @@ + (BOOL)trackingDisabled { + (void)setTrackingDisabled:(BOOL)disabled { @synchronized(self) { + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"setTrackingDisabled to %d", disabled] error:nil]; + BOOL currentSetting = self.trackingDisabled; if (!!currentSetting == !!disabled) return; @@ -521,6 +524,8 @@ + (void)setTrackingDisabled:(BOOL)disabled { Branch *branch = Branch.getInstance; [branch clearNetworkQueue]; branch.initializationStatus = BNCInitStatusUninitialized; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"initializationStatus %ld", branch.initializationStatus] error:nil]; + [branch.linkCache clear]; // Release the lock in case it's locked: [BranchOpenRequest releaseOpenResponseLock]; @@ -528,7 +533,7 @@ + (void)setTrackingDisabled:(BOOL)disabled { // Set the flag: [BNCPreferenceHelper sharedInstance].trackingDisabled = NO; // Initialize a Branch session: - [Branch.getInstance initUserSessionAndCallCallback:NO sceneIdentifier:nil urlString:nil]; + [Branch.getInstance initUserSessionAndCallCallback:NO sceneIdentifier:nil urlString:nil reset:NO]; } } } @@ -654,9 +659,15 @@ - (void)initSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL) - (void)initSceneSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL)isReferrable explicitlyRequestedReferrable:(BOOL)explicitlyRequestedReferrable automaticallyDisplayController:(BOOL)automaticallyDisplayController registerDeepLinkHandler:(void (^)(BNCInitSessionResponse * _Nullable initResponse, NSError * _Nullable error))callback { + NSMutableDictionary * optionsWithDeferredInit = [[NSMutableDictionary alloc ] initWithDictionary:options]; + if (self.deferInitForPluginRuntime) { + [optionsWithDeferredInit setObject:@1 forKey:@"BRANCH_DEFER_INIT_FOR_PLUGIN_RUNTIME_KEY"]; + } else { + [optionsWithDeferredInit setObject:@0 forKey:@"BRANCH_DEFER_INIT_FOR_PLUGIN_RUNTIME_KEY"]; + } [self deferInitBlock:^{ self.sceneSessionInitWithCallback = callback; - [self initSessionWithLaunchOptions:options isReferrable:isReferrable explicitlyRequestedReferrable:explicitlyRequestedReferrable automaticallyDisplayController:automaticallyDisplayController]; + [self initSessionWithLaunchOptions:(NSDictionary *)optionsWithDeferredInit isReferrable:isReferrable explicitlyRequestedReferrable:explicitlyRequestedReferrable automaticallyDisplayController:automaticallyDisplayController]; }]; } @@ -681,7 +692,9 @@ - (void)initSessionWithLaunchOptions:(NSDictionary *)options } #endif - [self initUserSessionAndCallCallback:YES sceneIdentifier:nil urlString:pushURL]; + if(pushURL || [[options objectForKey:@"BRANCH_DEFER_INIT_FOR_PLUGIN_RUNTIME_KEY"] isEqualToNumber:@1] || (![options.allKeys containsObject:UIApplicationLaunchOptionsURLKey] && ![options.allKeys containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) ) { + [self initUserSessionAndCallCallback:YES sceneIdentifier:nil urlString:pushURL reset:NO]; + } } - (void)setDeepLinkDebugMode:(NSDictionary *)debugParams { @@ -711,10 +724,12 @@ - (BOOL)handleDeepLink:(NSURL *)url { } - (BOOL)handleDeepLink:(NSURL *)url sceneIdentifier:(NSString *)sceneIdentifier { + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Handle deep link %@", url] error:nil]; // we've been resetting the session on all deeplinks for quite some time // this allows foreground links to callback self.initializationStatus = BNCInitStatusUninitialized; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"initializationStatus %ld", self.initializationStatus] error:nil]; //Check the referring url/uri for query parameters and save them BNCReferringURLUtility *utility = [BNCReferringURLUtility new]; @@ -727,10 +742,12 @@ - (BOOL)handleDeepLink:(NSURL *)url sceneIdentifier:(NSString *)sceneIdentifier } if (pattern) { self.preferenceHelper.dropURLOpen = YES; - self.preferenceHelper.externalIntentURI = pattern; - self.preferenceHelper.referringURL = pattern; + + NSString *urlString = [url absoluteString]; + self.preferenceHelper.externalIntentURI = urlString; + self.preferenceHelper.referringURL = urlString; - [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:nil]; + [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:nil reset:YES]; return NO; } @@ -745,6 +762,8 @@ - (BOOL)handleDeepLink:(NSURL *)url sceneIdentifier:(NSString *)sceneIdentifier - (BOOL)handleSchemeDeepLink_private:(NSURL*)url sceneIdentifier:(NSString *)sceneIdentifier { BOOL handled = NO; self.preferenceHelper.referringURL = nil; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Set referringURL to %@", self.preferenceHelper.referringURL] error:nil]; + if (url && ![url isEqual:[NSNull null]]) { NSString *urlScheme = [url scheme]; @@ -755,12 +774,14 @@ - (BOOL)handleSchemeDeepLink_private:(NSURL*)url sceneIdentifier:(NSString *)sce if (urlScheme && [scheme isEqualToString:urlScheme]) { self.preferenceHelper.externalIntentURI = [url absoluteString]; self.preferenceHelper.referringURL = [url absoluteString]; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Allowed scheme list, set externalIntentURI and referringURL to %@", [url absoluteString]] error:nil]; break; } } } else { self.preferenceHelper.externalIntentURI = [url absoluteString]; self.preferenceHelper.referringURL = [url absoluteString]; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Set externalIntentURI and referringURL to %@", [url absoluteString]] error:nil]; } NSString *query = [url fragment]; @@ -774,7 +795,7 @@ - (BOOL)handleSchemeDeepLink_private:(NSURL*)url sceneIdentifier:(NSString *)sce self.preferenceHelper.linkClickIdentifier = params[@"link_click_id"]; } } - [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:url.absoluteString]; + [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:url.absoluteString reset:YES]; return handled; } @@ -805,9 +826,10 @@ - (BOOL)handleUniversalDeepLink_private:(NSString*)urlString sceneIdentifier:(NS if (urlString.length) { self.preferenceHelper.universalLinkUrl = urlString; self.preferenceHelper.referringURL = urlString; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Set universalLinkUrl and referringURL to %@", urlString] error:nil]; } - [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:urlString]; + [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:urlString reset:YES]; return [Branch isBranchLink:urlString]; } @@ -847,7 +869,7 @@ - (BOOL)continueUserActivity:(NSUserActivity *)userActivity sceneIdentifier:(NSS } #endif - [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:userActivity.webpageURL.absoluteString]; + [self initUserSessionAndCallCallback:YES sceneIdentifier:sceneIdentifier urlString:userActivity.webpageURL.absoluteString reset:YES]; return spotlightIdentifier != nil; } @@ -1742,17 +1764,24 @@ - (void)applicationDidBecomeActive { dispatch_async(self.isolationQueue, ^(){ // if necessary, creates a new organic open BOOL installOrOpenInQueue = [self.requestQueue containsInstallOrOpen]; + + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"applicationDidBecomeActive installOrOpenInQueue %d", installOrOpenInQueue] error:nil]; + if (!Branch.trackingDisabled && self.initializationStatus != BNCInitStatusInitialized && !installOrOpenInQueue) { - [self initUserSessionAndCallCallback:YES sceneIdentifier:nil urlString:nil]; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"applicationDidBecomeActive trackingDisabled %d initializationStatus %d installOrOpenInQueue %d", Branch.trackingDisabled, self.initializationStatus, installOrOpenInQueue] error:nil]; + + [self initUserSessionAndCallCallback:YES sceneIdentifier:nil urlString:nil reset:NO]; } }); } - (void)applicationWillResignActive { + [[BranchLogger shared] logVerbose:@"applicationWillResignActive" error:nil]; + dispatch_async(self.isolationQueue, ^(){ if (!Branch.trackingDisabled) { self.initializationStatus = BNCInitStatusUninitialized; - [self.requestQueue persistImmediately]; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"applicationWillResignActive initializationStatus %ld", self.initializationStatus] error:nil]; [BranchOpenRequest setWaitNeededForOpenResponseLock]; } }); @@ -1885,6 +1914,8 @@ - (void)processNextQueueItem { self.networkCount = 1; dispatch_semaphore_signal(self.processing_sema); BNCServerRequest *req = [self.requestQueue peek]; + + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"Processing %@", req]error:nil]; if (req) { @@ -1906,7 +1937,7 @@ - (void)processNextQueueItem { return; } } - + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^ { [req makeRequest:self.serverInterface key:self.class.branchKey callback: @@ -1966,11 +1997,11 @@ - (void)notifyNativeToInit { - (void)initSafetyCheck { if (self.initializationStatus == BNCInitStatusUninitialized) { [[BranchLogger shared] logDebug:@"Branch avoided an error by preemptively initializing." error:nil]; - [self initUserSessionAndCallCallback:NO sceneIdentifier:nil urlString:nil]; + [self initUserSessionAndCallCallback:NO sceneIdentifier:nil urlString:nil reset:NO]; } } -- (void)initUserSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSString *)sceneIdentifier urlString:(NSString *)urlString { +- (void)initUserSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSString *)sceneIdentifier urlString:(NSString *)urlString reset:(BOOL)reset { @synchronized (self) { if (self.deferInitForPluginRuntime) { @@ -1992,8 +2023,10 @@ - (void)initUserSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSStr dispatch_async(self.isolationQueue, ^(){ - // If the session is not yet initialized - if (self.initializationStatus == BNCInitStatusUninitialized) { + + // If the session is not yet initialized OR + // If the session is already initialized or is initializing but we need to reset it. + if ( reset || self.initializationStatus == BNCInitStatusUninitialized) { [self initializeSessionAndCallCallback:callCallback sceneIdentifier:sceneIdentifier urlString:urlString]; } // If the session was initialized, but callCallback was specified, do so. @@ -2078,6 +2111,8 @@ - (void)initializeSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSS } self.initializationStatus = BNCInitStatusInitializing; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"initializationStatus %ld", self.initializationStatus] error:nil]; + [self processNextQueueItem]; }); } @@ -2087,6 +2122,8 @@ - (void)initializeSessionAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSS - (void)handleInitSuccessAndCallCallback:(BOOL)callCallback sceneIdentifier:(NSString *)sceneIdentifier { self.initializationStatus = BNCInitStatusInitialized; + [[BranchLogger shared] logVerbose:[NSString stringWithFormat:@"initializationStatus %ld", self.initializationStatus] error:nil]; + NSDictionary *latestReferringParams = [self getLatestReferringParams]; if ([latestReferringParams[@"_branch_validate"] isEqualToString:@"060514"]) { @@ -2303,6 +2340,7 @@ - (void)presentSharingViewController:(UIViewController +@property (nonatomic, copy, readwrite) NSString *requestUUID; +@property (nonatomic, copy, readwrite) NSNumber *requestCreationTimeStamp; + - (void)makeRequest:(BNCServerInterface *)serverInterface key:(NSString *)key callback:(BNCServerCallback)callback; - (void)processResponse:(BNCServerResponse *)response error:(NSError *)error; - (void)safeSetValue:(NSObject *)value forKey:(NSString *)key onDict:(NSMutableDictionary *)dict; ++ (NSString *) generateRequestUUIDFromDate:(NSDate *) localDate; @end diff --git a/Sources/BranchSDK/Public/BNCServerRequestQueue.h b/Sources/BranchSDK/Public/BNCServerRequestQueue.h index 535034112..1c9d9391f 100755 --- a/Sources/BranchSDK/Public/BNCServerRequestQueue.h +++ b/Sources/BranchSDK/Public/BNCServerRequestQueue.h @@ -25,8 +25,5 @@ - (BranchOpenRequest *)findExistingInstallOrOpen; -- (void)persistEventually; -- (void)persistImmediately; - + (id)getInstance; @end diff --git a/Sources/BranchSDK/Public/Branch.h b/Sources/BranchSDK/Public/Branch.h index 6f0cc0572..c1334bbaf 100644 --- a/Sources/BranchSDK/Public/Branch.h +++ b/Sources/BranchSDK/Public/Branch.h @@ -764,9 +764,9 @@ Sets a custom base URL for all calls to the Branch API. Key-value pairs to be included in the metadata on every request. @param key String to be included in request metadata - @param value Object to be included in request metadata + @param value Value to be included in request metadata */ -- (void)setRequestMetadataKey:(NSString *)key value:(nullable id)value; +- (void)setRequestMetadataKey:(NSString *)key value:(nullable NSString *)value; /** Disables the Branch SDK from tracking the user. This is useful for GDPR privacy compliance. diff --git a/scripts/version.sh b/scripts/version.sh index 357544c9c..f83d5093d 100755 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -30,7 +30,7 @@ Options: USAGE } -version=3.4.4 +version=3.6.5 prev_version="$version" if (( $# == 0 )); then