-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
ExpoKit.m
211 lines (170 loc) · 7.83 KB
/
ExpoKit.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Copyright 2015-present 650 Industries. All rights reserved.
#import "ExpoKit.h"
#import "EXViewController.h"
#import "EXAnalytics.h"
#import "EXBuildConstants.h"
#import "EXEnvironment.h"
#import "EXGoogleAuthManager.h"
#import "EXKernel.h"
#import "EXKernelUtil.h"
#import "EXKernelLinkingManager.h"
#import "EXReactAppExceptionHandler.h"
#import "EXRemoteNotificationManager.h"
#import <Crashlytics/Crashlytics.h>
#import <GoogleMaps/GoogleMaps.h>
NSString * const EXAppDidRegisterForRemoteNotificationsNotification = @"kEXAppDidRegisterForRemoteNotificationsNotification";
NSString * const EXAppDidRegisterUserNotificationSettingsNotification = @"kEXAppDidRegisterUserNotificationSettingsNotification";
@interface ExpoKit () <CrashlyticsDelegate>
{
Class _rootViewControllerClass;
}
@property (nonatomic, nullable, strong) EXViewController *rootViewController;
@property (nonatomic, strong) NSDictionary *launchOptions;
@end
@implementation ExpoKit
+ (nonnull instancetype)sharedInstance
{
static ExpoKit *theExpoKit = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
if (!theExpoKit) {
theExpoKit = [[ExpoKit alloc] init];
}
});
return theExpoKit;
}
- (instancetype)init
{
if (self = [super init]) {
_rootViewControllerClass = [EXViewController class];
[self _initDefaultKeys];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)registerRootViewControllerClass:(Class)rootViewControllerClass
{
NSAssert([rootViewControllerClass isSubclassOfClass:[EXViewController class]], @"ExpoKit root view controller class must subclass EXViewController.");
_rootViewControllerClass = rootViewControllerClass;
}
- (EXViewController *)rootViewController
{
if (!_rootViewController) {
_rootViewController = [[_rootViewControllerClass alloc] init];
_rootViewController.delegate = [EXKernel sharedInstance];
}
return _rootViewController;
}
- (UIViewController *)currentViewController
{
EXViewController *rootViewController = [self rootViewController];
UIViewController *controller = [rootViewController contentViewController];
while (controller.presentedViewController != nil) {
controller = controller.presentedViewController;
}
return controller;
}
#pragma mark - misc AppDelegate hooks
- (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[DDLog addLogger:[DDOSLogger sharedInstance]];
RCTSetFatalHandler(handleFatalReactError);
// init analytics
[EXAnalytics sharedInstance];
NSString *standaloneGMSKey = [[NSBundle mainBundle].infoDictionary objectForKey:@"GMSApiKey"];
if (standaloneGMSKey && standaloneGMSKey.length) {
[GMSServices provideAPIKey:standaloneGMSKey];
} else {
if (_applicationKeys[@"GOOGLE_MAPS_IOS_API_KEY"]) {// we may define this as empty
if ([_applicationKeys[@"GOOGLE_MAPS_IOS_API_KEY"] length]) {
[GMSServices provideAPIKey:_applicationKeys[@"GOOGLE_MAPS_IOS_API_KEY"]];
}
}
}
[UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) [EXKernel sharedInstance].serviceRegistry.notificationsManager;
// This is safe to call; if the app doesn't have permission to display user-facing notifications
// then registering for a push token is a no-op
[[EXKernel sharedInstance].serviceRegistry.remoteNotificationManager registerForRemoteNotifications];
_launchOptions = launchOptions;
}
#pragma mark - Crash handling
- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report
{
// set a persistent flag because we may not get a chance to take any action until a future execution of the app.
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kEXKernelClearJSCacheUserDefaultsKey];
// block to ensure we save this key (in case the app crashes again)
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - APNS hooks
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
{
[[EXKernel sharedInstance].serviceRegistry.remoteNotificationManager registerAPNSToken:token registrationError:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:EXAppDidRegisterForRemoteNotificationsNotification object:nil userInfo:@{ @"token": token }];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
{
DDLogWarn(@"Failed to register for remote notifs: %@", err);
[[EXKernel sharedInstance].serviceRegistry.remoteNotificationManager registerAPNSToken:nil registrationError:err];
// Post this even in the failure case -- up to subscribers to subsequently read the system permission state
[[NSNotificationCenter defaultCenter] postNotificationName:EXAppDidRegisterForRemoteNotificationsNotification object:nil];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
// Here background task execution should go.
completionHandler(UIBackgroundFetchResultNoData);
}
// TODO: Remove once SDK31 is phased out
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings
{
[[NSNotificationCenter defaultCenter] postNotificationName:EXAppDidRegisterUserNotificationSettingsNotification object:nil];
}
#pragma clang diagnostic pop
#pragma mark - deep linking hooks
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation
{
if ([[EXKernel sharedInstance].serviceRegistry.googleAuthManager
application:application openURL:url sourceApplication:sourceApplication annotation:annotation]) {
return YES;
}
return [EXKernelLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler
{
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *webpageURL = userActivity.webpageURL;
if ([EXEnvironment sharedEnvironment].isDetached) {
return [EXKernelLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
} else {
NSString *path = [webpageURL path];
// Filter out URLs that don't match experience URLs since the AASA pattern's grammar is not as
// expressive as we'd like and matches profile URLs too
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^/@[a-z0-9_-]+/.+$"
options:NSRegularExpressionCaseInsensitive
error:nil];
NSUInteger matchCount = [regex numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)];
if (matchCount > 0) {
[EXKernelLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
return YES;
} else {
[application openURL:webpageURL options:@{} completionHandler:nil];
return YES;
}
}
}
return NO;
}
#pragma mark - internal
- (void)_initDefaultKeys
{
// these are provided in the expo/expo open source repo as defaults; they can all be overridden by setting
// the `applicationKeys` property on ExpoKit.
if ([EXBuildConstants sharedInstance].defaultApiKeys) {
self.applicationKeys = [EXBuildConstants sharedInstance].defaultApiKeys;
}
}
@end