diff --git a/src/ios/dependencies/Branch-SDK/BNCConfig.m b/src/ios/dependencies/Branch-SDK/BNCConfig.m index 63b0ce45..d4fefc2f 100644 --- a/src/ios/dependencies/Branch-SDK/BNCConfig.m +++ b/src/ios/dependencies/Branch-SDK/BNCConfig.m @@ -11,4 +11,4 @@ NSString * const BNC_API_BASE_URL = @"https://api.branch.io"; NSString * const BNC_API_VERSION = @"v1"; NSString * const BNC_LINK_URL = @"https://bnc.lt"; -NSString * const BNC_SDK_VERSION = @"0.14.12"; +NSString * const BNC_SDK_VERSION = @"0.15.3"; diff --git a/src/ios/dependencies/Branch-SDK/BNCLog.h b/src/ios/dependencies/Branch-SDK/BNCLog.h index 83ae1346..399df6a7 100644 --- a/src/ios/dependencies/Branch-SDK/BNCLog.h +++ b/src/ios/dependencies/Branch-SDK/BNCLog.h @@ -119,11 +119,11 @@ extern void BNCLogSetFlushFunction(BNCLogFlushFunctionPtr _Nullable flushFunctio extern BNCLogFlushFunctionPtr _Nullable BNCLogFlushFunction(); -#pragma mark - BNCLogMessageInternal +#pragma mark - BNCLogWriteMessage -/// The main logging function used in the logging defines. -extern void BNCLogMessageInternal( +/// The main logging function used in the variadic logging defines. +extern void BNCLogWriteMessageFormat( BNCLogLevel logLevel, const char *_Nullable sourceFileName, int sourceLineNumber, @@ -131,6 +131,14 @@ extern void BNCLogMessageInternal( ... ); +/// Swift-friendly wrapper for BNCLogWriteMessageFormat +extern void BNCLogWriteMessage( + BNCLogLevel logLevel, + NSString *_Nonnull sourceFileName, + NSUInteger sourceLineNumber, + NSString *_Nonnull message +); + /// This function synchronizes all outstanding log messages and writes them to the logging function /// set by BNCLogSetOutputFunction. extern void BNCLogFlushMessages(); @@ -141,25 +149,25 @@ extern void BNCLogFlushMessages(); ///@param format Log a debug message with the specified formatting. #define BNCLogDebug(...) \ - do { BNCLogMessageInternal(BNCLogLevelDebug, __FILE__, __LINE__, __VA_ARGS__); } while (0) + do { BNCLogWriteMessageFormat(BNCLogLevelDebug, __FILE__, __LINE__, __VA_ARGS__); } while (0) ///@param format Log a warning message with the specified formatting. #define BNCLogWarning(...) \ - do { BNCLogMessageInternal(BNCLogLevelWarning, __FILE__, __LINE__, __VA_ARGS__); } while (0) + do { BNCLogWriteMessageFormat(BNCLogLevelWarning, __FILE__, __LINE__, __VA_ARGS__); } while (0) ///@param format Log an error message with the specified formatting. #define BNCLogError(...) \ - do { BNCLogMessageInternal(BNCLogLevelError, __FILE__, __LINE__, __VA_ARGS__); } while (0) + do { BNCLogWriteMessageFormat(BNCLogLevelError, __FILE__, __LINE__, __VA_ARGS__); } while (0) ///@param format Log a message with the specified formatting. #define BNCLog(...) \ - do { BNCLogMessageInternal(BNCLogLevelLog, __FILE__, __LINE__, __VA_ARGS__); } while (0) + do { BNCLogWriteMessageFormat(BNCLogLevelLog, __FILE__, __LINE__, __VA_ARGS__); } while (0) ///Cause a programmatic breakpoint if breakpoints are enabled. #define BNCLogBreakPoint() \ do { \ if (BNCLogBreakPointsAreEnabled()) { \ - BNCLogMessageInternal(BNCLogLevelBreakPoint, __FILE__, __LINE__, @"Programmatic breakpoint."); \ + BNCLogWriteMessageFormat(BNCLogLevelBreakPoint, __FILE__, __LINE__, @"Programmatic breakpoint."); \ if (BNCDebuggerIsAttached()) { \ BNCLogFlushMessages(); \ BNCDebugBreakpoint(); \ @@ -171,7 +179,7 @@ extern void BNCLogFlushMessages(); #define BNCBreakPointWithMessage(...) \ do { \ if (BNCLogBreakPointsAreEnabled() { \ - BNCLogMessageInternal(BNCLogLevelBreakPoint, __FILE__, __LINE__, __VA_ARGS__); \ + BNCLogWriteMessageFormat(BNCLogLevelBreakPoint, __FILE__, __LINE__, __VA_ARGS__); \ if (BNCDebuggerIsAttached()) { \ BNCLogFlushMessages(); \ BNCDebugBreakpoint(); \ @@ -183,7 +191,7 @@ extern void BNCLogFlushMessages(); #define BNCLogAssert(condition) \ do { \ if (!(condition)) { \ - BNCLogMessageInternal(BNCLogLevelAssert, __FILE__, __LINE__, @"(%s) !!!", #condition); \ + BNCLogWriteMessageFormat(BNCLogLevelAssert, __FILE__, __LINE__, @"(%s) !!!", #condition); \ if (BNCLogBreakPointsAreEnabled() && BNCDebuggerIsAttached()) { \ BNCLogFlushMessages(); \ BNCDebugBreakpoint(); \ @@ -197,7 +205,7 @@ extern void BNCLogFlushMessages(); do { \ if (!(condition)) { \ NSString *m = [NSString stringWithFormat:message, __VA_ARGS__]; \ - BNCLogMessageInternal(BNCLogLevelAssert, __FILE__, __LINE__, @"(%s) !!! %@", #condition, m); \ + BNCLogWriteMessageFormat(BNCLogLevelAssert, __FILE__, __LINE__, @"(%s) !!! %@", #condition, m); \ if (BNCLogBreakPointsAreEnabled() && BNCDebuggerIsAttached()) { \ BNCLogFlushMessages(); \ BNCDebugBreakpoint(); \ diff --git a/src/ios/dependencies/Branch-SDK/BNCLog.m b/src/ios/dependencies/Branch-SDK/BNCLog.m index 1def862c..6e5a63d3 100644 --- a/src/ios/dependencies/Branch-SDK/BNCLog.m +++ b/src/ios/dependencies/Branch-SDK/BNCLog.m @@ -463,7 +463,7 @@ void BNCLogSetBreakPointsEnabled(BOOL enabled) { #pragma mark - Log Functions -static BNCLogOutputFunctionPtr bnc_LoggingFunction = BNCLogFunctionOutputToStdOut; +static BNCLogOutputFunctionPtr bnc_LoggingFunction = nil; // Default to just NSLog output. static BNCLogFlushFunctionPtr bnc_LogFlushFunction = BNCLogFlushFileDescriptor; BNCLogOutputFunctionPtr _Nullable BNCLogOutputFunction() { @@ -498,7 +498,7 @@ void BNCLogSetFlushFunction(BNCLogFlushFunctionPtr flushFunction) { static dispatch_queue_t bnc_LogQueue = nil; -void BNCLogMessageInternal( +void BNCLogWriteMessageFormat( BNCLogLevel logLevel, const char *_Nullable file, int lineNumber, @@ -551,6 +551,15 @@ void BNCLogMessageInternal( } } +void BNCLogWriteMessage( + BNCLogLevel logLevel, + NSString *_Nonnull file, + NSUInteger lineNumber, + NSString *_Nonnull message + ) { + BNCLogWriteMessageFormat(logLevel, file.UTF8String, (int)lineNumber, @"%@", message); +} + void BNCLogFlushMessages() { if (BNCLogSynchronizeMessages()) { dispatch_sync(bnc_LogQueue, ^{ diff --git a/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.h b/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.h index fd0286d1..419a6299 100755 --- a/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.h +++ b/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.h @@ -6,13 +6,12 @@ // // -#import "BNCServerRequest.h" +#import "BNCServerRequest.h" @class BranchOpenRequest; -@interface BNCServerRequestQueue : NSObject -@property (nonatomic, readonly) unsigned int size; +@interface BNCServerRequestQueue : NSObject - (void)enqueue:(BNCServerRequest *)request; - (BNCServerRequest *)dequeue; @@ -32,4 +31,6 @@ + (id)getInstance; +@property (readonly, assign) NSInteger queueDepth; +@property (readonly, assign) BOOL isDirty; @end diff --git a/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.m b/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.m index 9288fed2..3eeafdfc 100755 --- a/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.m +++ b/src/ios/dependencies/Branch-SDK/BNCServerRequestQueue.m @@ -6,35 +6,46 @@ // // + #import "BNCServerRequestQueue.h" #import "BNCPreferenceHelper.h" #import "BranchCloseRequest.h" #import "BranchOpenRequest.h" + NSString * const BRANCH_QUEUE_FILE = @"BNCServerRequestQueue"; -NSUInteger const BATCH_WRITE_TIMEOUT = 3; +NSTimeInterval const BATCH_WRITE_TIMEOUT = 3.0; -@interface BNCServerRequestQueue() -@property (nonatomic, strong) NSMutableArray *queue; -@property (nonatomic) dispatch_queue_t asyncQueue; -@property (strong, nonatomic) NSTimer *writeTimer; +static inline uint64_t BNCNanoSecondsFromTimeInterval(NSTimeInterval interval) { + return interval * ((NSTimeInterval) NSEC_PER_SEC); +} + +@interface BNCServerRequestQueue() +@property (strong) NSMutableArray *queue; +@property (strong) dispatch_queue_t asyncQueue; +@property (strong) dispatch_source_t persistTimer; @end @implementation BNCServerRequestQueue - (id)init { - if (self = [super init]) { - self.queue = [NSMutableArray array]; - self.asyncQueue = dispatch_queue_create("brnch_persist_queue", NULL); - } + self = [super init]; + if (!self) return self; + + self.queue = [NSMutableArray array]; + self.asyncQueue = dispatch_queue_create("io.branch.persist_queue", DISPATCH_QUEUE_SERIAL); return self; } +- (void) dealloc { + [self persistImmediately]; +} + - (void)enqueue:(BNCServerRequest *)request { - @synchronized(self.queue) { + @synchronized (self) { if (request) { [self.queue addObject:request]; [self persistEventually]; @@ -43,12 +54,11 @@ - (void)enqueue:(BNCServerRequest *)request { } - (void)insert:(BNCServerRequest *)request at:(unsigned int)index { - @synchronized(self.queue) { + @synchronized (self) { if (index > self.queue.count) { [[BNCPreferenceHelper preferenceHelper] log:FILE_NAME line:LINE_NUM message:@"Invalid queue operation: index out of bound!"]; return; } - if (request) { [self.queue insertObject:request atIndex:index]; [self persistEventually]; @@ -57,38 +67,39 @@ - (void)insert:(BNCServerRequest *)request at:(unsigned int)index { } - (BNCServerRequest *)dequeue { - BNCServerRequest *request = nil; - - @synchronized(self.queue) { + @synchronized (self) { + BNCServerRequest *request = nil; if (self.queue.count > 0) { request = [self.queue objectAtIndex:0]; [self.queue removeObjectAtIndex:0]; [self persistEventually]; } - } - return request; + } } - (BNCServerRequest *)removeAt:(unsigned int)index { - BNCServerRequest *request = nil; - @synchronized(self.queue) { + @synchronized (self) { + BNCServerRequest *request = nil; if (index >= self.queue.count) { - [[BNCPreferenceHelper preferenceHelper] log:FILE_NAME line:LINE_NUM message:@"Invalid queue operation: index out of bound!"]; + [[BNCPreferenceHelper preferenceHelper] + log:FILE_NAME line:LINE_NUM + message:@"Invalid queue operation: index out of bound!"]; return nil; } request = [self.queue objectAtIndex:index]; [self.queue removeObjectAtIndex:index]; [self persistEventually]; + return request; } - - return request; } - (void)remove:(BNCServerRequest *)request { - [self.queue removeObject:request]; - [self persistEventually]; + @synchronized (self) { + [self.queue removeObject:request]; + [self persistEventually]; + } } - (BNCServerRequest *)peek { @@ -96,19 +107,25 @@ - (BNCServerRequest *)peek { } - (BNCServerRequest *)peekAt:(unsigned int)index { - if (index >= self.queue.count) { - [[BNCPreferenceHelper preferenceHelper] log:FILE_NAME line:LINE_NUM message:@"Invalid queue operation: index out of bound!"]; - return nil; + @synchronized (self) { + if (index >= self.queue.count) { + [[BNCPreferenceHelper preferenceHelper] + log:FILE_NAME line:LINE_NUM + message:@"Invalid queue operation: index out of bound!"]; + return nil; + } + + BNCServerRequest *request = nil; + request = [self.queue objectAtIndex:index]; + + return request; } - - BNCServerRequest *request = nil; - request = [self.queue objectAtIndex:index]; - - return request; } -- (unsigned int)size { - return (unsigned int)self.queue.count; +- (NSInteger)queueDepth { + @synchronized (self) { + return (NSInteger) self.queue.count; + } } - (NSString *)description { @@ -116,23 +133,27 @@ - (NSString *)description { } - (void)clearQueue { - [self.queue removeAllObjects]; - [self persistEventually]; + @synchronized (self) { + [self.queue removeAllObjects]; + [self persistEventually]; + } } - (BOOL)containsInstallOrOpen { - for (int 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]]) { - return YES; + @synchronized (self) { + for (int 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]]) { + return YES; + } } + return NO; } - return NO; } - (BOOL)removeInstallOrOpen { - @synchronized (self.queue) { + @synchronized (self) { for (int i = 0; i < self.queue.count; i++) { BranchOpenRequest *req = [self.queue objectAtIndex:i]; // Install extends open, so only need to check open. @@ -147,67 +168,84 @@ - (BOOL)removeInstallOrOpen { } - (BranchOpenRequest *)moveInstallOrOpenToFront:(NSInteger)networkCount { - BOOL requestAlreadyInProgress = networkCount > 0; + @synchronized (self) { - BNCServerRequest *openOrInstallRequest; - for (int i = 0; i < self.queue.count; i++) { - BNCServerRequest *req = [self.queue objectAtIndex:i]; - if ([req isKindOfClass:[BranchOpenRequest class]]) { - - // Already in front, nothing to do - if (i == 0 || (i == 1 && requestAlreadyInProgress)) { - return (BranchOpenRequest *)req; - } + BOOL requestAlreadyInProgress = networkCount > 0; + + BNCServerRequest *openOrInstallRequest; + for (int i = 0; i < self.queue.count; i++) { + BNCServerRequest *req = [self.queue objectAtIndex:i]; + if ([req isKindOfClass:[BranchOpenRequest class]]) { + + // Already in front, nothing to do + if (i == 0 || (i == 1 && requestAlreadyInProgress)) { + return (BranchOpenRequest *)req; + } - // Otherwise, pull this request out and stop early - openOrInstallRequest = [self removeAt:i]; - break; + // Otherwise, pull this request out and stop early + openOrInstallRequest = [self removeAt:i]; + break; + } } + + if (!openOrInstallRequest) { + [[BNCPreferenceHelper preferenceHelper] + logWarning:@"No install or open request in queue while trying to move it to the front"]; + return nil; + } + + if (!requestAlreadyInProgress || !self.queue.count) { + [self insert:openOrInstallRequest at:0]; + } + else { + [self insert:openOrInstallRequest at:1]; + } + + return (BranchOpenRequest *)openOrInstallRequest; } - - if (!openOrInstallRequest) { - [[BNCPreferenceHelper preferenceHelper] logWarning:@"No install or open request in queue while trying to move it to the front"]; - return nil; - } - - if (!requestAlreadyInProgress || !self.queue.count) { - [self insert:openOrInstallRequest at:0]; - } - else { - [self insert:openOrInstallRequest at:1]; - } - - return (BranchOpenRequest *)openOrInstallRequest; } - (BOOL)containsClose { - for (int i = 0; i < self.queue.count; i++) { - BNCServerRequest *req = [self.queue objectAtIndex:i]; - if ([req isKindOfClass:[BranchCloseRequest class]]) { - return YES; + @synchronized (self) { + for (int i = 0; i < self.queue.count; i++) { + BNCServerRequest *req = [self.queue objectAtIndex:i]; + if ([req isKindOfClass:[BranchCloseRequest class]]) { + return YES; + } } + return NO; } - - return NO; } - -#pragma mark - Private method +#pragma mark - Private Methods - (void)persistEventually { - if (!self.writeTimer.valid) { - self.writeTimer = [NSTimer scheduledTimerWithTimeInterval:BATCH_WRITE_TIMEOUT target:self selector:@selector(persistToDisk) userInfo:nil repeats:NO]; + @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) + ); + dispatch_source_set_event_handler(self.persistTimer, ^ { [self persistImmediately]; }); + dispatch_resume(self.persistTimer); } } - (void)persistImmediately { - [self.writeTimer invalidate]; - [self persistToDisk]; -} - -- (void)persistToDisk { - NSArray *requestsToPersist = [self.queue copy]; - dispatch_async(self.asyncQueue, ^ { + @synchronized (self) { + if (self.persistTimer) { + dispatch_source_cancel(self.persistTimer); + self.persistTimer = nil; + } + NSArray *requestsToPersist = [self.queue copy]; @try { NSMutableArray *encodedRequests = [[NSMutableArray alloc] init]; for (BNCServerRequest *req in requestsToPersist) { @@ -236,72 +274,82 @@ - (void)persistToDisk { NSString *warningMessage = [NSString stringWithFormat: @"An exception occurred while attempting to save the queue. Exception information:\n\n%@", - [self exceptionString:exception]]; + [self.class exceptionString:exception]]; [[BNCPreferenceHelper preferenceHelper] logWarning:warningMessage]; } - }); + } } -- (void)retrieve { - NSMutableArray *queue = [[NSMutableArray alloc] init]; - NSArray *encodedRequests = nil; - - // Capture exception while loading the queue file - @try { - NSError *error = nil; - NSData *data = [NSData dataWithContentsOfURL:self.class.URLForQueueFile options:0 error:&error]; - if ([error.domain isEqualToString:NSCocoaErrorDomain] && error.code == NSFileReadNoSuchFileError) { - encodedRequests = [NSArray new]; - } else if (!error && data) - encodedRequests = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - if (![encodedRequests isKindOfClass:[NSArray class]]) { - @throw [NSException exceptionWithName:NSInvalidArgumentException - reason:@"Saved server queue is invalid." userInfo:nil]; - } +- (BOOL) isDirty { + @synchronized (self) { + return (self.persistTimer != nil); } - @catch (NSException *exception) { - NSString *warningMessage = - [NSString stringWithFormat: - @"An exception occurred while attempting to load the queue file, " - "proceeding without requests. Exception information:\n\n%@", - [self exceptionString:exception]]; - [[BNCPreferenceHelper preferenceHelper] logWarning:warningMessage]; - self.queue = queue; - return; - } - - for (NSData *encodedRequest in encodedRequests) { - BNCServerRequest *request; +} - // Capture exceptions while parsing individual request objects +- (void)retrieve { + @synchronized (self) { + NSMutableArray *queue = [[NSMutableArray alloc] init]; + NSArray *encodedRequests = nil; + + // Capture exception while loading the queue file @try { - request = [NSKeyedUnarchiver unarchiveObjectWithData:encodedRequest]; + NSError *error = nil; + NSData *data = [NSData dataWithContentsOfURL:self.class.URLForQueueFile options:0 error:&error]; + if ([error.domain isEqualToString:NSCocoaErrorDomain] && error.code == NSFileReadNoSuchFileError) { + encodedRequests = [NSArray new]; + } else if (!error && data) + encodedRequests = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + if (![encodedRequests isKindOfClass:[NSArray class]]) { + @throw [NSException exceptionWithName:NSInvalidArgumentException + reason:@"Saved server queue is invalid." userInfo:nil]; + } } @catch (NSException *exception) { - [[BNCPreferenceHelper preferenceHelper] - logWarning:@"An exception occurred while attempting to parse a queued request, discarding."]; - continue; + NSString *warningMessage = + [NSString stringWithFormat: + @"An exception occurred while attempting to load the queue file, " + "proceeding without requests. Exception information:\n\n%@", + [self.class exceptionString:exception]]; + [[BNCPreferenceHelper preferenceHelper] logWarning:warningMessage]; + self.queue = queue; + return; } - - // Throw out invalid request types - if (![request isKindOfClass:[BNCServerRequest class]]) { - [[BNCPreferenceHelper preferenceHelper] logWarning:@"Found an invalid request object, discarding."]; - continue; + + for (NSData *encodedRequest in encodedRequests) { + BNCServerRequest *request; + + // Capture exceptions while parsing individual request objects + @try { + request = [NSKeyedUnarchiver unarchiveObjectWithData:encodedRequest]; + } + @catch (NSException *exception) { + [[BNCPreferenceHelper preferenceHelper] + logWarning:@"An exception occurred while attempting to parse a queued request, discarding."]; + continue; + } + + // Throw out invalid request types + if (![request isKindOfClass:[BNCServerRequest class]]) { + [[BNCPreferenceHelper preferenceHelper] logWarning:@"Found an invalid request object, discarding."]; + continue; + } + + // Throw out persisted close requests + if ([request isKindOfClass:[BranchCloseRequest class]]) { + continue; + } + + [queue addObject:request]; } - // Throw out persisted close requests - if ([request isKindOfClass:[BranchCloseRequest class]]) { - continue; - } - - [queue addObject:request]; + self.queue = queue; } - - self.queue = queue; } -- (NSString *)exceptionString:(NSException *)exception { - return [NSString stringWithFormat:@"Name: %@\nReason: %@\nStack:\n\t%@\n\n", exception.name, exception.reason, [exception.callStackSymbols componentsJoinedByString:@"\n\t"]]; ++ (NSString *)exceptionString:(NSException *)exception { + return [NSString stringWithFormat:@"Name: %@\nReason: %@\nStack:\n\t%@\n\n", + exception.name, exception.reason, + [exception.callStackSymbols componentsJoinedByString:@"\n\t"]]; } + (NSString *)queueFile_deprecated { @@ -347,7 +395,7 @@ + (void) initialize { } } -#pragma mark - Singleton method +#pragma mark - Shared Method + (id)getInstance { static BNCServerRequestQueue *sharedQueue = nil; @@ -361,7 +409,6 @@ + (id)getInstance { line:LINE_NUM message:@"Retrieved from Persist: %@", sharedQueue]; }); - return sharedQueue; } diff --git a/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m b/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m index 499d235b..ef6d92c4 100644 --- a/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m +++ b/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m @@ -254,7 +254,7 @@ - (UIWindow*) keyWindow { } /** - Find the top view controller that is not of type UINavigationController or UITabBarController + Find the top view controller that is not of type UINavigationController, UITabBarController, UISplitViewController */ - (UIViewController *)topViewController:(UIViewController *)baseViewController { if ([baseViewController isKindOfClass:[UINavigationController class]]) { @@ -265,6 +265,10 @@ - (UIViewController *)topViewController:(UIViewController *)baseViewController { return [self topViewController: ((UITabBarController *)baseViewController).selectedViewController]; } + if ([baseViewController isKindOfClass:[UISplitViewController class]]) { + return [self topViewController: ((UISplitViewController *)baseViewController).viewControllers.firstObject]; + } + if ([baseViewController presentedViewController] != nil) { return [self topViewController: [baseViewController presentedViewController]]; } diff --git a/src/ios/dependencies/Branch-SDK/BNCSystemObserver.m b/src/ios/dependencies/Branch-SDK/BNCSystemObserver.m index 559ee7c8..a39ec090 100644 --- a/src/ios/dependencies/Branch-SDK/BNCSystemObserver.m +++ b/src/ios/dependencies/Branch-SDK/BNCSystemObserver.m @@ -259,7 +259,7 @@ + (NSDate*) dateForPathComponent:(NSString*)component inURLs:(NSArray*)f } } if (!success || error) { - NSLog(@"Can't retrieve attributes. Success: %d Error: %@.", success, error); + NSLog(@"Warning: Can't retrieve bundle attributes. Success: %d Error: %@.", success, error); return nil; } return buildDate; diff --git a/src/ios/dependencies/Branch-SDK/Branch.m b/src/ios/dependencies/Branch-SDK/Branch.m index f2835b24..a41740be 100644 --- a/src/ios/dependencies/Branch-SDK/Branch.m +++ b/src/ios/dependencies/Branch-SDK/Branch.m @@ -1404,7 +1404,10 @@ void BNCPerformBlockOnMainThread(dispatch_block_t block) { dispatch_async(dispatch_get_main_queue(), block); } -- (void) processRequest:(BNCServerRequest*)req response:(BNCServerResponse*)response error:(NSError*)error { +- (void) processRequest:(BNCServerRequest*)req + response:(BNCServerResponse*)response + error:(NSError*)error { + // If the request was successful, or was a bad user request, continue processing. if (!error || error.code == BNCBadRequestError || error.code == BNCDuplicateResourceError) { @@ -1418,7 +1421,7 @@ - (void) processRequest:(BNCServerRequest*)req response:(BNCServerResponse*)resp else { // First, gather all the requests to fail NSMutableArray *requestsToFail = [[NSMutableArray alloc] init]; - for (int i = 0; i < self.requestQueue.size; i++) { + for (int i = 0; i < self.requestQueue.queueDepth; i++) { BNCServerRequest *request = [self.requestQueue peekAt:i]; if (request) { [requestsToFail addObject:request]; @@ -1448,7 +1451,10 @@ - (void) processRequest:(BNCServerRequest*)req response:(BNCServerResponse*)resp - (void)processNextQueueItem { dispatch_semaphore_wait(self.processing_sema, DISPATCH_TIME_FOREVER); - if (self.networkCount == 0 && self.requestQueue.size > 0 && !self.preferenceHelper.shouldWaitForInit) { + if (self.networkCount == 0 && + self.requestQueue.queueDepth > 0 && + !self.preferenceHelper.shouldWaitForInit) { + self.networkCount = 1; dispatch_semaphore_signal(self.processing_sema); BNCServerRequest *req = [self.requestQueue peek]; diff --git a/src/ios/dependencies/Branch-SDK/BranchActivityItemProvider.m b/src/ios/dependencies/Branch-SDK/BranchActivityItemProvider.m index 5b42e268..950827ac 100644 --- a/src/ios/dependencies/Branch-SDK/BranchActivityItemProvider.m +++ b/src/ios/dependencies/Branch-SDK/BranchActivityItemProvider.m @@ -8,6 +8,7 @@ #import "BranchActivityItemProvider.h" #import "Branch.h" +#import "BranchConstants.h" #import "BNCSystemObserver.h" #import "BNCDeviceInfo.h" @@ -57,19 +58,67 @@ - (id)item { NSString *stage = [self stageForChannel:channel]; NSString *campaign = [self campaignForChannel:channel]; NSString *alias = [self aliasForChannel:channel]; - + // Allow the channel param to be overridden, perhaps they want "fb" instead of "facebook" if ([self.delegate respondsToSelector:@selector(activityItemOverrideChannelForChannel:)]) { channel = [self.delegate activityItemOverrideChannelForChannel:channel]; } - // Because Facebook et al immediately scrape URLs, we add an additional parameter to the existing list, telling the backend to ignore the first click + // Because Facebook et al immediately scrape URLs, we add an additional parameter to the + // existing list, telling the backend to ignore the first click NSArray *scrapers = @[@"Facebook", @"Twitter", @"Slack", @"Apple Notes"]; for (NSString *scraper in scrapers) { if ([channel isEqualToString:scraper]) - return [NSURL URLWithString:[[Branch getInstance] getShortURLWithParams:params andTags:tags andChannel:channel andFeature:feature andStage:stage andCampaign:campaign andAlias:alias ignoreUAString:self.userAgentString forceLinkCreation:YES]]; + return [NSURL URLWithString:[[Branch getInstance] + getShortURLWithParams:params + andTags:tags + andChannel:channel + andFeature:feature + andStage:stage + andCampaign:campaign + andAlias:alias + ignoreUAString:self.userAgentString + forceLinkCreation:YES]]; } - return [NSURL URLWithString:[[Branch getInstance] getShortURLWithParams:params andTags:tags andChannel:channel andFeature:feature andStage:stage andCampaign:campaign andAlias:alias ignoreUAString:nil forceLinkCreation:YES]]; + + // Wrap the link in HTML content + if (self.activityType == UIActivityTypeMail && + [params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_HEADER] && + [params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_FOOTER]) { + NSURL *link = [NSURL URLWithString:[[Branch getInstance] + getShortURLWithParams:params + andTags:tags + andChannel:channel + andFeature:feature + andStage:stage + andCampaign:campaign + andAlias:alias + ignoreUAString:nil + forceLinkCreation:YES]]; + NSString *emailLink; + if ([params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_LINK_TEXT]) { + emailLink = [NSString stringWithFormat:@"%@", + link, [params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_LINK_TEXT]]; + } else { + emailLink = link.absoluteString; + } + + return [NSString stringWithFormat:@"%@%@%@", + [params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_HEADER], + emailLink, + [params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_FOOTER]]; + } + + return [NSURL URLWithString:[[Branch getInstance] + getShortURLWithParams:params + andTags:tags + andChannel:channel + andFeature:feature + andStage:stage + andCampaign:campaign + andAlias:alias + ignoreUAString:nil + forceLinkCreation:YES]]; } diff --git a/src/ios/dependencies/Branch-SDK/BranchConstants.h b/src/ios/dependencies/Branch-SDK/BranchConstants.h index 5ad1449c..6c4f08d8 100644 --- a/src/ios/dependencies/Branch-SDK/BranchConstants.h +++ b/src/ios/dependencies/Branch-SDK/BranchConstants.h @@ -104,6 +104,9 @@ extern NSString * const BRANCH_LINK_DATA_KEY_CANONICAL_URL; extern NSString * const BRANCH_LINK_DATA_KEY_CONTENT_EXPIRATION_DATE; extern NSString * const BRANCH_LINK_DATA_KEY_CONTENT_TYPE; extern NSString * const BRANCH_LINK_DATA_KEY_EMAIL_SUBJECT; +extern NSString * const BRANCH_LINK_DATA_KEY_EMAIL_HTML_HEADER; +extern NSString * const BRANCH_LINK_DATA_KEY_EMAIL_HTML_FOOTER; +extern NSString * const BRANCH_LINK_DATA_KEY_EMAIL_HTML_LINK_TEXT; extern NSString * const BRANCH_SPOTLIGHT_PREFIX; diff --git a/src/ios/dependencies/Branch-SDK/BranchConstants.m b/src/ios/dependencies/Branch-SDK/BranchConstants.m index ddacc389..cf45a21c 100644 --- a/src/ios/dependencies/Branch-SDK/BranchConstants.m +++ b/src/ios/dependencies/Branch-SDK/BranchConstants.m @@ -103,6 +103,9 @@ NSString * const BRANCH_LINK_DATA_KEY_CONTENT_EXPIRATION_DATE = @"$exp_date"; NSString * const BRANCH_LINK_DATA_KEY_CONTENT_TYPE = @"$content_type"; NSString * const BRANCH_LINK_DATA_KEY_EMAIL_SUBJECT = @"$email_subject"; +NSString * const BRANCH_LINK_DATA_KEY_EMAIL_HTML_HEADER = @"$email_html_header"; +NSString * const BRANCH_LINK_DATA_KEY_EMAIL_HTML_FOOTER = @"$email_html_footer"; +NSString * const BRANCH_LINK_DATA_KEY_EMAIL_HTML_LINK_TEXT = @"$email_html_link_text"; NSString * const BRANCH_SPOTLIGHT_PREFIX = @"io.branch.link.v1";