Skip to content

Commit

Permalink
Merge pull request #1368 from BranchMetrics/SDK-2263
Browse files Browse the repository at this point in the history
Add safety fallback when we are unable to unarchive requests
  • Loading branch information
echo-branch authored Apr 3, 2024
2 parents 1435b6b + 104c760 commit 980a0d5
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 4 deletions.
120 changes: 120 additions & 0 deletions Branch-TestBed/Branch-SDK-Tests/BNCClassSerializationTests.m
Original file line number Diff line number Diff line change
@@ -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 <XCTest/XCTest.h>
#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
5 changes: 5 additions & 0 deletions Branch-TestBed/Branch-TestBed.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@
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 */; };
5F8B7B4021B5F5CD009CE0A6 /* libBranch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 466B58381B17773000A69EDE /* libBranch.a */; };
Expand Down Expand Up @@ -475,6 +477,7 @@
5F644BB72B7AA811000DCD78 /* BNCCallbackMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCCallbackMap.m; sourceTree = "<group>"; };
5F644BB82B7AA811000DCD78 /* BNCEventUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCEventUtils.m; sourceTree = "<group>"; };
5F67F48D228F535500067429 /* BNCEncodingUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCEncodingUtilsTests.m; sourceTree = "<group>"; };
5F6D86D82BB5E9650068B536 /* BNCClassSerializationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCClassSerializationTests.m; sourceTree = "<group>"; };
5F73FC8023314697000EBD32 /* BNCJSONUtilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCJSONUtilityTests.m; sourceTree = "<group>"; };
5F8650192B76DA3200364BDE /* NSMutableDictionaryBranchTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSMutableDictionaryBranchTests.m; sourceTree = "<group>"; };
5F892EC4236116CC0023AEC1 /* NSErrorBranchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSErrorBranchTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -648,6 +651,7 @@
5F892EC4236116CC0023AEC1 /* NSErrorBranchTests.m */,
5F8650192B76DA3200364BDE /* NSMutableDictionaryBranchTests.m */,
4D16839E2098C901008819E3 /* NSStringBranchTests.m */,
5F6D86D82BB5E9650068B536 /* BNCClassSerializationTests.m */,
);
path = "Branch-SDK-Tests";
sourceTree = "<group>";
Expand Down Expand Up @@ -1371,6 +1375,7 @@
C15CC9DE2ABCB549003CC339 /* BNCCurrencyTests.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 */,
Expand Down
18 changes: 16 additions & 2 deletions Sources/BranchSDK/BNCServerRequestQueue.m
Original file line number Diff line number Diff line change
Expand Up @@ -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<BNCServerRequest *> *)unarchiveQueueFromData:(NSData *)data {
NSMutableArray<BNCServerRequest *> *queue = [NSMutableArray new];

Expand Down Expand Up @@ -317,7 +330,8 @@ - (id)unarchiveObjectFromData:(NSData *)data {
id object = [NSKeyedUnarchiver unarchivedObjectOfClasses:[BNCServerRequestQueue encodableClasses] fromData:data error:&error];

if (error) {
[[BranchLogger shared] logWarning:@"Failed to unarchive" error:error];
[[BranchLogger shared] logError:@"Failed to unarchive" error:error];
[self removeSaveFile];
}

return object;
Expand All @@ -331,7 +345,7 @@ - (id)unarchiveObjectFromData:(NSData *)data {
NSArray *tmp = @[
[BranchOpenRequest class],
[BranchInstallRequest class],
[BranchEventRequest class],
[BranchEventRequest class]
];
requestClasses = [NSSet setWithArray:tmp];
});
Expand Down
7 changes: 5 additions & 2 deletions Sources/BranchSDK/BranchEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,11 @@ - (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) return self;

self.serverURL = [decoder decodeObjectOfClass:NSString.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;
}

Expand Down
15 changes: 15 additions & 0 deletions Sources/BranchSDK/BranchOpenRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 980a0d5

Please sign in to comment.