317 lines
13 KiB
Objective-C
317 lines
13 KiB
Objective-C
#import "RNFirebaseLinks.h"
|
|
|
|
#if __has_include(<FirebaseDynamicLinks/FirebaseDynamicLinks.h>)
|
|
#import <Firebase.h>
|
|
#import "RNFirebaseEvents.h"
|
|
#import "RNFirebaseUtil.h"
|
|
|
|
@implementation RNFirebaseLinks
|
|
|
|
static RNFirebaseLinks *theRNFirebaseLinks = nil;
|
|
static NSString *initialLink = nil;
|
|
static bool jsReady = FALSE;
|
|
|
|
+ (nonnull instancetype)instance {
|
|
// If an event comes in before the bridge has initialised the native module
|
|
// then we create a temporary instance which handles events until the bridge
|
|
// and JS side are ready
|
|
if (theRNFirebaseLinks == nil) {
|
|
theRNFirebaseLinks = [[RNFirebaseLinks alloc] init];
|
|
}
|
|
return theRNFirebaseLinks;
|
|
}
|
|
|
|
RCT_EXPORT_MODULE();
|
|
|
|
- (id)init {
|
|
self = [super init];
|
|
if (self != nil) {
|
|
NSLog(@"Setting up RNFirebaseLinks instance");
|
|
// Set static instance for use from AppDelegate
|
|
theRNFirebaseLinks = self;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
}
|
|
|
|
// *******************************************************
|
|
// ** Start AppDelegate methods
|
|
// *******************************************************
|
|
|
|
- (BOOL)application:(UIApplication *)app
|
|
openURL:(NSURL *)url
|
|
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
|
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
|
|
if (dynamicLink && dynamicLink.url) {
|
|
NSURL* url = dynamicLink.url;
|
|
[self sendJSEvent:self name:LINKS_LINK_RECEIVED body:url.absoluteString];
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)application:(UIApplication *)application
|
|
continueUserActivity:(NSUserActivity *)userActivity
|
|
restorationHandler:(void (^)(NSArray *))restorationHandler {
|
|
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
|
return [[FIRDynamicLinks dynamicLinks]
|
|
handleUniversalLink:userActivity.webpageURL
|
|
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
|
|
if (dynamicLink && dynamicLink.url && error == nil) {
|
|
NSURL* url = dynamicLink.url;
|
|
[self sendJSEvent:self name:LINKS_LINK_RECEIVED body:url.absoluteString];
|
|
} else {
|
|
NSLog(@"Failed to handle universal link: %@", userActivity.webpageURL);
|
|
}
|
|
}];
|
|
}
|
|
return NO;
|
|
}
|
|
// *******************************************************
|
|
// ** Finish AppDelegate methods
|
|
// *******************************************************
|
|
|
|
- (void)sendLink:(NSString *)link {
|
|
[self sendJSEvent:self name:LINKS_LINK_RECEIVED body:link];
|
|
}
|
|
|
|
// ** Start React Module methods **
|
|
RCT_EXPORT_METHOD(createDynamicLink:(NSDictionary *)linkData
|
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
@try {
|
|
FIRDynamicLinkComponents *dynamicLink = [self buildDynamicLink:linkData];
|
|
|
|
if (dynamicLink == nil) {
|
|
reject(@"links/failure", @"Failed to create Dynamic Link", nil);
|
|
} else {
|
|
NSString *longLink = dynamicLink.url.absoluteString;
|
|
NSLog(@"created long dynamic link: %@", longLink);
|
|
resolve(longLink);
|
|
}
|
|
}
|
|
@catch(NSException * e) {
|
|
NSLog(@"create dynamic link failure %@", e);
|
|
reject(@"links/failure",[e reason], nil);
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(createShortDynamicLink:(NSDictionary *)linkData
|
|
type:(NSString *)type
|
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
@try {
|
|
FIRDynamicLinkComponents *components = [self buildDynamicLink:linkData];
|
|
if (type) {
|
|
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
|
|
if ([type isEqual: @"SHORT"]) {
|
|
options.pathLength = FIRShortDynamicLinkPathLengthShort;
|
|
} else if ([type isEqual: @"UNGUESSABLE"]) {
|
|
options.pathLength = FIRShortDynamicLinkPathLengthUnguessable;
|
|
}
|
|
components.options = options;
|
|
}
|
|
[components shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray *_Nullable warnings, NSError *_Nullable error) {
|
|
if (error) {
|
|
NSLog(@"create short dynamic link failure %@", [error localizedDescription]);
|
|
reject(@"links/failure", @"Failed to create Short Dynamic Link", error);
|
|
} else {
|
|
NSString *shortLink = shortURL.absoluteString;
|
|
NSLog(@"created short dynamic link: %@", shortLink);
|
|
resolve(shortLink);
|
|
}
|
|
}];
|
|
}
|
|
@catch(NSException * e) {
|
|
NSLog(@"create short dynamic link failure %@", e);
|
|
reject(@"links/failure",[e reason], nil);
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getInitialLink:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
if (self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
|
NSURL* url = (NSURL*)self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
|
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
|
|
resolve(dynamicLink ? dynamicLink.url.absoluteString : initialLink);
|
|
} else if (self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey]
|
|
&& [self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey][UIApplicationLaunchOptionsUserActivityTypeKey] isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
|
NSDictionary *dictionary = self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
|
NSUserActivity* userActivity = (NSUserActivity*) dictionary[@"UIApplicationLaunchOptionsUserActivityKey"];
|
|
BOOL handled = [[FIRDynamicLinks dynamicLinks] handleUniversalLink:userActivity.webpageURL
|
|
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
|
|
if (error != nil){
|
|
NSLog(@"Failed to handle universal link: %@", [error localizedDescription]);
|
|
reject(@"links/failure", @"Failed to handle universal link", error);
|
|
} else {
|
|
NSString* urlString = dynamicLink ? dynamicLink.url.absoluteString : userActivity.webpageURL.absoluteString;
|
|
NSLog(@"initial link is: %@", urlString);
|
|
resolve(urlString);
|
|
}
|
|
}];
|
|
if (!handled) {
|
|
resolve(nil);
|
|
}
|
|
} else {
|
|
resolve(initialLink);
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(jsInitialised:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
jsReady = TRUE;
|
|
resolve(nil);
|
|
}
|
|
|
|
// ** Start internals **
|
|
|
|
// Because of the time delay between the app starting and the bridge being initialised
|
|
// we catch any events that are received before the JS is ready to receive them
|
|
- (void)sendJSEvent:(RCTEventEmitter *)emitter name:(NSString *)name body:(id)body {
|
|
if (emitter.bridge && jsReady) {
|
|
[RNFirebaseUtil sendJSEvent:emitter name:name body:body];
|
|
} else if (!initialLink) {
|
|
initialLink = body;
|
|
} else {
|
|
NSLog(@"Multiple link events received before the JS links module has been initialised");
|
|
}
|
|
}
|
|
|
|
- (FIRDynamicLinkComponents *)buildDynamicLink:(NSDictionary *)linkData {
|
|
@try {
|
|
NSURL *link = [NSURL URLWithString:linkData[@"link"]];
|
|
FIRDynamicLinkComponents *components = [FIRDynamicLinkComponents componentsWithLink:link domain:linkData[@"dynamicLinkDomain"]];
|
|
|
|
[self setAnalyticsParameters:linkData[@"analytics"] components:components];
|
|
[self setAndroidParameters:linkData[@"android"] components:components];
|
|
[self setIosParameters:linkData[@"ios"] components:components];
|
|
[self setITunesParameters:linkData[@"itunes"] components:components];
|
|
[self setNavigationParameters:linkData[@"navigation"] components:components];
|
|
[self setSocialParameters:linkData[@"social"] components:components];
|
|
|
|
return components;
|
|
}
|
|
@catch(NSException * e) {
|
|
NSLog(@"error while building componets from meta data %@", e);
|
|
@throw;
|
|
}
|
|
}
|
|
|
|
- (void)setAnalyticsParameters:(NSDictionary *)analyticsData
|
|
components:(FIRDynamicLinkComponents *)components {
|
|
FIRDynamicLinkGoogleAnalyticsParameters *analyticsParams = [FIRDynamicLinkGoogleAnalyticsParameters parameters];
|
|
|
|
if (analyticsData[@"campaign"]) {
|
|
analyticsParams.campaign = analyticsData[@"campaign"];
|
|
}
|
|
if (analyticsData[@"content"]) {
|
|
analyticsParams.content = analyticsData[@"content"];
|
|
}
|
|
if (analyticsData[@"medium"]) {
|
|
analyticsParams.medium = analyticsData[@"medium"];
|
|
}
|
|
if (analyticsData[@"source"]) {
|
|
analyticsParams.source = analyticsData[@"source"];
|
|
}
|
|
if (analyticsData[@"term"]) {
|
|
analyticsParams.term = analyticsData[@"term"];
|
|
}
|
|
components.analyticsParameters = analyticsParams;
|
|
}
|
|
|
|
- (void)setAndroidParameters:(NSDictionary *)androidData
|
|
components:(FIRDynamicLinkComponents *)components {
|
|
if (androidData[@"packageName"]) {
|
|
FIRDynamicLinkAndroidParameters *androidParams = [FIRDynamicLinkAndroidParameters parametersWithPackageName: androidData[@"packageName"]];
|
|
|
|
if (androidData[@"fallbackUrl"]) {
|
|
androidParams.fallbackURL = [NSURL URLWithString:androidData[@"fallbackUrl"]];
|
|
}
|
|
if (androidData[@"minimumVersion"]) {
|
|
androidParams.minimumVersion = [androidData[@"minimumVersion"] integerValue];
|
|
}
|
|
components.androidParameters = androidParams;
|
|
}
|
|
}
|
|
|
|
- (void)setIosParameters:(NSDictionary *)iosData
|
|
components:(FIRDynamicLinkComponents *)components {
|
|
if (iosData[@"bundleId"]) {
|
|
FIRDynamicLinkIOSParameters *iOSParams = [FIRDynamicLinkIOSParameters parametersWithBundleID:iosData[@"bundleId"]];
|
|
if (iosData[@"appStoreId"]) {
|
|
iOSParams.appStoreID = iosData[@"appStoreId"];
|
|
}
|
|
if (iosData[@"customScheme"]) {
|
|
iOSParams.customScheme = iosData[@"customScheme"];
|
|
}
|
|
if (iosData[@"fallbackUrl"]) {
|
|
iOSParams.fallbackURL = [NSURL URLWithString:iosData[@"fallbackUrl"]];
|
|
}
|
|
if (iosData[@"iPadBundleId"]) {
|
|
iOSParams.iPadBundleID = iosData[@"iPadBundleId"];
|
|
}
|
|
if (iosData[@"iPadFallbackUrl"]) {
|
|
iOSParams.iPadFallbackURL = [NSURL URLWithString:iosData[@"iPadFallbackUrl"]];
|
|
}
|
|
if (iosData[@"minimumVersion"]) {
|
|
iOSParams.minimumAppVersion = iosData[@"minimumVersion"];
|
|
}
|
|
components.iOSParameters = iOSParams;
|
|
}
|
|
}
|
|
|
|
- (void)setITunesParameters:(NSDictionary *)itunesData
|
|
components:(FIRDynamicLinkComponents *)components {
|
|
FIRDynamicLinkItunesConnectAnalyticsParameters *itunesParams = [FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
|
|
if (itunesData[@"affiliateToken"]) {
|
|
itunesParams.affiliateToken = itunesData[@"affiliateToken"];
|
|
}
|
|
if (itunesData[@"campaignToken"]) {
|
|
itunesParams.campaignToken = itunesData[@"campaignToken"];
|
|
}
|
|
if (itunesData[@"providerToken"]) {
|
|
itunesParams.providerToken = itunesData[@"providerToken"];
|
|
}
|
|
components.iTunesConnectParameters = itunesParams;
|
|
}
|
|
|
|
- (void)setNavigationParameters:(NSDictionary *)navigationData
|
|
components:(FIRDynamicLinkComponents *)components {
|
|
FIRDynamicLinkNavigationInfoParameters *navigationParams = [FIRDynamicLinkNavigationInfoParameters parameters];
|
|
if (navigationData[@"forcedRedirectEnabled"]) {
|
|
navigationParams.forcedRedirectEnabled = navigationData[@"forcedRedirectEnabled"];
|
|
}
|
|
components.navigationInfoParameters = navigationParams;
|
|
}
|
|
|
|
- (void)setSocialParameters:(NSDictionary *)socialData
|
|
components:(FIRDynamicLinkComponents *)components {
|
|
FIRDynamicLinkSocialMetaTagParameters *socialParams = [FIRDynamicLinkSocialMetaTagParameters parameters];
|
|
if (socialData[@"descriptionText"]) {
|
|
socialParams.descriptionText = socialData[@"descriptionText"];
|
|
}
|
|
if (socialData[@"imageUrl"]) {
|
|
socialParams.imageURL = [NSURL URLWithString:socialData[@"imageUrl"]];
|
|
}
|
|
if (socialData[@"title"]) {
|
|
socialParams.title = socialData[@"title"];
|
|
}
|
|
components.socialMetaTagParameters = socialParams;
|
|
}
|
|
|
|
- (NSArray<NSString *> *)supportedEvents {
|
|
return @[LINKS_LINK_RECEIVED];
|
|
}
|
|
|
|
+ (BOOL)requiresMainQueueSetup {
|
|
return YES;
|
|
}
|
|
|
|
@end
|
|
|
|
#else
|
|
@implementation RNFirebaseLinks
|
|
@end
|
|
#endif
|