Skip to content

Commit 6b481a7

Browse files
author
Edward Smith
committed
Updated and tested the strong match helper in Xcode 8.
1 parent 56a2000 commit 6b481a7

File tree

2 files changed

+175
-62
lines changed

2 files changed

+175
-62
lines changed

Branch-SDK/Branch-SDK/BNCStrongMatchHelper.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
// Copyright © 2015 Branch Metrics. All rights reserved.
77
//
88

9+
910
#import <Foundation/Foundation.h>
1011
#import <UIKit/UIKit.h>
1112

13+
1214
@interface BNCStrongMatchHelper : NSObject
1315

1416
+ (BNCStrongMatchHelper *)strongMatchHelper;
1517
- (void)createStrongMatchWithBranchKey:(NSString *)branchKey;
1618
- (BOOL)shouldDelayInstallRequest;
17-
+ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey redirectUrl:(NSString *)redirectUrl;
19+
+ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey
20+
redirectUrl:(NSString *)redirectUrl;
1821

1922
@end

Branch-SDK/Branch-SDK/BNCStrongMatchHelper.m

Lines changed: 171 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,95 @@
66
// Copyright © 2015 Branch Metrics. All rights reserved.
77
//
88

9+
910
#import "BNCStrongMatchHelper.h"
11+
#import <objc/runtime.h>
1012
#import "BNCConfig.h"
1113
#import "BNCPreferenceHelper.h"
1214
#import "BNCSystemObserver.h"
1315
#import "BranchConstants.h"
1416

17+
18+
#pragma mark BNCStrongMatchHelper iOS 8.0
19+
20+
1521
// Stub the class for older Xcode versions, methods don't actually do anything.
1622
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
1723

1824
@implementation BNCStrongMatchHelper
1925

20-
+ (BNCStrongMatchHelper *)strongMatchHelper { return nil; }
21-
- (void)createStrongMatchWithBranchKey:(NSString *)branchKey { }
22-
- (BOOL)shouldDelayInstallRequest { return NO; }
23-
+ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey redirectUrl:(NSString *)redirectUrl { return nil; }
26+
+ (BNCStrongMatchHelper *)strongMatchHelper {
27+
return nil;
28+
}
29+
30+
- (void)createStrongMatchWithBranchKey:(NSString *)branchKey {
31+
}
32+
33+
- (BOOL)shouldDelayInstallRequest {
34+
return NO;
35+
}
36+
37+
+ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey
38+
redirectUrl:(NSString *)redirectUrl {
39+
return nil;
40+
}
2441

2542
@end
2643

27-
#else
2844

29-
NSInteger const ABOUT_30_DAYS_TIME_IN_SECONDS = 60 * 60 * 24 * 30;
45+
#else // ------------------------------------------------------------------------------ iOS >= 9.0
46+
47+
48+
#pragma mark - BNCMatchViewController
49+
50+
51+
#import <SafariServices/SafariServices.h>
52+
53+
54+
// This is a class interface that will be dynamically subclassed to the SFSafariViewController
55+
@interface BNCMatchViewController : UIViewController
56+
@end
57+
58+
59+
@implementation BNCMatchViewController
60+
61+
- (instancetype) initWithURL:(NSURL*)URL {
62+
self = [super init];
63+
return self;
64+
}
65+
66+
- (BOOL) canBecomeFirstResponder {
67+
//NSLog(@"Can be!!! Responder.");
68+
return NO;
69+
}
70+
71+
- (BOOL) becomeFirstResponder {
72+
//NSLog(@"First!! Responder.");
73+
return NO;
74+
}
75+
76+
- (UIResponder*) nextResponder {
77+
//NSLog(@"Next!! Responder.");
78+
return nil;
79+
}
80+
81+
- (void) setDelegate:(id)delegate {
82+
}
83+
84+
@end
85+
86+
87+
#pragma mark - BNCStrongMatchHelper iOS 9.0
3088

31-
@interface BNCStrongMatchHelper ()
3289

33-
@property (strong, nonatomic) UIWindow *secondWindow;
90+
@interface BNCStrongMatchHelper ()
3491
@property (assign, nonatomic) BOOL requestInProgress;
3592
@property (assign, nonatomic) BOOL shouldDelayInstallRequest;
36-
93+
@property (strong, nonatomic) UIWindow *primaryWindow;
94+
@property (strong, nonatomic) BNCMatchViewController *matchViewController;
3795
@end
3896

97+
3998
@implementation BNCStrongMatchHelper
4099

41100
+ (BNCStrongMatchHelper *)strongMatchHelper {
@@ -49,7 +108,8 @@ + (BNCStrongMatchHelper *)strongMatchHelper {
49108
return strongMatchHelper;
50109
}
51110

52-
+ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey redirectUrl:(NSString *)redirectUrl {
111+
+ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey
112+
redirectUrl:(NSString *)redirectUrl {
53113
if (!branchKey) {
54114
return nil;
55115
}
@@ -95,69 +155,119 @@ + (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey redir
95155
}
96156

97157
- (void)createStrongMatchWithBranchKey:(NSString *)branchKey {
98-
if (self.requestInProgress) {
99-
return;
100-
}
158+
@synchronized (self) {
159+
if (self.requestInProgress) return;
101160

102-
self.requestInProgress = YES;
103-
104-
NSDate *thirtyDaysAgo = [NSDate dateWithTimeIntervalSinceNow:-ABOUT_30_DAYS_TIME_IN_SECONDS];
105-
NSDate *lastCheck = [BNCPreferenceHelper preferenceHelper].lastStrongMatchDate;
106-
if ([lastCheck compare:thirtyDaysAgo] == NSOrderedDescending) {
107-
self.requestInProgress = NO;
108-
return;
109-
}
110-
111-
self.shouldDelayInstallRequest = YES;
112-
[self presentSafariVCWithBranchKey:branchKey];
113-
}
161+
NSInteger const ABOUT_30_DAYS_TIME_IN_SECONDS = 60 * 60 * 24 * 30;
162+
NSDate *thirtyDaysAgo = [NSDate dateWithTimeIntervalSinceNow:-ABOUT_30_DAYS_TIME_IN_SECONDS];
163+
NSDate *lastCheck = [BNCPreferenceHelper preferenceHelper].lastStrongMatchDate;
164+
if ([lastCheck compare:thirtyDaysAgo] == NSOrderedDescending) return;
165+
166+
NSURL *strongMatchUrl =
167+
[BNCStrongMatchHelper
168+
getUrlForCookieBasedMatchingWithBranchKey:branchKey
169+
redirectUrl:nil];
170+
if (!strongMatchUrl) return;
171+
172+
self.requestInProgress = YES;
173+
self.shouldDelayInstallRequest = YES;
114174

115-
- (void)presentSafariVCWithBranchKey:(NSString *)branchKey {
116-
NSURL *strongMatchUrl = [BNCStrongMatchHelper getUrlForCookieBasedMatchingWithBranchKey:branchKey redirectUrl:nil];
117-
if (!strongMatchUrl) {
118-
self.shouldDelayInstallRequest = NO;
119-
self.requestInProgress = NO;
120-
return;
121-
}
122-
123-
Class SFSafariViewControllerClass = NSClassFromString(@"SFSafariViewController");
124-
Class UIApplicationClass = NSClassFromString(@"UIApplication");
125-
if (SFSafariViewControllerClass) {
126-
127175
// Must be on next run loop to avoid a warning
128176
dispatch_async(dispatch_get_main_queue(), ^{
129-
UIViewController * safController = [[SFSafariViewControllerClass alloc] initWithURL:strongMatchUrl];
130-
self.secondWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
131-
self.secondWindow.rootViewController = safController;
132-
self.secondWindow.windowLevel = UIWindowLevelNormal - 100;
133-
[self.secondWindow setHidden:NO];
134-
UIWindow *keyWindow = [[UIApplicationClass sharedApplication] keyWindow];
135-
[self.secondWindow makeKeyWindow];
136-
137-
// Give enough time for Safari to load the request (optimized for 3G)
138-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
139-
[keyWindow makeKeyWindow];
140-
141-
// Remove the window and release it's strong reference. This is important to ensure that
142-
// applications using view controller based status bar appearance are restored.
143-
[self.secondWindow removeFromSuperview];
144-
self.secondWindow = nil;
145-
146-
[BNCPreferenceHelper preferenceHelper].lastStrongMatchDate = [NSDate date];
177+
178+
if (![self willLoadViewControllerWithURL:strongMatchUrl]) {
179+
self.shouldDelayInstallRequest = NO;
147180
self.requestInProgress = NO;
148-
});
181+
return;
182+
}
183+
184+
// Give enough time for Safari to load the request (optimized for 3G)
185+
dispatch_after(
186+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)),
187+
dispatch_get_main_queue(),
188+
^{ [self unloadViewController]; }
189+
);
149190
});
150191
}
151-
else {
152-
self.requestInProgress = NO;
192+
}
193+
194+
- (BOOL) subclass:(Class)subclass selector:(SEL)selector {
195+
Class templateClass = objc_getClass("BNCMatchViewController");
196+
if (!templateClass) return NO;
197+
198+
Method method = class_getInstanceMethod(templateClass, selector);
199+
if (!method) return NO;
200+
201+
const char * typeEncoding = method_getTypeEncoding(method);
202+
if (!typeEncoding) return NO;
203+
204+
IMP implementation = class_getMethodImplementation(templateClass, selector);
205+
if (!implementation) return NO;
206+
207+
class_addMethod(subclass, selector, implementation, typeEncoding);
208+
return YES;
209+
}
210+
211+
- (BOOL) willLoadViewControllerWithURL:(NSURL*)matchURL {
212+
if (self.primaryWindow) return NO;
213+
214+
// Dynamically subclass the SFSafariViewController if available.
215+
// This allows us to compile and link to an app that doesn't
216+
// include SafariServices, but is also able to compile and link
217+
// when it is.
218+
219+
Class SFSafariViewControllerClass = NSClassFromString(@"SFSafariViewController");
220+
if (!SFSafariViewControllerClass) return NO;
221+
222+
Class BNCMatchViewControllerSubclass = NSClassFromString(@"BNCMatchViewController_Safari");
223+
if (!BNCMatchViewControllerSubclass) {
224+
// The class isn't registered. Create it:
225+
226+
BNCMatchViewControllerSubclass =
227+
objc_allocateClassPair(SFSafariViewControllerClass, "BNCMatchViewController_Safari", 0);
228+
if (!BNCMatchViewControllerSubclass) return NO;
229+
230+
BOOL fail = NO;
231+
fail |= ![self subclass:BNCMatchViewControllerSubclass selector:@selector(becomeFirstResponder)];
232+
fail |= ![self subclass:BNCMatchViewControllerSubclass selector:@selector(canBecomeFirstResponder)];
233+
fail |= ![self subclass:BNCMatchViewControllerSubclass selector:@selector(nextResponder)];
234+
if (fail) return NO;
235+
236+
objc_registerClassPair(BNCMatchViewControllerSubclass);
153237
}
238+
239+
self.primaryWindow = [[UIApplication sharedApplication] keyWindow];
240+
self.matchViewController = [[BNCMatchViewControllerSubclass alloc] initWithURL:matchURL];
241+
self.matchViewController.delegate = self;
242+
self.matchViewController.view.frame = self.primaryWindow.bounds;
243+
244+
[self.primaryWindow.rootViewController addChildViewController:self.matchViewController];
245+
[self.primaryWindow insertSubview:self.matchViewController.view atIndex:0];
246+
[self.matchViewController didMoveToParentViewController:self.primaryWindow.rootViewController];
247+
248+
return YES;
154249
}
155250

156-
- (BOOL)shouldDelayInstallRequest {
157-
return _shouldDelayInstallRequest;
251+
- (void) unloadViewController {
252+
//NSLog(@"unloadViewController");
253+
[self.matchViewController willMoveToParentViewController:nil];
254+
[self.matchViewController.view removeFromSuperview];
255+
[self.matchViewController removeFromParentViewController];
256+
self.matchViewController.delegate = nil;
257+
self.matchViewController = nil;
258+
self.primaryWindow = nil;
259+
260+
[BNCPreferenceHelper preferenceHelper].lastStrongMatchDate = [NSDate date];
261+
self.shouldDelayInstallRequest = NO;
262+
self.requestInProgress = NO;
158263
}
159264

265+
- (void)safariViewController:(SFSafariViewController *)controller
266+
didCompleteInitialLoad:(BOOL)didLoadSuccessfully {
267+
//NSLog(@"Safari Did load.");
268+
[self unloadViewController];
269+
}
160270

161271
@end
162272

163-
#endif
273+
#endif // ------------------------------------------------------------------------------ iOS >= 9.0

0 commit comments

Comments
 (0)