diff --git a/package.json b/package.json index 92ebaab5..3369e1fc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "branch-cordova-sdk", "description": "Branch Metrics Cordova SDK", "main": "src/branch.js", - "version": "2.6.12", + "version": "2.6.13", "homepage": "https://github.com/BranchMetrics/cordova-ionic-phonegap-branch-deep-linking", "repository": { "type": "git", diff --git a/plugin.template.xml b/plugin.template.xml index 4fe8ca21..4ddb2318 100644 --- a/plugin.template.xml +++ b/plugin.template.xml @@ -24,7 +24,7 @@ SOFTWARE. + version="2.6.13"> diff --git a/plugin.xml b/plugin.xml index 29f6a1f8..8daaf5b9 100644 --- a/plugin.xml +++ b/plugin.xml @@ -24,7 +24,7 @@ SOFTWARE. + version="2.6.13"> diff --git a/src/android/dependencies/Branch-2.11.1.jar b/src/android/dependencies/Branch-2.11.1.jar deleted file mode 100644 index 1836a609..00000000 Binary files a/src/android/dependencies/Branch-2.11.1.jar and /dev/null differ diff --git a/src/android/dependencies/Branch-2.12.1.jar b/src/android/dependencies/Branch-2.12.1.jar new file mode 100644 index 00000000..02eff838 Binary files /dev/null and b/src/android/dependencies/Branch-2.12.1.jar differ diff --git a/src/ios/dependencies/Branch-SDK/BNCConfig.m b/src/ios/dependencies/Branch-SDK/BNCConfig.m index eb5f5039..891d51d7 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.17.9"; +NSString * const BNC_SDK_VERSION = @"0.18.8"; diff --git a/src/ios/dependencies/Branch-SDK/BNCDeviceInfo.m b/src/ios/dependencies/Branch-SDK/BNCDeviceInfo.m index 358cf73d..66ccdf6c 100644 --- a/src/ios/dependencies/Branch-SDK/BNCDeviceInfo.m +++ b/src/ios/dependencies/Branch-SDK/BNCDeviceInfo.m @@ -65,11 +65,9 @@ - (id)init { return self; } -- (NSString *)vendorId -{ +- (NSString *)vendorId { @synchronized (self) { if (_vendorId) return _vendorId; - /* * https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor * BNCSystemObserver.getVendorId is based on UIDevice.identifierForVendor. Note from the @@ -179,59 +177,71 @@ + (NSString*) systemBuildVersion { return version; } - + (NSString*) userAgentString { - - static NSString* browserUserAgentString = nil; - void (^setBrowserUserAgent)(void) = ^() { - if (!browserUserAgentString) { - browserUserAgentString = - [[[UIWebView alloc] - initWithFrame:CGRectZero] - stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; - BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper]; - preferences.browserUserAgentString = browserUserAgentString; - preferences.lastSystemBuildVersion = self.systemBuildVersion; - BNCLogDebugSDK(@"userAgentString: '%@'.", browserUserAgentString); - } + + static NSString* brn_browserUserAgentString = nil; + + void (^setBrowserUserAgent)(void) = ^() { + @synchronized (self) { + if (!brn_browserUserAgentString) { + brn_browserUserAgentString = + [[[UIWebView alloc] + initWithFrame:CGRectZero] + stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; + BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper]; + preferences.browserUserAgentString = brn_browserUserAgentString; + preferences.lastSystemBuildVersion = self.systemBuildVersion; + BNCLogDebugSDK(@"userAgentString: '%@'.", brn_browserUserAgentString); + } + } }; - // We only get the string once per app run: + NSString* (^browserUserAgent)(void) = ^ NSString* () { + @synchronized (self) { + return brn_browserUserAgentString; + } + }; + + @synchronized (self) { + // We only get the string once per app run: - if (browserUserAgentString) - return browserUserAgentString; + if (brn_browserUserAgentString) + return brn_browserUserAgentString; - // Did we cache it? + // Did we cache it? - BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper]; - if (preferences.browserUserAgentString && - preferences.lastSystemBuildVersion && - [preferences.lastSystemBuildVersion isEqualToString:self.systemBuildVersion]) { - browserUserAgentString = [preferences.browserUserAgentString copy]; - return browserUserAgentString; - } + BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper]; + if (preferences.browserUserAgentString && + preferences.lastSystemBuildVersion && + [preferences.lastSystemBuildVersion isEqualToString:self.systemBuildVersion]) { + brn_browserUserAgentString = [preferences.browserUserAgentString copy]; + return brn_browserUserAgentString; + } + + // Make sure this executes on the main thread. + // Uses an implied lock through dispatch_queues: This can deadlock if mis-used! - // Make sure this executes on the main thread. - // Uses an implied lock through dispatch_queues: This can deadlock if mis-used! + if (NSThread.isMainThread) { + setBrowserUserAgent(); + return brn_browserUserAgentString; + } - if (NSThread.isMainThread) { - setBrowserUserAgent(); - return browserUserAgentString; - } + } // Different case for iOS 7.0: if ([UIDevice currentDevice].systemVersion.floatValue < 8.0) { + BNCLogDebugSDK(@"Getting iOS 7 UserAgent."); dispatch_sync(dispatch_get_main_queue(), ^ { setBrowserUserAgent(); }); - return browserUserAgentString; + BNCLogDebugSDK(@"Got iOS 7 UserAgent."); + return browserUserAgent(); } - // Wait and yield to prevent deadlock: - - int retries = 10; - int64_t timeoutDelta = (dispatch_time_t)((long double)NSEC_PER_SEC * (long double)0.100); - while (!browserUserAgentString && retries > 0) { + // Wait and yield to prevent deadlock: + int retries = 10; + int64_t timeoutDelta = (dispatch_time_t)((long double)NSEC_PER_SEC * (long double)0.100); + while (!browserUserAgent() && retries > 0) { dispatch_block_t agentBlock = dispatch_block_create_with_qos_class( DISPATCH_BLOCK_DETACHED | DISPATCH_BLOCK_ENFORCE_QOS_CLASS, @@ -243,11 +253,13 @@ + (NSString*) userAgentString { }); dispatch_async(dispatch_get_main_queue(), agentBlock); - dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutDelta); + dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutDelta); dispatch_block_wait(agentBlock, timeoutTime); - retries--; - } - return browserUserAgentString; + retries--; + } + BNCLogDebugSDK(@"Retries: %d", 10-retries); + + return browserUserAgent(); } @end diff --git a/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.h b/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.h index 02a2002d..b50f0eef 100644 --- a/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.h +++ b/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.h @@ -8,6 +8,20 @@ #import +#pragma mark BNCKeyValue + +@interface BNCKeyValue : NSObject + ++ (BNCKeyValue*) key:(NSString*)key value:(NSString*)value; +- (NSString*) description; + +@property (nonatomic, strong) NSString* key; +@property (nonatomic, strong) NSString* value; + +@end + +#pragma mark - BNCEncodingUtils + @interface BNCEncodingUtils : NSObject + (NSString *)base64EncodeStringToString:(NSString *)strData; @@ -22,6 +36,8 @@ + (NSString *)encodeDictionaryToJsonString:(NSDictionary *)dictionary; + (NSData *)encodeDictionaryToJsonData:(NSDictionary *)dictionary; ++ (NSString*) stringByPercentDecodingString:(NSString*)string; + + (NSDictionary *)decodeJsonDataToDictionary:(NSData *)jsonData; + (NSDictionary *)decodeJsonStringToDictionary:(NSString *)jsonString; + (NSDictionary *)decodeQueryStringToDictionary:(NSString *)queryString; @@ -29,4 +45,7 @@ + (NSString *) hexStringFromData:(NSData*)data; + (NSData *) dataFromHexString:(NSString*)string; + ++ (NSArray*) queryItems:(NSURL*)URL; + @end diff --git a/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.m b/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.m index 7f839a88..f301972b 100644 --- a/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.m +++ b/src/ios/dependencies/Branch-SDK/BNCEncodingUtils.m @@ -11,6 +11,33 @@ #import "BNCLog.h" #import +#pragma mark BNCKeyValue + +@implementation BNCKeyValue + ++ (BNCKeyValue*) key:(NSString*)key value:(NSString*)value { + BNCKeyValue *kv = [[BNCKeyValue alloc] init]; + kv.key = key; + kv.value = value; + return kv; +} + +- (NSString*) description { + return [NSString stringWithFormat:@"<%@, %@>", self.key, self.value]; +} + +- (BOOL) isEqual:(BNCKeyValue*)object { + return + [object isKindOfClass:[BNCKeyValue class]] && + [self.key isEqualToString:object.key] && + [self.value isEqualToString:object.value] + ; +} + +@end + +#pragma mark - BNCEncodingUtils + @implementation BNCEncodingUtils #pragma mark - Base 64 Encoding @@ -113,8 +140,14 @@ + (NSString *)encodeDictionaryToJsonString:(NSDictionary *)dictionary { string = NO; } else if ([obj isKindOfClass:[NSNumber class]]) { - value = [obj stringValue]; string = NO; + if (obj == (id)kCFBooleanFalse) + value = @"false"; + else + if (obj == (id)kCFBooleanTrue) + value = @"true"; + else + value = [obj stringValue]; } else if ([obj isKindOfClass:[NSNull class]]) { value = @"null"; @@ -251,7 +284,12 @@ + (NSString *)encodeDictionaryToQueryString:(NSDictionary *)dictionary { return queryString; } -#pragma mark - Param Decoding methods ++ (NSString*) stringByPercentDecodingString:(NSString *)string { + return [string stringByRemovingPercentEncoding]; +} + +#pragma mark - Param Decoding Methods + + (NSDictionary *)decodeJsonDataToDictionary:(NSData *)jsonData { NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; @@ -393,4 +431,41 @@ + (NSData *) dataFromHexString:(NSString*)string { return data; } +#pragma mark - URL QueryItems + ++ (NSArray*) queryItems:(NSURL*)URL { + NSMutableArray* keyValues = [NSMutableArray new]; + if (!URL) return keyValues; + + NSArray *queryItems = [[URL query] componentsSeparatedByString:@"&"]; + for (NSString* itemPair in queryItems) { + + BNCKeyValue *keyValue = [BNCKeyValue new]; + NSRange range = [itemPair rangeOfString:@"="]; + if (range.location == NSNotFound) { + if (itemPair.length) + keyValue.key = itemPair; + } else { + keyValue.key = [itemPair substringWithRange:NSMakeRange(0, range.location)]; + NSRange r = NSMakeRange(range.location+1, itemPair.length-range.location-1); + if (r.length > 0) + keyValue.value = [itemPair substringWithRange:r]; + } + + keyValue.key = [BNCEncodingUtils stringByPercentDecodingString:keyValue.key]; + keyValue.key = [keyValue.key stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + keyValue.value = [BNCEncodingUtils stringByPercentDecodingString:keyValue.value]; + keyValue.value = [keyValue.value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + if (keyValue.key.length || keyValue.value.length) { + if (keyValue.key == nil) keyValue.key = @""; + if (keyValue.value == nil) keyValue.value = @""; + [keyValues addObject:keyValue]; + } + } + + return keyValues; +} + @end diff --git a/src/ios/dependencies/Branch-SDK/BNCLog.h b/src/ios/dependencies/Branch-SDK/BNCLog.h index 25f2b6b1..b69a633c 100644 --- a/src/ios/dependencies/Branch-SDK/BNCLog.h +++ b/src/ios/dependencies/Branch-SDK/BNCLog.h @@ -65,7 +65,7 @@ extern NSString *_Nonnull const BNCLogStringFromLogLevel(BNCLogLevel level); * @param string A string indicating the log level. * @return Returns The log level corresponding to the string. */ -extern BNCLogLevel BNBLogLevelFromString(NSString*_Null_unspecified string); +extern BNCLogLevel BNCLogLevelFromString(NSString*_Null_unspecified string); #pragma mark - Programmatic Breakpoints @@ -78,6 +78,16 @@ extern BOOL BNCLogBreakPointsAreEnabled(void); extern void BNCLogSetBreakPointsEnabled(BOOL enabled); +#pragma mark - Client Initialization Function + + +typedef void (*BNCLogClientInitializeFunctionPtr)(void); + +///@param clientInitializationFunction The client function that should be called before logging starts. +extern BNCLogClientInitializeFunctionPtr _Null_unspecified + BNCLogSetClientInitializeFunction(BNCLogClientInitializeFunctionPtr _Nullable clientInitializationFunction); + + #pragma mark - Optional Log Output Handlers diff --git a/src/ios/dependencies/Branch-SDK/BNCLog.m b/src/ios/dependencies/Branch-SDK/BNCLog.m index d036d78c..4b4de8f6 100644 --- a/src/ios/dependencies/Branch-SDK/BNCLog.m +++ b/src/ios/dependencies/Branch-SDK/BNCLog.m @@ -46,6 +46,13 @@ void BNCLogInternalErrorFunction(int linenumber, NSString*format, ...) { BNCLogInternalErrorFunction(__LINE__, __VA_ARGS__) +inline static void BNCLogInitializeClient_Internal() { + BNCLogClientInitializeFunctionPtr initFunction = BNCLogSetClientInitializeFunction(NULL); + if (initFunction) { + initFunction(); + } +} + #pragma mark - Default Output Functions static int bnc_LogDescriptor = -1; @@ -477,6 +484,7 @@ BNCLogLevel BNCLogDisplayLevel() { } void BNCLogSetDisplayLevel(BNCLogLevel level) { + BNCLogInitializeClient_Internal(); dispatch_async(bnc_LogQueue, ^{ bnc_LogDisplayLevel = level; }); @@ -499,7 +507,7 @@ void BNCLogSetDisplayLevel(BNCLogLevel level) { return bnc_logLevelStrings[level]; } -BNCLogLevel BNBLogLevelFromString(NSString*string) { +BNCLogLevel BNCLogLevelFromString(NSString*string) { if (!string) return BNCLogLevelNone; for (NSInteger i = 0; i < _countof(bnc_logLevelStrings); ++i) { if ([bnc_logLevelStrings[i] isEqualToString:string]) { @@ -512,6 +520,19 @@ BNCLogLevel BNBLogLevelFromString(NSString*string) { return BNCLogLevelNone; } +#pragma mark - Client Initialization Function + +#include "stdatomic.h" +static _Atomic(BNCLogClientInitializeFunctionPtr) bnc_LogClientInitializeFunctionPtr = (BNCLogClientInitializeFunctionPtr) 0; + +extern BNCLogClientInitializeFunctionPtr _Null_unspecified BNCLogSetClientInitializeFunction( + BNCLogClientInitializeFunctionPtr _Nullable clientInitializationFunction + ) { + BNCLogClientInitializeFunctionPtr lastPtr = + atomic_exchange(&bnc_LogClientInitializeFunctionPtr, clientInitializationFunction); + return lastPtr; +} + #pragma mark - Break Points static BOOL bnc_LogBreakPointsAreEnabled = NO; @@ -582,6 +603,7 @@ void BNCLogWriteMessageFormat( NSString *_Nullable message, ... ) { + BNCLogInitializeClient_Internal(); if (!file) file = ""; if (!message) message = @""; if (![message isKindOfClass:[NSString class]]) { diff --git a/src/ios/dependencies/Branch-SDK/BNCPreferenceHelper.m b/src/ios/dependencies/Branch-SDK/BNCPreferenceHelper.m index 8a1d81e4..6909f238 100644 --- a/src/ios/dependencies/Branch-SDK/BNCPreferenceHelper.m +++ b/src/ios/dependencies/Branch-SDK/BNCPreferenceHelper.m @@ -805,7 +805,7 @@ + (void) initialize { for (NSURL *URL in URLs) { NSError *error = nil; - NSURL *branchURL = [URL URLByAppendingPathComponent:@"io.branch" isDirectory:YES]; + NSURL *branchURL = [[NSURL alloc] initWithString:@"io.branch" relativeToURL:URL]; BOOL success = [fileManager createDirectoryAtURL:branchURL diff --git a/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m b/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m index b35f620d..31dbaa4a 100644 --- a/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m +++ b/src/ios/dependencies/Branch-SDK/BNCStrongMatchHelper.m @@ -133,7 +133,7 @@ + (BNCStrongMatchHelper *)strongMatchHelper { + (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey redirectUrl:(NSString *)redirectUrl { - if (!branchKey) { + if (!branchKey || !self.cookiesAvailableInOS) { return nil; } @@ -192,7 +192,14 @@ + (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey #pragma clang diagnostic pop } ++ (BOOL)cookiesAvailableInOS +{ + return [UIDevice currentDevice].systemVersion.floatValue < 11.0; +} + - (void)createStrongMatchWithBranchKey:(NSString *)branchKey { + if (!self.class.cookiesAvailableInOS) return; + @synchronized (self) { if (self.requestInProgress) return; diff --git a/src/ios/dependencies/Branch-SDK/Branch.m b/src/ios/dependencies/Branch-SDK/Branch.m index 50095fb4..f7607edf 100644 --- a/src/ios/dependencies/Branch-SDK/Branch.m +++ b/src/ios/dependencies/Branch-SDK/Branch.m @@ -69,6 +69,7 @@ NSString * const BNCShareInitiatedEvent = @"Share Started"; NSString * const BNCShareCompletedEvent = @"Share Completed"; +NSString * const BNCLogLevelKey = @"io.branch.sdk.BNCLogLevel"; #pragma mark - Load Categories @@ -117,28 +118,35 @@ @implementation Branch #pragma mark - GetInstance methods -+ (void) load { - if (self != [Branch self]) - return; - ForceCategoriesToLoad(); - [self openLog]; -} - static NSURL* bnc_logURL = nil; + (void) openLog { // Initialize the log @synchronized (self) { - if (!bnc_logURL) { + if (bnc_logURL) { + BNCLogSetOutputToURLByteWrap(bnc_logURL, 102400); + } else { BNCLogInitialize(); - BNCLogSetDisplayLevel(BNCLogLevelAll); + BNCLogSetDisplayLevel(BNCLogLevelAll); bnc_logURL = BNCURLForBranchDirectory(); - bnc_logURL = [bnc_logURL URLByAppendingPathComponent:@"Branch.log"]; + bnc_logURL = [[NSURL alloc] initWithString:@"Branch.log" relativeToURL:bnc_logURL]; BNCLogSetOutputToURLByteWrap(bnc_logURL, 102400); - BNCLogSetDisplayLevel(BNCLogLevelWarning); + BNCLogSetDisplayLevel(BNCLogLevelWarning); // Default + + // Try loading from the Info.plist + NSString *logLevelString = [[NSBundle mainBundle] infoDictionary][@"BranchLogLevel"]; + if ([logLevelString isKindOfClass:[NSString class]]) { + BNCLogLevel logLevel = BNCLogLevelFromString(logLevelString); + BNCLogSetDisplayLevel(logLevel); + } + + // Try loading from user defaults + NSNumber *logLevel = [[NSUserDefaults standardUserDefaults] objectForKey:BNCLogLevelKey]; + if ([logLevel isKindOfClass:[NSNumber class]]) { + BNCLogSetDisplayLevel([logLevel integerValue]); + } + BNCLogDebug(@"Branch version %@ started at %@.", BNC_SDK_VERSION, [NSDate date]); - } else { - BNCLogSetOutputToURLByteWrap(bnc_logURL, 102400); } } } @@ -147,6 +155,18 @@ + (void) closeLog { BNCLogCloseLogFile(); } +void BranchClassInitializeLog(void); +void BranchClassInitializeLog(void) { + [Branch openLog]; +} + ++ (void) load { + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + BNCLogSetClientInitializeFunction(BranchClassInitializeLog); + }); +} + + (Branch *) getTestInstance { Branch.useTestBranchKey = YES; return Branch.getInstance; @@ -544,7 +564,9 @@ - (void)initSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL) // Note that this is no longer a recommended path, and that we tell developers to just return YES if (self.accountForFacebookSDK) { // does not work in Swift, because Objective-C to Swift interop is bad - id activity = [[options objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey] objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"]; + id activity = + [[options objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey] + objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"]; if (activity && [activity isKindOfClass:[NSUserActivity class]]) { [self continueUserActivity:activity]; return; @@ -667,12 +689,12 @@ - (BOOL)handleUniversalDeepLink:(NSURL*)url fromSelf:(BOOL)isFromSelf { id branchUniversalLinkDomains = [self.preferenceHelper getBranchUniversalLinkDomains]; if ([branchUniversalLinkDomains isKindOfClass:[NSString class]] && - [urlString containsString:branchUniversalLinkDomains]) { + [urlString bnc_containsString:branchUniversalLinkDomains]) { return YES; } else if ([branchUniversalLinkDomains isKindOfClass:[NSArray class]]) { for (id oneDomain in branchUniversalLinkDomains) { - if ([oneDomain isKindOfClass:[NSString class]] && [urlString containsString:oneDomain]) { + if ([oneDomain isKindOfClass:[NSString class]] && [urlString bnc_containsString:oneDomain]) { return YES; } } @@ -681,7 +703,7 @@ - (BOOL)handleUniversalDeepLink:(NSURL*)url fromSelf:(BOOL)isFromSelf { NSString *userActivityURL = urlString; NSArray *branchDomains = [NSArray arrayWithObjects:@"bnc.lt", @"app.link", @"test-app.link", nil]; for (NSString* domain in branchDomains) { - if ([userActivityURL containsString:domain]) + if ([userActivityURL bnc_containsString:domain]) return YES; } @@ -689,9 +711,18 @@ - (BOOL)handleUniversalDeepLink:(NSURL*)url fromSelf:(BOOL)isFromSelf { } - (BOOL)continueUserActivity:(NSUserActivity *)userActivity { - //check to see if a browser activity needs to be handled + BNCLogDebugSDK(@"continueUserActivity:"); + + // Check to see if a browser activity needs to be handled if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { - return [self handleUniversalDeepLink:userActivity.webpageURL fromSelf:NO]; + + // If we're already in-progress cancel the last open and do this one. + BOOL isNewSession = NO; + if (![self removeInstallOrOpen]) { + isNewSession = YES; + } + + return [self handleUniversalDeepLink:userActivity.webpageURL fromSelf:isNewSession]; } // Check to see if a spotlight activity needs to be handled @@ -717,25 +748,12 @@ - (BOOL)continueUserActivity:(NSUserActivity *)userActivity { #pragma mark - Push Notification support -// handle push notification if app is already launched - (void)handlePushNotification:(NSDictionary *)userInfo { - Class UIApplicationClass = NSClassFromString(@"UIApplication"); - - // If app is active, then close out the session and start a new one - if ([[UIApplicationClass sharedApplication] applicationState] == UIApplicationStateActive) { - [self callClose]; - } - // look for a branch shortlink in the payload (shortlink because iOS7 only supports 256 bytes) NSString *urlStr = [userInfo objectForKey:BRANCH_PUSH_NOTIFICATION_PAYLOAD_KEY]; if (urlStr) { - // reusing this field, so as not to create yet another url slot on prefshelper - self.preferenceHelper.universalLinkUrl = urlStr; - } - - // Again, if app is active, then close out the session and start a new one - if ([[UIApplicationClass sharedApplication] applicationState] == UIApplicationStateActive) { - [self applicationDidBecomeActive]; + NSURL *url = [NSURL URLWithString:urlStr]; + if (url) [self handleDeepLink:url fromSelf:YES]; } } @@ -783,33 +801,35 @@ - (BOOL)checkAppleSearchAdsAttribution { self.preferenceHelper.checkedAppleSearchAdAttribution = YES; self.asyncRequestCount++; - void (^__nullable completionBlock)(NSDictionary *attrDetails, NSError *error) = ^void(NSDictionary *__nullable attrDetails, NSError *__nullable error) { + void (^__nullable completionBlock)(NSDictionary *attrDetails, NSError *error) = + ^ void(NSDictionary *__nullable attrDetails, NSError *__nullable error) { self.asyncRequestCount--; - if (attrDetails && [attrDetails count]) { + if (attrDetails.count) { self.preferenceHelper.appleSearchAdDetails = attrDetails; } else if (self.searchAdsDebugMode) { - NSMutableDictionary *testInfo = [[NSMutableDictionary alloc] init]; - - NSMutableDictionary *testDetails = [[NSMutableDictionary alloc] init]; - [testDetails setObject:[NSNumber numberWithBool:YES] forKey:@"iad-attribution"]; - [testDetails setObject:[NSNumber numberWithInteger:1234567890] forKey:@"iad-campaign-id"]; - [testDetails setObject:@"DebugAppleSearchAdsCampaignName" forKey:@"iad-campaign-name"]; - [testDetails setObject:@"2016-09-09T01:33:17Z" forKey:@"iad-click-date"]; - [testDetails setObject:@"2016-09-09T01:33:17Z" forKey:@"iad-conversion-date"]; - [testDetails setObject:[NSNumber numberWithInteger:1234567890] forKey:@"iad-creative-id"]; - [testDetails setObject:@"CreativeName" forKey:@"iad-creative-name"]; - [testDetails setObject:[NSNumber numberWithInteger:1234567890] forKey:@"iad-lineitem-id"]; - [testDetails setObject:@"LineName" forKey:@"iad-lineitem-name"]; - [testDetails setObject:@"OrgName" forKey:@"iad-org-name"]; - - [testInfo setObject:testDetails forKey:@"Version3.1"]; - - self.preferenceHelper.appleSearchAdDetails = testInfo; - } + NSDictionary *debugSearchAd = @{ + @"Version3.1": @{ + @"iad-adgroup-id": @1234567890, + @"iad-adgroup-name": @"AdGroupName", + @"iad-attribution": (id)kCFBooleanTrue, + @"iad-campaign-id": @1234567890, + @"iad-campaign-name": @"CampaignName", + @"iad-click-date": [NSDate date], + @"iad-conversion-date": [NSDate date], + @"iad-creative-id": @1234567890, + @"iad-creative-name": @"CreativeName", + @"iad-keyword": @"Keyword", + @"iad-lineitem-id": @1234567890, + @"iad-lineitem-name": @"LineName", + @"iad-org-name": @"OrgName" + } + }; + self.preferenceHelper.appleSearchAdDetails = debugSearchAd; + } // if there's another async attribution check in flight, don't continue with init if (self.asyncRequestCount > 0) { return; } @@ -1597,7 +1617,6 @@ - (void)applicationWillResignActive { [self callClose]; [self.requestQueue persistImmediately]; [BranchOpenRequest setWaitNeededForOpenResponseLock]; - NSLog(@"Resigned active."); // TODO: Remove BNCLogDebugSDK(@"Application resigned active."); [self.class closeLog]; [self.class openLog]; @@ -1747,6 +1766,26 @@ - (void)initSessionIfNeededAndNotInProgress { - (void)initUserSessionAndCallCallback:(BOOL)callCallback { self.shouldCallSessionInitCallback = callCallback; + NSString *urlstring = nil; + if (self.preferenceHelper.universalLinkUrl.length) + urlstring = self.preferenceHelper.universalLinkUrl; + else + if (self.preferenceHelper.externalIntentURI.length) + urlstring = self.preferenceHelper.externalIntentURI; + + if (urlstring.length) { + NSArray *queryItems = [BNCEncodingUtils queryItems:[NSURL URLWithString:urlstring]]; + for (BNCKeyValue*item in queryItems) { + if ([item.key isEqualToString:@"BranchLogLevel"]) { + BNCLogLevel logLevel = BNCLogLevelFromString(item.value); + [[NSUserDefaults standardUserDefaults] + setObject:[NSNumber numberWithInteger:logLevel] + forKey:BNCLogLevelKey]; + BNCLogSetDisplayLevel(logLevel); + NSLog(@"[io.branch.sdk] BNCLogLevel set to %ld.", (long) logLevel); + } + } + } // If the session is not yet initialized if (!self.isInitialized) { [self initializeSession]; @@ -1787,8 +1826,7 @@ - (void)initializeSession { } @synchronized (self) { - if ([self.requestQueue removeInstallOrOpen]) - self.networkCount = 0; + [self removeInstallOrOpen]; [BranchOpenRequest setWaitNeededForOpenResponseLock]; BranchOpenRequest *req = [[clazz alloc] initWithCallback:initSessionCallback]; [self insertRequestAtFront:req]; @@ -1796,6 +1834,16 @@ - (void)initializeSession { } } +- (BOOL) removeInstallOrOpen { + @synchronized (self) { + if ([self.requestQueue removeInstallOrOpen]) { + self.networkCount = 0; + return YES; + } + return NO; + } +} + - (void)handleInitSuccess { self.isInitialized = YES; @@ -1968,7 +2016,6 @@ - (void)deepLinkingControllerCompleted { } - (void)deepLinkingControllerCompletedFrom:(UIViewController *)viewController { - [self.deepLinkControllers enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { if([obj isKindOfClass:[BNCDeepLinkViewControllerInstance class]]) { @@ -1987,7 +2034,7 @@ - (void)deepLinkingControllerCompletedFrom:(UIViewController *)viewController { } } - }else { + } else { //Support for old API if ((UIViewController*)obj == viewController) [self.deepLinkPresentingController dismissViewControllerAnimated:YES completion:nil]; @@ -2008,19 +2055,19 @@ + (NSString *)kitDisplayVersion { #pragma mark - Crashlytics reporting enhancements -+ (void)logLowMemoryToCrashlytics -{ - [NSNotificationCenter.defaultCenter addObserverForName:UIApplicationDidReceiveMemoryWarningNotification - object:nil - queue:NSOperationQueue.mainQueue - usingBlock:^(NSNotification *notification) { - BNCCrashlyticsWrapper *crashlytics = [BNCCrashlyticsWrapper wrapper]; - [crashlytics setBoolValue:YES forKey:BRANCH_CRASHLYTICS_LOW_MEMORY_KEY]; - }]; ++ (void)logLowMemoryToCrashlytics { + [NSNotificationCenter.defaultCenter + addObserverForName:UIApplicationDidReceiveMemoryWarningNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *notification) { + BNCCrashlyticsWrapper *crashlytics = [BNCCrashlyticsWrapper wrapper]; + [crashlytics setBoolValue:YES forKey:BRANCH_CRASHLYTICS_LOW_MEMORY_KEY]; + } + ]; } -+ (void)addBranchSDKVersionToCrashlyticsReport -{ ++ (void)addBranchSDKVersionToCrashlyticsReport { BNCCrashlyticsWrapper *crashlytics = [BNCCrashlyticsWrapper wrapper]; [crashlytics setObjectValue:BNC_SDK_VERSION forKey:BRANCH_CRASHLYTICS_SDK_VERSION_KEY]; } diff --git a/src/ios/dependencies/Branch-SDK/BranchShareLink.m b/src/ios/dependencies/Branch-SDK/BranchShareLink.m index 35c25bb7..a83c8d44 100644 --- a/src/ios/dependencies/Branch-SDK/BranchShareLink.m +++ b/src/ios/dependencies/Branch-SDK/BranchShareLink.m @@ -127,8 +127,8 @@ - (void) shareDidComplete:(BOOL)completed activityError:(NSError*)error { andFeature:self.linkProperties.feature andStage:self.linkProperties.stage andAlias:self.linkProperties.alias]; - NSURL *URL = [[NSURL alloc] initWithString:URLString]; - item = [[BranchShareActivityItem alloc] initWithPlaceholderItem:URL]; + self.shareURL = [[NSURL alloc] initWithString:URLString]; + item = [[BranchShareActivityItem alloc] initWithPlaceholderItem:self.shareURL]; item.itemType = BranchShareActivityItemTypeBranchURL; item.parent = self; [items addObject:item]; @@ -257,7 +257,8 @@ - (id) shareObjectForItem:(BranchShareActivityItem*)activityItem andAlias:self.linkProperties.alias ignoreUAString:userAgentString forceLinkCreation:YES]; - return [NSURL URLWithString:URLString]; + self.shareURL = [NSURL URLWithString:URLString]; + return self.shareURL; } @end diff --git a/src/ios/dependencies/Branch-SDK/NSString+Branch.h b/src/ios/dependencies/Branch-SDK/NSString+Branch.h index a283e47c..764fb31f 100644 --- a/src/ios/dependencies/Branch-SDK/NSString+Branch.h +++ b/src/ios/dependencies/Branch-SDK/NSString+Branch.h @@ -27,6 +27,11 @@ ///@return Returns a string that is truncated at the first null character. - (NSString*_Nonnull) bnc_stringTruncatedAtNull; +///@discusion The `containsString:` method isn't supported pre-iOS 8. Here we roll our own. +// +///@param string The string to for comparison. +///@return Reurns true if the instance contains the string. +- (BOOL) bnc_containsString:(NSString*_Nullable)string; @end void BNCForceNSStringCategoryToLoad(void) __attribute__((constructor)); diff --git a/src/ios/dependencies/Branch-SDK/NSString+Branch.m b/src/ios/dependencies/Branch-SDK/NSString+Branch.m index 77331240..d7ecdabf 100644 --- a/src/ios/dependencies/Branch-SDK/NSString+Branch.m +++ b/src/ios/dependencies/Branch-SDK/NSString+Branch.m @@ -45,4 +45,9 @@ - (NSString*_Nonnull) bnc_stringTruncatedAtNull { return [self substringWithRange:range]; } +- (BOOL) bnc_containsString:(NSString*_Nullable)string { + if (!string) return NO; + return ([self rangeOfString:string].location != NSNotFound); +} + @end diff --git a/src/ios/dependencies/Branch-SDK/Networking/BNCServerInterface.m b/src/ios/dependencies/Branch-SDK/Networking/BNCServerInterface.m index 9d4bf66a..b9710bc9 100644 --- a/src/ios/dependencies/Branch-SDK/Networking/BNCServerInterface.m +++ b/src/ios/dependencies/Branch-SDK/Networking/BNCServerInterface.m @@ -397,7 +397,7 @@ - (instancetype) init { NSData *data = [BNCEncodingUtils dataFromHexString:hexKey]; if (data) { SecKeyRef secKey = [self publicSecKeyFromPKCS12CertChainData:data]; - if (secKey) [array addObject:(__bridge id)secKey]; + if (secKey) [array addObject:(__bridge_transfer id)secKey]; } } return array; diff --git a/src/ios/dependencies/Branch-SDK/Networking/BNCServerRequestQueue.m b/src/ios/dependencies/Branch-SDK/Networking/BNCServerRequestQueue.m index f68b764e..84d7a50b 100755 --- a/src/ios/dependencies/Branch-SDK/Networking/BNCServerRequestQueue.m +++ b/src/ios/dependencies/Branch-SDK/Networking/BNCServerRequestQueue.m @@ -86,7 +86,7 @@ - (BNCServerRequest *)removeAt:(unsigned int)index { BNCLogError(@"Invalid queue operation: index out of bound!"); return nil; } - + request = [self.queue objectAtIndex:index]; [self.queue removeObjectAtIndex:index]; [self persistEventually]; @@ -111,10 +111,10 @@ - (BNCServerRequest *)peekAt:(unsigned int)index { BNCLogError(@"Invalid queue operation: index out of bound!"); return nil; } - + BNCServerRequest *request = nil; request = [self.queue objectAtIndex:index]; - + return request; } } @@ -126,7 +126,9 @@ - (NSInteger)queueDepth { } - (NSString *)description { - return [self.queue description]; + @synchronized(self) { + return [self.queue description]; + } } - (void)clearQueue { @@ -155,6 +157,7 @@ - (BOOL)removeInstallOrOpen { BranchOpenRequest *req = [self.queue objectAtIndex:i]; // Install extends open, so only need to check open. if ([req isKindOfClass:[BranchOpenRequest class]]) { + BNCLogDebugSDK(@"Removing open request."); req.callback = nil; [self remove:req]; return YES; @@ -173,7 +176,7 @@ - (BranchOpenRequest *)moveInstallOrOpenToFront:(NSInteger)networkCount { 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; @@ -184,19 +187,19 @@ - (BranchOpenRequest *)moveInstallOrOpenToFront:(NSInteger)networkCount { break; } } - + if (!openOrInstallRequest) { BNCLogError(@"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; } } @@ -230,17 +233,21 @@ - (void)persistEventually { BNCNanoSecondsFromTimeInterval(BATCH_WRITE_TIMEOUT), BNCNanoSecondsFromTimeInterval(BATCH_WRITE_TIMEOUT / 10.0) ); - dispatch_source_set_event_handler(self.persistTimer, ^ { [self persistImmediately]; }); + __weak __typeof(self) weakSelf = self; + dispatch_source_set_event_handler(self.persistTimer, ^ { + __strong __typeof(self) strongSelf = weakSelf; + if (strongSelf) { + [strongSelf persistImmediately]; + dispatch_source_cancel(strongSelf.persistTimer); + strongSelf.persistTimer = nil; + } + }); dispatch_resume(self.persistTimer); } } - (void)persistImmediately { @synchronized (self) { - if (self.persistTimer) { - dispatch_source_cancel(self.persistTimer); - self.persistTimer = nil; - } NSArray *requestsToPersist = [self.queue copy]; @try { NSMutableArray *encodedRequests = [[NSMutableArray alloc] init]; @@ -283,7 +290,7 @@ - (void)retrieve { @synchronized (self) { NSMutableArray *queue = [[NSMutableArray alloc] init]; NSArray *encodedRequests = nil; - + // Capture exception while loading the queue file @try { NSError *error = nil; @@ -318,13 +325,13 @@ - (void)retrieve { BNCLogWarning(@"An exception occurred while attempting to parse a queued request, discarding."); continue; } - + // Throw out invalid request types if (![request isKindOfClass:[BNCServerRequest class]]) { BNCLogWarning(@"Found an invalid request object, discarding. Object is: %@.", request); continue; } - + // Throw out persisted close requests if ([request isKindOfClass:[BranchCloseRequest class]]) { continue; @@ -332,7 +339,7 @@ - (void)retrieve { [queue addObject:request]; } - + self.queue = queue; } } @@ -360,9 +367,9 @@ + (NSURL* _Nonnull) URLForQueueFile { + (void) moveOldQueueFile { NSURL *oldURL = [NSURL fileURLWithPath:self.queueFile_deprecated]; NSURL *newURL = [self URLForQueueFile]; - + if (!oldURL || !newURL) { return; } - + NSError *error = nil; [[NSFileManager defaultManager] moveItemAtURL:oldURL @@ -391,7 +398,7 @@ + (void) initialize { + (id)getInstance { static BNCServerRequestQueue *sharedQueue = nil; static dispatch_once_t onceToken; - + dispatch_once(&onceToken, ^ { sharedQueue = [[BNCServerRequestQueue alloc] init]; [sharedQueue retrieve];