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