Skip to content

Commit 08edbc8

Browse files
authored
Merge pull request #932 from BranchMetrics/INTENG-7505-uiwebview-deprecated
INTENG-7505 uiwebview deprecated
2 parents eb00d9d + 68e407a commit 08edbc8

19 files changed

+286
-453
lines changed

Branch-SDK-Tests/BNCDeviceInfo.Test.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#import "BNCConfig.h"
2020
#import "BNCPreferenceHelper.h"
2121
#import "BNCSystemObserver.h"
22+
#import "BNCUserAgentCollector.h"
2223

2324

2425
@interface BNCDeviceInfoTest : BNCTestCase

Branch-SDK-Tests/BNCTestCase.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#import "Branch.h"
1414
#import "BNCApplication+BNCTest.h"
1515

16+
#import "BNCDeviceInfo.h"
17+
#import "BNCUserAgentCollector.h"
18+
1619
@interface Branch (BNCTest)
1720
+ (void) clearAll;
1821
@end
@@ -45,6 +48,13 @@ @implementation BNCTestCase
4548
- (void)setUp {
4649
[super setUp];
4750
[self resetExpectations];
51+
52+
// user agent needs to be loaded since many tests assume it's synchronously lazy loaded
53+
__block XCTestExpectation *expectation = [self expectationWithDescription:@"setup"];
54+
[[BNCUserAgentCollector instance] loadUserAgentForSystemBuildVersion:[BNCDeviceInfo systemBuildVersion] withCompletion:^(NSString * _Nullable userAgent) {
55+
[expectation fulfill];
56+
}];
57+
[self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) { }];
4858
}
4959

5060
- (void)resetExpectations {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//
2+
// BNCUserAgentCollectorTests.m
3+
// Branch-SDK-Tests
4+
//
5+
// Created by Ernest Cho on 8/29/19.
6+
// Copyright © 2019 Branch, Inc. All rights reserved.
7+
//
8+
9+
#import <XCTest/XCTest.h>
10+
#import "BNCPreferenceHelper.h"
11+
#import "BNCUserAgentCollector.h"
12+
13+
// expose private methods for unit testing
14+
@interface BNCUserAgentCollector()
15+
16+
- (NSString *)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion;
17+
- (void)saveUserAgent:(NSString *)userAgent forSystemBuildVersion:(NSString *)systemBuildVersion;
18+
- (void)collectUserAgentWithCompletion:(void (^)(NSString * _Nullable userAgent))completion;
19+
20+
@end
21+
22+
@interface BNCUserAgentCollectorTests : XCTestCase
23+
24+
@end
25+
26+
@implementation BNCUserAgentCollectorTests
27+
28+
+ (void)setUp {
29+
[BNCUserAgentCollectorTests resetPersistentData];
30+
}
31+
32+
- (void)setUp {
33+
34+
}
35+
36+
- (void)tearDown {
37+
[BNCUserAgentCollectorTests resetPersistentData];
38+
}
39+
40+
+ (void)resetPersistentData {
41+
BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
42+
preferences.browserUserAgentString = nil;
43+
preferences.lastSystemBuildVersion = nil;
44+
}
45+
46+
- (void)testResetPersistentData {
47+
BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
48+
XCTAssertNil(preferences.browserUserAgentString);
49+
XCTAssertNil(preferences.lastSystemBuildVersion);
50+
}
51+
52+
- (void)testSaveAndLoadUserAgent {
53+
NSString *systemBuildVersion = @"test";
54+
NSString *userAgent = @"UserAgent";
55+
56+
BNCUserAgentCollector *collector = [BNCUserAgentCollector new];
57+
[collector saveUserAgent:userAgent forSystemBuildVersion:systemBuildVersion];
58+
NSString *expected = [collector loadUserAgentForSystemBuildVersion:systemBuildVersion];
59+
XCTAssertTrue([userAgent isEqualToString:expected]);
60+
}
61+
62+
- (void)testCollectUserAgent {
63+
XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"];
64+
65+
BNCUserAgentCollector *collector = [BNCUserAgentCollector new];
66+
[collector collectUserAgentWithCompletion:^(NSString * _Nullable userAgent) {
67+
XCTAssertNotNil(userAgent);
68+
XCTAssertTrue([userAgent containsString:@"AppleWebKit"]);
69+
[expectation fulfill];
70+
}];
71+
72+
[self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) {
73+
74+
}];
75+
}
76+
77+
- (void)testLoadUserAgent_EmptyDataStore {
78+
XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"];
79+
NSString *systemBuildVersion = @"test";
80+
81+
BNCUserAgentCollector *collector = [BNCUserAgentCollector new];
82+
[collector loadUserAgentForSystemBuildVersion:systemBuildVersion withCompletion:^(NSString * _Nullable userAgent) {
83+
XCTAssertNotNil(userAgent);
84+
XCTAssertTrue([userAgent containsString:@"AppleWebKit"]);
85+
[expectation fulfill];
86+
}];
87+
88+
[self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) {
89+
90+
}];
91+
}
92+
93+
- (void)testLoadUserAgent_FilledDataStore {
94+
XCTestExpectation *expectation = [self expectationWithDescription:@"expectation"];
95+
NSString *systemBuildVersion = @"test";
96+
NSString *savedUserAgent = @"UserAgent";
97+
98+
BNCUserAgentCollector *collector = [BNCUserAgentCollector new];
99+
[collector saveUserAgent:savedUserAgent forSystemBuildVersion:systemBuildVersion];
100+
[collector loadUserAgentForSystemBuildVersion:systemBuildVersion withCompletion:^(NSString * _Nullable userAgent) {
101+
XCTAssertNotNil(userAgent);
102+
XCTAssertTrue([userAgent isEqualToString:savedUserAgent]);
103+
XCTAssertFalse([userAgent containsString:@"AppleWebKit"]);
104+
[expectation fulfill];
105+
}];
106+
107+
[self waitForExpectationsWithTimeout:2.0 handler:^(NSError * _Nullable error) {
108+
109+
}];
110+
}
111+
112+
@end

Branch-SDK/Branch-SDK/BNCDeviceInfo.m

Lines changed: 3 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#import "BNCLog.h"
1414
#import "BNCConfig.h"
1515
#import "BNCPreferenceHelper.h"
16+
#import "BNCUserAgentCollector.h"
1617

1718
#if __has_feature(modules)
1819
@import UIKit;
@@ -335,89 +336,8 @@ + (NSString*) systemBuildVersion {
335336
return version;
336337
}
337338

338-
+ (NSString*) userAgentString {
339-
340-
static NSString* brn_browserUserAgentString = nil;
341-
342-
void (^setBrowserUserAgent)(void) = ^() {
343-
@synchronized (self) {
344-
if (!brn_browserUserAgentString) {
345-
brn_browserUserAgentString =
346-
[[[UIWebView alloc]
347-
initWithFrame:CGRectZero]
348-
stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
349-
BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
350-
preferences.browserUserAgentString = brn_browserUserAgentString;
351-
preferences.lastSystemBuildVersion = self.systemBuildVersion;
352-
BNCLogDebugSDK(@"userAgentString: '%@'.", brn_browserUserAgentString);
353-
}
354-
}
355-
};
356-
357-
NSString* (^browserUserAgent)(void) = ^ NSString* () {
358-
@synchronized (self) {
359-
return brn_browserUserAgentString;
360-
}
361-
};
362-
363-
@synchronized (self) {
364-
// We only get the string once per app run:
365-
366-
if (brn_browserUserAgentString)
367-
return brn_browserUserAgentString;
368-
369-
// Did we cache it?
370-
371-
BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
372-
if (preferences.browserUserAgentString &&
373-
preferences.lastSystemBuildVersion &&
374-
[preferences.lastSystemBuildVersion isEqualToString:self.systemBuildVersion]) {
375-
brn_browserUserAgentString = [preferences.browserUserAgentString copy];
376-
return brn_browserUserAgentString;
377-
}
378-
379-
// Make sure this executes on the main thread.
380-
// Uses an implied lock through dispatch_queues: This can deadlock if mis-used!
381-
382-
if (NSThread.isMainThread) {
383-
setBrowserUserAgent();
384-
return brn_browserUserAgentString;
385-
}
386-
387-
}
388-
389-
// Different case for iOS 7.0:
390-
if ([UIDevice currentDevice].systemVersion.doubleValue < 8.0) {
391-
BNCLogDebugSDK(@"Getting iOS 7 UserAgent.");
392-
dispatch_sync(dispatch_get_main_queue(), ^ {
393-
setBrowserUserAgent();
394-
});
395-
BNCLogDebugSDK(@"Got iOS 7 UserAgent.");
396-
return browserUserAgent();
397-
}
398-
399-
// Wait and yield to prevent deadlock:
400-
int retries = 10;
401-
int64_t timeoutDelta = (dispatch_time_t)((long double)NSEC_PER_SEC * (long double)0.100);
402-
while (!browserUserAgent() && retries > 0) {
403-
404-
dispatch_block_t agentBlock = dispatch_block_create_with_qos_class(
405-
DISPATCH_BLOCK_DETACHED | DISPATCH_BLOCK_ENFORCE_QOS_CLASS,
406-
QOS_CLASS_USER_INTERACTIVE,
407-
0, ^ {
408-
BNCLogDebugSDK(@"Will set userAgent.");
409-
setBrowserUserAgent();
410-
BNCLogDebugSDK(@"Did set userAgent.");
411-
});
412-
dispatch_async(dispatch_get_main_queue(), agentBlock);
413-
414-
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutDelta);
415-
dispatch_block_wait(agentBlock, timeoutTime);
416-
retries--;
417-
}
418-
BNCLogDebugSDK(@"Retries: %d", 10-retries);
419-
420-
return browserUserAgent();
339+
+ (NSString *)userAgentString {
340+
return [BNCUserAgentCollector instance].userAgent;
421341
}
422342

423343
- (NSDictionary*) v2dictionary {

Branch-SDK/Branch-SDK/BNCPreferenceHelper.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ NSURL* /* _Nonnull */ BNCURLForBranchDirectory(void);
7676
- (NSInteger)getCreditCount;
7777
- (NSInteger)getCreditCountForBucket:(NSString *)bucket;
7878

79-
- (void)updateBranchViewCount:(NSString *)branchViewID;
80-
- (NSInteger)getBranchViewCount:(NSString *)branchViewID;
81-
8279
- (void)setRequestMetadataKey:(NSString *)key value:(NSObject *)value;
8380
- (NSMutableDictionary *)requestMetadataDictionary;
8481

Branch-SDK/Branch-SDK/BNCPreferenceHelper.m

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -676,19 +676,6 @@ - (void)clearUserCredits {
676676

677677
#pragma mark - Count Storage
678678

679-
- (void)updateBranchViewCount:(NSString *)branchViewID {
680-
NSInteger currentCount = [self getBranchViewCount:branchViewID] + 1;
681-
[self writeObjectToDefaults:[BRANCH_PREFS_KEY_BRANCH_VIEW_USAGE_CNT stringByAppendingString:branchViewID] value:@(currentCount)];
682-
}
683-
684-
- (NSInteger)getBranchViewCount:(NSString *)branchViewID {
685-
NSInteger count = [self readIntegerFromDefaults:[BRANCH_PREFS_KEY_BRANCH_VIEW_USAGE_CNT stringByAppendingString:branchViewID]];
686-
if (count == NSNotFound){
687-
count = 0;
688-
}
689-
return count;
690-
}
691-
692679
- (void)saveBranchAnalyticsData:(NSDictionary *)analyticsData {
693680
if (_sessionID) {
694681
if (!_savedAnalyticsData) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// BNCUserAgentCollector.h
3+
// Branch
4+
//
5+
// Created by Ernest Cho on 8/29/19.
6+
// Copyright © 2019 Branch, Inc. All rights reserved.
7+
//
8+
9+
@import Foundation;
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@interface BNCUserAgentCollector : NSObject
14+
15+
+ (BNCUserAgentCollector *)instance;
16+
17+
@property (nonatomic, copy, readwrite) NSString *userAgent;
18+
19+
- (void)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion withCompletion:(void (^)(NSString * _Nullable userAgent))completion;
20+
21+
@end
22+
23+
NS_ASSUME_NONNULL_END
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// BNCUserAgentCollector.m
3+
// Branch
4+
//
5+
// Created by Ernest Cho on 8/29/19.
6+
// Copyright © 2019 Branch, Inc. All rights reserved.
7+
//
8+
9+
#import "BNCUserAgentCollector.h"
10+
#import "BNCPreferenceHelper.h"
11+
@import WebKit;
12+
13+
@interface BNCUserAgentCollector()
14+
// need to hold onto the webview until the async user agent fetch is done
15+
@property (nonatomic, strong, readwrite) WKWebView *webview;
16+
@end
17+
18+
@implementation BNCUserAgentCollector
19+
20+
+ (BNCUserAgentCollector *)instance {
21+
static BNCUserAgentCollector *collector;
22+
static dispatch_once_t onceToken;
23+
dispatch_once(&onceToken, ^{
24+
collector = [BNCUserAgentCollector new];
25+
});
26+
return collector;
27+
}
28+
29+
- (void)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion withCompletion:(void (^)(NSString *userAgent))completion {
30+
31+
NSString *savedUserAgent = [self loadUserAgentForSystemBuildVersion:systemBuildVersion];
32+
if (savedUserAgent) {
33+
self.userAgent = savedUserAgent;
34+
if (completion) {
35+
completion(savedUserAgent);
36+
}
37+
} else {
38+
[self collectUserAgentWithCompletion:^(NSString * _Nullable userAgent) {
39+
self.userAgent = userAgent;
40+
[self saveUserAgent:userAgent forSystemBuildVersion:systemBuildVersion];
41+
if (completion) {
42+
completion(userAgent);
43+
}
44+
}];
45+
}
46+
}
47+
48+
// load user agent from preferences
49+
- (NSString *)loadUserAgentForSystemBuildVersion:(NSString *)systemBuildVersion {
50+
51+
NSString *userAgent = nil;
52+
BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
53+
NSString *savedUserAgent = [preferences.browserUserAgentString copy];
54+
NSString *savedSystemBuildVersion = [preferences.lastSystemBuildVersion copy];
55+
56+
if (savedUserAgent && [systemBuildVersion isEqualToString:savedSystemBuildVersion]) {
57+
userAgent = savedUserAgent;
58+
}
59+
60+
return userAgent;
61+
}
62+
63+
// save user agent to preferences
64+
- (void)saveUserAgent:(NSString *)userAgent forSystemBuildVersion:(NSString *)systemBuildVersion {
65+
if (userAgent && systemBuildVersion) {
66+
BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
67+
preferences.browserUserAgentString = userAgent;
68+
preferences.lastSystemBuildVersion = systemBuildVersion;
69+
}
70+
}
71+
72+
// collect user agent from webkit. this is expensive.
73+
- (void)collectUserAgentWithCompletion:(void (^)(NSString *userAgent))completion {
74+
dispatch_async(dispatch_get_main_queue(), ^{
75+
self.webview = [[WKWebView alloc] initWithFrame:CGRectZero];
76+
[self.webview evaluateJavaScript:@"navigator.userAgent;" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
77+
if (completion) {
78+
completion(response);
79+
80+
// release the webview
81+
self.webview = nil;
82+
}
83+
}];
84+
});
85+
}
86+
87+
@end

0 commit comments

Comments
 (0)