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