From ed3ca281238d382b5821944e0148527b41164c78 Mon Sep 17 00:00:00 2001 From: echo Date: Tue, 26 Mar 2024 16:03:12 -0700 Subject: [PATCH 1/6] Add safety fallback when we are unable to unarchive requests --- Sources/BranchSDK/BNCServerRequestQueue.m | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Sources/BranchSDK/BNCServerRequestQueue.m b/Sources/BranchSDK/BNCServerRequestQueue.m index 534946775..744da069e 100755 --- a/Sources/BranchSDK/BNCServerRequestQueue.m +++ b/Sources/BranchSDK/BNCServerRequestQueue.m @@ -284,6 +284,19 @@ - (void)retrieve { } } +// 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]; @@ -317,7 +330,8 @@ - (id)unarchiveObjectFromData:(NSData *)data { id object = [NSKeyedUnarchiver unarchivedObjectOfClasses:[BNCServerRequestQueue encodableClasses] fromData:data error:&error]; if (error) { - [[BranchLogger shared] logWarning:[NSString stringWithFormat:@"Failed to unarchive: %@", error]]; + [[BranchLogger shared] logError:@"Failed to unarchive" error:error]; + [self removeSaveFile]; } return object; @@ -331,7 +345,7 @@ - (id)unarchiveObjectFromData:(NSData *)data { NSArray *tmp = @[ [BranchOpenRequest class], [BranchInstallRequest class], - [BranchEventRequest class], + [BranchEventRequest class] ]; requestClasses = [NSSet setWithArray:tmp]; }); @@ -347,6 +361,7 @@ - (id)unarchiveObjectFromData:(NSData *)data { NSMutableArray *tmp = [NSMutableArray new]; [tmp addObject:[NSArray class]]; // root object [tmp addObject:[NSData class]]; // legacy format compatibility + [tmp addObject:[NSURL class]]; // add all replayable request objects [tmp addObjectsFromArray: [[BNCServerRequestQueue replayableRequestClasses] allObjects]]; From b7376186e95d99174795bf4ab5859b57de73aa48 Mon Sep 17 00:00:00 2001 From: echo Date: Wed, 27 Mar 2024 23:01:38 -0700 Subject: [PATCH 2/6] Fix very old coding error --- Sources/BranchSDK/BranchEvent.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/BranchSDK/BranchEvent.m b/Sources/BranchSDK/BranchEvent.m index 392cc9952..257d4524c 100644 --- a/Sources/BranchSDK/BranchEvent.m +++ b/Sources/BranchSDK/BranchEvent.m @@ -134,7 +134,7 @@ - (instancetype)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) return self; - self.serverURL = [decoder decodeObjectOfClass:NSString.class forKey:@"serverURL"]; + self.serverURL = [decoder decodeObjectOfClass:NSURL.class forKey:@"serverURL"]; self.eventDictionary = [decoder decodeObjectOfClass:NSDictionary.class forKey:@"eventDictionary"]; return self; } From aae88f33f04e5add328f48b146cb4a71e87b1523 Mon Sep 17 00:00:00 2001 From: echo Date: Wed, 27 Mar 2024 23:10:08 -0700 Subject: [PATCH 3/6] Remove invalid fix attempt --- Sources/BranchSDK/BNCServerRequestQueue.m | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/BranchSDK/BNCServerRequestQueue.m b/Sources/BranchSDK/BNCServerRequestQueue.m index 744da069e..dcc8ad8e0 100755 --- a/Sources/BranchSDK/BNCServerRequestQueue.m +++ b/Sources/BranchSDK/BNCServerRequestQueue.m @@ -361,7 +361,6 @@ - (id)unarchiveObjectFromData:(NSData *)data { NSMutableArray *tmp = [NSMutableArray new]; [tmp addObject:[NSArray class]]; // root object [tmp addObject:[NSData class]]; // legacy format compatibility - [tmp addObject:[NSURL class]]; // add all replayable request objects [tmp addObjectsFromArray: [[BNCServerRequestQueue replayableRequestClasses] allObjects]]; From dcbbb34951363d9da02f63f6d8ce0a46687d46fa Mon Sep 17 00:00:00 2001 From: echo Date: Wed, 27 Mar 2024 23:32:01 -0700 Subject: [PATCH 4/6] Also fix event dictionary serialization. --- Sources/BranchSDK/BranchEvent.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/BranchSDK/BranchEvent.m b/Sources/BranchSDK/BranchEvent.m index 257d4524c..0dd108ef1 100644 --- a/Sources/BranchSDK/BranchEvent.m +++ b/Sources/BranchSDK/BranchEvent.m @@ -134,8 +134,11 @@ - (instancetype)initWithCoder:(NSCoder *)decoder { self = [super initWithCoder:decoder]; if (!self) return self; - self.serverURL = [decoder decodeObjectOfClass:NSURL.class forKey:@"serverURL"]; - self.eventDictionary = [decoder decodeObjectOfClass:NSDictionary.class forKey:@"eventDictionary"]; + self.serverURL = [decoder decodeObjectOfClass:NSURL.class forKey:@"serverURL"]; + + NSSet *classes = [NSSet setWithArray:@[NSDictionary.class, NSArray.class, NSString.class, NSNumber.class]]; + self.eventDictionary = [decoder decodeObjectOfClasses:classes forKey:@"eventDictionary"]; + return self; } From b15b22f6b8766b7dd4588308b693472bd5a1a605 Mon Sep 17 00:00:00 2001 From: echo Date: Thu, 28 Mar 2024 21:25:23 -0700 Subject: [PATCH 5/6] Add unit tests --- .../BNCClassSerializationTests.m | 117 ++++++++++++++++++ .../Branch-TestBed.xcodeproj/project.pbxproj | 4 + 2 files changed, 121 insertions(+) create mode 100644 Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m new file mode 100644 index 000000000..501e17e4f --- /dev/null +++ b/Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m @@ -0,0 +1,117 @@ +// +// 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 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; + + // Should the urlString be restored? Probably not. + //XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); + XCTAssertNil(unarchivedRequest.callback); +} + +- (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; + + // Should the urlString be restored? Probably not. + //XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); + XCTAssertNil(unarchivedRequest.callback); + XCTAssertTrue([@"install" isEqualToString:[unarchivedRequest getActionName]]); +} + +@end diff --git a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj index eb3c8474b..3dfa83dfa 100644 --- a/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj +++ b/Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj @@ -196,6 +196,7 @@ 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 */; }; 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 */; }; @@ -496,6 +497,7 @@ 5F644BB72B7AA811000DCD78 /* BNCCallbackMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCCallbackMap.m; sourceTree = ""; }; 5F644BB82B7AA811000DCD78 /* BNCEventUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCEventUtils.m; sourceTree = ""; }; 5F67F48D228F535500067429 /* BNCEncodingUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCEncodingUtilsTests.m; sourceTree = ""; }; + 5F6D86D82BB5E9650068B536 /* BNCClassSerializationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCClassSerializationTests.m; sourceTree = ""; }; 5F73FC8023314697000EBD32 /* BNCJSONUtilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCJSONUtilityTests.m; sourceTree = ""; }; 5F8650192B76DA3200364BDE /* NSMutableDictionaryBranchTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSMutableDictionaryBranchTests.m; sourceTree = ""; }; 5F892EC4236116CC0023AEC1 /* NSErrorBranchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSErrorBranchTests.m; sourceTree = ""; }; @@ -681,6 +683,7 @@ 5F892EC4236116CC0023AEC1 /* NSErrorBranchTests.m */, 5F8650192B76DA3200364BDE /* NSMutableDictionaryBranchTests.m */, 4D16839E2098C901008819E3 /* NSStringBranchTests.m */, + 5F6D86D82BB5E9650068B536 /* BNCClassSerializationTests.m */, ); path = "Branch-SDK-Tests"; sourceTree = ""; @@ -1409,6 +1412,7 @@ 5F83B9ED2433BAAA0054A022 /* BNCServerInterface.Test.m in Sources */, 5F205D0823186AF700C776D1 /* BNCUserAgentCollectorTests.m in Sources */, 5FCF7EAD29DC96A7008D629E /* BNCURLFilterSkiplistUpgradeTests.m in Sources */, + 5F6D86D92BB5E9650068B536 /* BNCClassSerializationTests.m in Sources */, E7A728BD2AA9A112009343B7 /* BNCAPIServerTest.m in Sources */, 4D1683C12098C902008819E3 /* BNCApplicationTests.m in Sources */, 4D1683B92098C902008819E3 /* BNCSystemObserverTests.m in Sources */, From 4604b370f974e5c955aad57c2c046af0218a277c Mon Sep 17 00:00:00 2001 From: echo Date: Fri, 29 Mar 2024 13:44:11 -0700 Subject: [PATCH 6/6] Fix serialization of install/open for 3.3. This only impacts error handling. --- .../Branch-SDK-Tests/BNCClassSerializationTests.m | 11 +++++++---- Sources/BranchSDK/BranchOpenRequest.m | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m b/Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m index 501e17e4f..f1af287d9 100644 --- a/Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m +++ b/Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m @@ -16,6 +16,10 @@ @interface BranchEvent() - (NSDictionary *)buildEventDictionary; @end +@interface BranchOpenRequest() +- (NSString *)getActionName; +@end + @interface BNCClassSerializationTests : XCTestCase @end @@ -84,9 +88,9 @@ - (void)testBranchOpenRequestArchive { XCTAssertTrue([object isKindOfClass:BranchOpenRequest.class]); BranchOpenRequest *unarchivedRequest = (BranchOpenRequest *)object; - // Should the urlString be restored? Probably not. - //XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); + XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); XCTAssertNil(unarchivedRequest.callback); + XCTAssertTrue([@"open" isEqualToString:[unarchivedRequest getActionName]]); } - (void)testBranchInstallRequestArchive { @@ -108,8 +112,7 @@ - (void)testBranchInstallRequestArchive { XCTAssertTrue([object isKindOfClass:BranchInstallRequest.class]); BranchInstallRequest *unarchivedRequest = (BranchInstallRequest *)object; - // Should the urlString be restored? Probably not. - //XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); + XCTAssertTrue([request.urlString isEqualToString:unarchivedRequest.urlString]); XCTAssertNil(unarchivedRequest.callback); XCTAssertTrue([@"install" isEqualToString:[unarchivedRequest getActionName]]); } diff --git a/Sources/BranchSDK/BranchOpenRequest.m b/Sources/BranchSDK/BranchOpenRequest.m index d7c23af8e..38533f8fe 100644 --- a/Sources/BranchSDK/BranchOpenRequest.m +++ b/Sources/BranchSDK/BranchOpenRequest.m @@ -253,6 +253,21 @@ - (NSString *)getActionName { return @"open"; } +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) return self; + self.urlString = [decoder decodeObjectOfClass:NSString.class forKey:@"urlString"]; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + [coder encodeObject:self.urlString forKey:@"urlString"]; +} + ++ (BOOL)supportsSecureCoding { + return YES; +} #pragma - Open Response Lock Handling