1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXLinkingManager.h" 4#import "EXScopedModuleRegistry.h" 5#import "EXUtil.h" 6 7#import <React/RCTBridge.h> 8#import <React/RCTEventDispatcher.h> 9#import <React/RCTUtils.h> 10 11NSString * const EXLinkingEventOpenUrl = @"url"; 12 13@interface EXLinkingManager () 14 15@property (nonatomic, weak) id <EXLinkingManagerScopedModuleDelegate> kernelLinkingDelegate; 16@property (nonatomic, strong) NSURL *initialUrl; 17@property (nonatomic) BOOL hasListeners; 18 19@end 20 21@implementation EXLinkingManager 22 23EX_EXPORT_SCOPED_MODULE(RCTLinkingManager, KernelLinkingManager); 24 25- (instancetype)initWithExperienceId:(NSString *)experienceId kernelServiceDelegate:(id)kernelServiceInstance params:(NSDictionary *)params 26{ 27 if (self = [super initWithExperienceId:experienceId kernelServiceDelegate:kernelServiceInstance params:params]) { 28 _kernelLinkingDelegate = kernelServiceInstance; 29 _initialUrl = params[@"initialUri"]; 30 if (_initialUrl == [NSNull null]) { 31 _initialUrl = nil; 32 } 33 } 34 return self; 35} 36 37#pragma mark - RCTEventEmitter methods 38 39- (NSArray<NSString *> *)supportedEvents 40{ 41 return @[EXLinkingEventOpenUrl]; 42} 43 44- (void)startObserving 45{ 46 _hasListeners = YES; 47} 48 49- (void)stopObserving 50{ 51 _hasListeners = NO; 52} 53 54#pragma mark - Linking methods 55 56- (void)dispatchOpenUrlEvent:(NSURL *)url 57{ 58 if (!url || !url.absoluteString) { 59 RCTFatal(RCTErrorWithMessage([NSString stringWithFormat:@"Tried to open a deep link to an invalid url: %@", url])); 60 return; 61 } 62 if (_hasListeners) { 63 [self sendEventWithName:EXLinkingEventOpenUrl body:@{@"url": url.absoluteString}]; 64 } 65} 66 67RCT_EXPORT_METHOD(openURL:(NSURL *)URL 68 resolve:(RCTPromiseResolveBlock)resolve 69 reject:(RCTPromiseRejectBlock)reject) 70{ 71 if ([_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL]) { 72 [_kernelLinkingDelegate linkingModule:self didOpenUrl:URL.absoluteString]; 73 resolve(@YES); 74 } else { 75 [EXUtil performSynchronouslyOnMainThread:^{ 76 [RCTSharedApplication() openURL:URL options:@{} completionHandler:^(BOOL success) { 77 if (success) { 78 resolve(nil); 79 } else { 80 reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unable to open URL: %@", URL], nil); 81 } 82 }]; 83 }]; 84 } 85} 86 87RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL 88 resolve:(RCTPromiseResolveBlock)resolve 89 reject:(__unused RCTPromiseRejectBlock)reject) 90{ 91 __block BOOL canOpen = [_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL]; 92 if (!canOpen) { 93 [EXUtil performSynchronouslyOnMainThread:^{ 94 canOpen = [RCTSharedApplication() canOpenURL:URL]; 95 }]; 96 } 97 resolve(@(canOpen)); 98} 99 100RCT_EXPORT_METHOD(openSettings:(RCTPromiseResolveBlock)resolve 101 reject:(RCTPromiseRejectBlock)reject) 102{ 103 [EXUtil performSynchronouslyOnMainThread:^{ 104 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; 105 [RCTSharedApplication() openURL:url options:@{} completionHandler:^(BOOL success) { 106 if (success) { 107 resolve(nil); 108 } else { 109 reject(RCTErrorUnspecified, @"Unable to open app settings", nil); 110 } 111 }]; 112 }]; 113} 114 115RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve 116 reject:(__unused RCTPromiseRejectBlock)reject) 117{ 118 resolve(RCTNullIfNil(_initialUrl.absoluteString)); 119} 120 121@end 122