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 } 31 return self; 32} 33 34#pragma mark - RCTEventEmitter methods 35 36- (NSArray<NSString *> *)supportedEvents 37{ 38 return @[EXLinkingEventOpenUrl]; 39} 40 41- (void)startObserving 42{ 43 _hasListeners = YES; 44} 45 46- (void)stopObserving 47{ 48 _hasListeners = NO; 49} 50 51#pragma mark - Linking methods 52 53- (void)dispatchOpenUrlEvent:(NSURL *)url 54{ 55 if (!url || !url.absoluteString) { 56 RCTFatal(RCTErrorWithMessage([NSString stringWithFormat:@"Tried to open a deep link to an invalid url: %@", url])); 57 return; 58 } 59 if (_hasListeners) { 60 [self sendEventWithName:EXLinkingEventOpenUrl body:@{@"url": url.absoluteString}]; 61 } 62} 63 64RCT_EXPORT_METHOD(openURL:(NSURL *)URL 65 resolve:(RCTPromiseResolveBlock)resolve 66 reject:(RCTPromiseRejectBlock)reject) 67{ 68 if ([_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL]) { 69 [_kernelLinkingDelegate linkingModule:self didOpenUrl:URL.absoluteString]; 70 resolve(@YES); 71 } else { 72 [EXUtil performSynchronouslyOnMainThread:^{ 73 [RCTSharedApplication() openURL:URL options:@{} completionHandler:^(BOOL success) { 74 if (success) { 75 resolve(nil); 76 } else { 77 reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unable to open URL: %@", URL], nil); 78 } 79 }]; 80 }]; 81 } 82} 83 84RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL 85 resolve:(RCTPromiseResolveBlock)resolve 86 reject:(__unused RCTPromiseRejectBlock)reject) 87{ 88 __block BOOL canOpen = [_kernelLinkingDelegate linkingModule:self shouldOpenExpoUrl:URL]; 89 if (!canOpen) { 90 [EXUtil performSynchronouslyOnMainThread:^{ 91 canOpen = [RCTSharedApplication() canOpenURL:URL]; 92 }]; 93 } 94 resolve(@(canOpen)); 95} 96 97RCT_EXPORT_METHOD(openSettings:(RCTPromiseResolveBlock)resolve 98 reject:(RCTPromiseRejectBlock)reject) 99{ 100 [EXUtil performSynchronouslyOnMainThread:^{ 101 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; 102 [RCTSharedApplication() openURL:url options:@{} completionHandler:^(BOOL success) { 103 if (success) { 104 resolve(nil); 105 } else { 106 reject(RCTErrorUnspecified, @"Unable to open app settings", nil); 107 } 108 }]; 109 }]; 110} 111 112RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve 113 reject:(__unused RCTPromiseRejectBlock)reject) 114{ 115 resolve(RCTNullIfNil(_initialUrl.absoluteString)); 116} 117 118@end 119