ios: SDK v2 pass one
- cleanup - API simplification (single loadURL method) - JitsiMeet singleton for linking helpers and globals - Linking moved to app
This commit is contained in:
parent
1c47720a08
commit
dbc88b972e
|
@ -19,11 +19,10 @@
|
|||
#import "FIRUtilities.h"
|
||||
#import "Types.h"
|
||||
|
||||
#import <JitsiMeet/JitsiMeet.h>
|
||||
|
||||
@import Crashlytics;
|
||||
@import Fabric;
|
||||
@import Firebase;
|
||||
@import JitsiMeet;
|
||||
|
||||
|
||||
@implementation AppDelegate
|
||||
|
@ -40,10 +39,10 @@
|
|||
|
||||
// Set the conference activity type defined in this application.
|
||||
// This cannot be defined by the SDK.
|
||||
JitsiMeetView.conferenceActivityType = JitsiMeetConferenceActivityType;
|
||||
[JitsiMeet sharedInstance].conferenceActivityType = JitsiMeetConferenceActivityType;
|
||||
[[JitsiMeet sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
|
||||
return [JitsiMeetView application:application
|
||||
didFinishLaunchingWithOptions:launchOptions];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark Linking delegate methods
|
||||
|
@ -63,9 +62,9 @@
|
|||
NSURL *dynamicLinkURL = dynamicLink.url;
|
||||
if (dynamicLinkURL) {
|
||||
userActivity.webpageURL = dynamicLinkURL;
|
||||
[JitsiMeetView application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
[[JitsiMeet sharedInstance] application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
}];
|
||||
|
||||
|
@ -75,9 +74,9 @@
|
|||
}
|
||||
|
||||
// 2. Default to plain old, non-Firebase-assisted Universal Links.
|
||||
return [JitsiMeetView application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
return [[JitsiMeet sharedInstance] application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
|
@ -100,9 +99,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
return [JitsiMeetView application:app
|
||||
openURL:openUrl
|
||||
options:options];
|
||||
return [[JitsiMeet sharedInstance] application:app
|
||||
openURL:openUrl
|
||||
options:options];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -15,18 +16,14 @@
|
|||
*/
|
||||
|
||||
#import <Availability.h>
|
||||
#import <CoreSpotlight/CoreSpotlight.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
@import CoreSpotlight;
|
||||
@import MobileCoreServices;
|
||||
@import Intents; // Needed for NSUserActivity suggestedInvocationPhrase
|
||||
|
||||
#import "Types.h"
|
||||
#import "ViewController.h"
|
||||
|
||||
// Needed for NSUserActivity suggestedInvocationPhrase
|
||||
@import Intents;
|
||||
|
||||
|
||||
@interface ViewController ()
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
|
@ -42,11 +39,9 @@
|
|||
// anyway.
|
||||
view.welcomePageEnabled = YES;
|
||||
|
||||
[view loadURL:nil];
|
||||
[view loadURL:[[JitsiMeet sharedInstance] getInitialURL]];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// JitsiMeetViewDelegate
|
||||
|
||||
- (void)_onJitsiMeetViewDelegateEvent:(NSString *)name
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */; };
|
||||
0B49424520AD8DBD00BD2DE0 /* outgoingStart.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */; };
|
||||
0B49424620AD8DBD00BD2DE0 /* outgoingRinging.wav in Resources */ = {isa = PBXBuildFile; fileRef = 0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */; };
|
||||
0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C2CFC200F51D60060D076 /* LaunchOptions.m */; };
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */; };
|
||||
|
@ -45,6 +44,8 @@
|
|||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
|
||||
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
|
||||
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */; };
|
||||
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
|
||||
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
@ -55,7 +56,6 @@
|
|||
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPVolumeViewManager.m; sourceTree = "<group>"; };
|
||||
0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = outgoingStart.wav; path = ../../sounds/outgoingStart.wav; sourceTree = "<group>"; };
|
||||
0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = outgoingRinging.wav; path = ../../sounds/outgoingRinging.wav; sourceTree = "<group>"; };
|
||||
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchOptions.m; sourceTree = "<group>"; };
|
||||
0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
||||
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
|
||||
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
|
||||
|
@ -93,6 +93,10 @@
|
|||
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocaleDetector.m; sourceTree = "<group>"; };
|
||||
DEFE535321FB1BF800011A3A /* JitsiMeet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeet.m; sourceTree = "<group>"; };
|
||||
DEFE535521FB2E8300011A3A /* ReactUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactUtils.m; sourceTree = "<group>"; };
|
||||
DEFE535721FB2E9E00011A3A /* ReactUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactUtils.h; sourceTree = "<group>"; };
|
||||
DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeet+Private.h"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -158,16 +162,19 @@
|
|||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */,
|
||||
DEFE535321FB1BF800011A3A /* JitsiMeet.m */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
|
||||
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */,
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */,
|
||||
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */,
|
||||
C6A3426B204F127900E062DD /* picture-in-picture */,
|
||||
0BCA495D1EC4B6C600B793EE /* POSIX.m */,
|
||||
0BCA495E1EC4B6C600B793EE /* Proximity.m */,
|
||||
DEFE535721FB2E9E00011A3A /* ReactUtils.h */,
|
||||
DEFE535521FB2E8300011A3A /* ReactUtils.m */,
|
||||
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
|
||||
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
|
||||
);
|
||||
|
@ -447,7 +454,6 @@
|
|||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
|
||||
0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */,
|
||||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */,
|
||||
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */,
|
||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
||||
|
@ -455,11 +461,13 @@
|
|||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
A480429C21EE335600289B73 /* AmplitudeModule.m in Sources */,
|
||||
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
|
||||
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */,
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,
|
||||
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */,
|
||||
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */,
|
||||
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
|
||||
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeet.h"
|
||||
|
||||
@interface JitsiMeet ()
|
||||
|
||||
- (RCTBridge *)getReactBridge;
|
||||
|
||||
@end
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,6 +15,32 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// JitsiMeetView
|
||||
#import <JitsiMeet/JitsiMeetView.h>
|
||||
#import <JitsiMeet/JitsiMeetViewDelegate.h>
|
||||
|
||||
@interface JitsiMeet : NSObject
|
||||
|
||||
@property (copy, nonatomic, nullable) NSString *conferenceActivityType;
|
||||
|
||||
#pragma mak - This class is a singleton
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
#pragma mark - Methods that the App delegate must call
|
||||
|
||||
- (BOOL)application:(UIApplication *_Nonnull)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *_Nonnull)launchOptions;
|
||||
|
||||
- (BOOL)application:(UIApplication * _Nonnull)application
|
||||
continueUserActivity:(NSUserActivity * _Nonnull)userActivity
|
||||
restorationHandler:(void (^ _Nullable)(NSArray * _Nullable))restorationHandler;
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
- (NSDictionary *)getInitialURL;
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Intents/Intents.h>
|
||||
|
||||
#import "Dropbox.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "RCTBridgeWrapper.h"
|
||||
#import "ReactUtils.h"
|
||||
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
|
||||
|
||||
@implementation JitsiMeet {
|
||||
RCTBridgeWrapper *_bridgeWrapper;
|
||||
NSDictionary *_launchOptions;
|
||||
}
|
||||
|
||||
#pragma mak - This class is a singleton
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static JitsiMeet *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
// Initialize the on and only bridge for interfacing with React Native.
|
||||
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
||||
|
||||
// Register a fatal error handler for React.
|
||||
registerReactFatalErrorHandler();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Methods that the App delegate must call
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
|
||||
_launchOptions = [launchOptions copy];
|
||||
|
||||
[Dropbox setAppKey];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
|
||||
|
||||
id url = [self urlFromUserActivity:userActivity];
|
||||
|
||||
return url && [JitsiMeetView loadURLInViews:url];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
|
||||
if ([Dropbox application:app openURL:url options:options]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([RNGoogleSignin application:app
|
||||
openURL:url
|
||||
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
|
||||
annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return [JitsiMeetView loadURLInViews:@{ @"url" : url.absoluteString }];
|
||||
}
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
- (NSDictionary *)getInitialURL {
|
||||
if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
NSURL *url = _launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
return @{ @"url" : url.absoluteString };
|
||||
} else {
|
||||
NSDictionary *userActivityDictionary
|
||||
= _launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
||||
NSUserActivity *userActivity
|
||||
= [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
|
||||
if (userActivity != nil) {
|
||||
return [self urlFromUserActivity:userActivity];
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)urlFromUserActivity:(NSUserActivity *)userActivity {
|
||||
NSString *activityType = userActivity.activityType;
|
||||
|
||||
if ([activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
// App was started by opening a URL in the browser
|
||||
return @{ @"url" : userActivity.webpageURL.absoluteString };
|
||||
} else if ([activityType isEqualToString:@"INStartAudioCallIntent"]
|
||||
|| [activityType isEqualToString:@"INStartVideoCallIntent"]) {
|
||||
// App was started by a CallKit Intent
|
||||
INIntent *intent = userActivity.interaction.intent;
|
||||
NSArray<INPerson *> *contacts;
|
||||
NSString *url;
|
||||
BOOL startAudioOnly = NO;
|
||||
|
||||
if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
|
||||
contacts = ((INStartAudioCallIntent *) intent).contacts;
|
||||
startAudioOnly = YES;
|
||||
} else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
|
||||
contacts = ((INStartVideoCallIntent *) intent).contacts;
|
||||
}
|
||||
|
||||
if (contacts && (url = contacts.firstObject.personHandle.value)) {
|
||||
return @{
|
||||
@"config": @{@"startAudioOnly":@(startAudioOnly)},
|
||||
@"url": url
|
||||
};
|
||||
}
|
||||
} else if (self.conferenceActivityType && [activityType isEqualToString:self.conferenceActivityType]) {
|
||||
// App was started by continuing a registered NSUserActivity (SiriKit, Handoff, ...)
|
||||
NSString *url;
|
||||
|
||||
if ((url = userActivity.userInfo[@"url"])) {
|
||||
return @{ @"url" : url };
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - Private API methods
|
||||
|
||||
- (RCTBridge *)getReactBridge {
|
||||
return _bridgeWrapper.bridge;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,7 +19,7 @@
|
|||
|
||||
@interface JitsiMeetView ()
|
||||
|
||||
+ (NSDictionary *)conferenceURLFromUserActivity:(NSUserActivity *)userActivity;
|
||||
+ (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope;
|
||||
+ (BOOL)loadURLInViews:(NSDictionary *)urlObject;
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -33,26 +34,6 @@
|
|||
|
||||
@property (nonatomic) BOOL welcomePageEnabled;
|
||||
|
||||
+ (BOOL)application:(UIApplication *_Nonnull)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *_Nonnull)launchOptions;
|
||||
|
||||
+ (BOOL)application:(UIApplication * _Nonnull)application
|
||||
continueUserActivity:(NSUserActivity * _Nonnull)userActivity
|
||||
restorationHandler:(void (^ _Nullable)(NSArray * _Nullable))restorationHandler;
|
||||
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options;
|
||||
|
||||
+ (BOOL)application:(UIApplication * _Nonnull)application
|
||||
openURL:(NSURL * _Nonnull)URL
|
||||
sourceApplication:(NSString * _Nullable)sourceApplication
|
||||
annotation:(id _Nullable)annotation __deprecated;
|
||||
|
||||
- (void)loadURL:(NSURL * _Nullable)url;
|
||||
|
||||
- (void)loadURLObject:(NSDictionary * _Nullable)urlObject;
|
||||
|
||||
- (void)loadURLString:(NSString * _Nullable)urlString;
|
||||
- (void)loadURL:(NSDictionary * _Nullable)urlObject;
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,62 +15,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Intents/Intents.h>
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
|
||||
#import "Dropbox.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "RCTBridgeWrapper.h"
|
||||
|
||||
/**
|
||||
* A `RCTFatalHandler` implementation which swallows JavaScript errors. In the
|
||||
* Release configuration, React Native will (intentionally) raise an unhandled
|
||||
* `NSException` for an unhandled JavaScript error. This will effectively kill
|
||||
* the application. `_RCTFatal` is suitable to be in accord with the Web i.e.
|
||||
* not kill the application.
|
||||
*/
|
||||
RCTFatalHandler _RCTFatal = ^(NSError *error) {
|
||||
id jsStackTrace = error.userInfo[RCTJSStackTraceKey];
|
||||
@try {
|
||||
NSString *name
|
||||
= [NSString stringWithFormat:@"%@: %@",
|
||||
RCTFatalExceptionName,
|
||||
error.localizedDescription];
|
||||
NSString *message
|
||||
= RCTFormatError(error.localizedDescription, jsStackTrace, 75);
|
||||
[NSException raise:name format:@"%@", message];
|
||||
} @catch (NSException *e) {
|
||||
if (!jsStackTrace) {
|
||||
@throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to register a fatal error handler for React. Our handler
|
||||
* won't kill the process, it will swallow JS errors and print stack traces
|
||||
* instead.
|
||||
*/
|
||||
void registerFatalErrorHandler() {
|
||||
#if !DEBUG
|
||||
// In the Release configuration, React Native will (intentionally) raise an
|
||||
// unhandled `NSException` for an unhandled JavaScript error. This will
|
||||
// effectively kill the application. In accord with the Web, do not kill the
|
||||
// application.
|
||||
if (!RCTGetFatalHandler()) {
|
||||
RCTSetFatalHandler(_RCTFatal);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@interface JitsiMeetView() {
|
||||
@implementation JitsiMeetView {
|
||||
/**
|
||||
* The unique identifier of this `JitsiMeetView` within the process for the
|
||||
* purposes of `ExternalAPI`. The name scope was inspired by postis which we
|
||||
|
@ -78,98 +32,24 @@ void registerFatalErrorHandler() {
|
|||
NSString *externalAPIScope;
|
||||
|
||||
RCTRootView *rootView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JitsiMeetView {
|
||||
NSNumber *_pictureInPictureEnabled;
|
||||
}
|
||||
|
||||
@dynamic pictureInPictureEnabled;
|
||||
|
||||
static NSString *_conferenceActivityType;
|
||||
|
||||
static RCTBridgeWrapper *bridgeWrapper;
|
||||
|
||||
/**
|
||||
* Copy of the `launchOptions` dictionary that the application was started with.
|
||||
* It is required for the initial URL to be used if a (Universal) link was used
|
||||
* to launch a new instance of the application.
|
||||
*/
|
||||
static NSDictionary *_launchOptions;
|
||||
|
||||
/**
|
||||
* The `JitsiMeetView`s associated with their `ExternalAPI` scopes (i.e. unique
|
||||
* identifiers within the process).
|
||||
*/
|
||||
static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// Store launch options, will be used when we create the bridge.
|
||||
_launchOptions = [launchOptions copy];
|
||||
|
||||
[Dropbox setAppKey];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark Linking delegate helpers
|
||||
// https://facebook.github.io/react-native/docs/linking.html
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler
|
||||
{
|
||||
// XXX At least twice we received bug reports about malfunctioning loadURL
|
||||
// in the Jitsi Meet SDK while the Jitsi Meet app seemed to functioning as
|
||||
// expected in our testing. But that was to be expected because the app does
|
||||
// not exercise loadURL. In order to increase the test coverage of loadURL,
|
||||
// channel Universal linking through loadURL.
|
||||
|
||||
id url = [self conferenceURLFromUserActivity:userActivity];
|
||||
|
||||
if (url && [self loadURLObjectInViews:url]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return [RCTLinkingManager application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
if ([Dropbox application:app openURL:url options:options]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([RNGoogleSignin application:app
|
||||
openURL:url
|
||||
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
|
||||
annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// XXX At least twice we received bug reports about malfunctioning loadURL
|
||||
// in the Jitsi Meet SDK while the Jitsi Meet app seemed to functioning as
|
||||
// expected in our testing. But that was to be expected because the app does
|
||||
// not exercise loadURL. In order to increase the test coverage of loadURL,
|
||||
// channel Universal linking through loadURL.
|
||||
if ([self loadURLInViews:url]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return [RCTLinkingManager application:app openURL:url options:options];
|
||||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
return [self application:application openURL:url options:@{}];
|
||||
/**
|
||||
* This gets called automagically when the program starts.
|
||||
*/
|
||||
__attribute__((constructor))
|
||||
static void initializeViewsMap() {
|
||||
views = [NSMapTable strongToWeakObjectsMapTable];
|
||||
}
|
||||
|
||||
#pragma mark Initializers
|
||||
|
@ -201,19 +81,26 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
return self;
|
||||
}
|
||||
|
||||
#pragma mark API
|
||||
|
||||
/**
|
||||
* Loads a specific `NSURL` which may identify a conference to join. If the
|
||||
* specified `NSURL` is `nil` and the Welcome page is enabled, the Welcome page
|
||||
* is displayed instead.
|
||||
* Internal initialization:
|
||||
*
|
||||
* @param url The `NSURL` to load which may identify a conference to join.
|
||||
* - sets the background color
|
||||
* - initializes the external API scope
|
||||
*/
|
||||
- (void)loadURL:(NSURL *)url {
|
||||
[self loadURLString:url ? url.absoluteString : nil];
|
||||
- (void)initWithXXX {
|
||||
// Hook this JitsiMeetView into ExternalAPI.
|
||||
externalAPIScope = [NSUUID UUID].UUIDString;
|
||||
[views setObject:self forKey:externalAPIScope];
|
||||
|
||||
// Set a background color which is in accord with the JavaScript and Android
|
||||
// parts of the application and causes less perceived visual flicker than
|
||||
// the default background color.
|
||||
self.backgroundColor
|
||||
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
||||
}
|
||||
|
||||
#pragma mark API
|
||||
|
||||
/**
|
||||
* Loads a specific URL which may identify a conference to join. The URL is
|
||||
* specified in the form of an `NSDictionary` of properties which (1)
|
||||
|
@ -224,7 +111,7 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
*
|
||||
* @param urlObject The URL to load which may identify a conference to join.
|
||||
*/
|
||||
- (void)loadURLObject:(NSDictionary *)urlObject {
|
||||
- (void)loadURL:(NSDictionary *_Nullable)urlObject {
|
||||
NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (self.defaultURL) {
|
||||
|
@ -242,14 +129,14 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
props[@"url"] = urlObject;
|
||||
}
|
||||
|
||||
// XXX The method loadURLObject: is supposed to be imperative i.e. a second
|
||||
// XXX The method loadURL: is supposed to be imperative i.e. a second
|
||||
// invocation with one and the same URL is expected to join the respective
|
||||
// conference again if the first invocation was followed by leaving the
|
||||
// conference. However, React and, respectively,
|
||||
// appProperties/initialProperties are declarative expressions i.e. one and
|
||||
// the same URL will not trigger an automatic re-render in the JavaScript
|
||||
// source code. The workaround implemented bellow introduces imperativeness
|
||||
// in React Component props by defining a unique value per loadURLObject:
|
||||
// in React Component props by defining a unique value per loadURL:
|
||||
// invocation.
|
||||
props[@"timestamp"] = @(mach_absolute_time());
|
||||
|
||||
|
@ -257,8 +144,9 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
// Update props with the new URL.
|
||||
rootView.appProperties = props;
|
||||
} else {
|
||||
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
|
||||
rootView
|
||||
= [[RCTRootView alloc] initWithBridge:bridgeWrapper.bridge
|
||||
= [[RCTRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"App"
|
||||
initialProperties:props];
|
||||
rootView.backgroundColor = self.backgroundColor;
|
||||
|
@ -272,28 +160,6 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a specific URL `NSString` which may identify a conference to
|
||||
* join. If the specified URL `NSString` is `nil` and the Welcome page is
|
||||
* enabled, the Welcome page is displayed instead.
|
||||
*
|
||||
* @param urlString The URL `NSString` to load which may identify a conference
|
||||
* to join.
|
||||
*/
|
||||
- (void)loadURLString:(NSString *)urlString {
|
||||
[self loadURLObject:urlString ? @{ @"url": urlString } : nil];
|
||||
}
|
||||
|
||||
#pragma conferenceActivityType getter / setter
|
||||
|
||||
+ (NSString *)conferenceActivityType {
|
||||
return _conferenceActivityType;
|
||||
}
|
||||
|
||||
+ (void) setConferenceActivityType:(NSString *)conferenceActivityType {
|
||||
_conferenceActivityType = conferenceActivityType;
|
||||
}
|
||||
|
||||
#pragma pictureInPictureEnabled getter / setter
|
||||
|
||||
- (void) setPictureInPictureEnabled:(BOOL)pictureInPictureEnabled {
|
||||
|
@ -318,19 +184,7 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
|
||||
#pragma mark Private methods
|
||||
|
||||
/**
|
||||
* Loads a specific `NSURL` in all existing `JitsiMeetView`s.
|
||||
*
|
||||
* @param url The `NSURL` to load in all existing `JitsiMeetView`s.
|
||||
* @return `YES` if the specified `url` was submitted for loading in at least
|
||||
* one `JitsiMeetView`; otherwise, `NO`.
|
||||
*/
|
||||
+ (BOOL)loadURLInViews:(NSURL *)url {
|
||||
return
|
||||
[self loadURLObjectInViews:url ? @{ @"url": url.absoluteString } : nil];
|
||||
}
|
||||
|
||||
+ (BOOL)loadURLObjectInViews:(NSDictionary *)urlObject {
|
||||
+ (BOOL)loadURLInViews:(NSDictionary *)urlObject {
|
||||
BOOL handled = NO;
|
||||
|
||||
if (views) {
|
||||
|
@ -339,7 +193,7 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
= [self viewForExternalAPIScope:externalAPIScope];
|
||||
|
||||
if (view) {
|
||||
[view loadURLObject:urlObject];
|
||||
[view loadURL:urlObject];
|
||||
handled = YES;
|
||||
}
|
||||
}
|
||||
|
@ -348,79 +202,8 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
|||
return handled;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)conferenceURLFromUserActivity:(NSUserActivity *)userActivity {
|
||||
NSString *activityType = userActivity.activityType;
|
||||
|
||||
if ([activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
// App was started by opening a URL in the browser
|
||||
return @{ @"url" : userActivity.webpageURL.absoluteString };
|
||||
} else if ([activityType isEqualToString:@"INStartAudioCallIntent"]
|
||||
|| [activityType isEqualToString:@"INStartVideoCallIntent"]) {
|
||||
// App was started by a CallKit Intent
|
||||
INIntent *intent = userActivity.interaction.intent;
|
||||
NSArray<INPerson *> *contacts;
|
||||
NSString *url;
|
||||
BOOL startAudioOnly = NO;
|
||||
|
||||
if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
|
||||
contacts = ((INStartAudioCallIntent *) intent).contacts;
|
||||
startAudioOnly = YES;
|
||||
} else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
|
||||
contacts = ((INStartVideoCallIntent *) intent).contacts;
|
||||
}
|
||||
|
||||
if (contacts && (url = contacts.firstObject.personHandle.value)) {
|
||||
return @{
|
||||
@"config": @{@"startAudioOnly":@(startAudioOnly)},
|
||||
@"url": url
|
||||
};
|
||||
}
|
||||
} else if (_conferenceActivityType && [activityType isEqualToString:_conferenceActivityType]) {
|
||||
// App was started by continuing a registered NSUserActivity (SiriKit, Handoff, ...)
|
||||
NSString *url;
|
||||
|
||||
if ((url = userActivity.userInfo[@"url"])) {
|
||||
return @{ @"url" : url };
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope {
|
||||
return [views objectForKey:externalAPIScope];
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal initialization:
|
||||
*
|
||||
* - sets the background color
|
||||
* - creates the React bridge
|
||||
* - loads the necessary custom fonts
|
||||
* - registers a custom fatal error error handler for React
|
||||
*/
|
||||
- (void)initWithXXX {
|
||||
static dispatch_once_t dispatchOncePredicate;
|
||||
|
||||
dispatch_once(&dispatchOncePredicate, ^{
|
||||
// Initialize the static state of JitsiMeetView.
|
||||
bridgeWrapper
|
||||
= [[RCTBridgeWrapper alloc] initWithLaunchOptions:_launchOptions];
|
||||
views = [NSMapTable strongToWeakObjectsMapTable];
|
||||
|
||||
// Register a fatal error handler for React.
|
||||
registerFatalErrorHandler();
|
||||
});
|
||||
|
||||
// Hook this JitsiMeetView into ExternalAPI.
|
||||
externalAPIScope = [NSUUID UUID].UUIDString;
|
||||
[views setObject:self forKey:externalAPIScope];
|
||||
|
||||
// Set a background color which is in accord with the JavaScript and Android
|
||||
// parts of the application and causes less perceived visual flicker than
|
||||
// the default background color.
|
||||
self.backgroundColor
|
||||
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetView+Private.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
|
||||
@interface LaunchOptions : NSObject<RCTBridgeModule>
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *bridge;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LaunchOptions
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
- (dispatch_queue_t)methodQueue {
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve
|
||||
reject:(__unused RCTPromiseRejectBlock)reject) {
|
||||
id initialURL = nil;
|
||||
|
||||
if (self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
NSURL *url = self.bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
initialURL = url.absoluteString;
|
||||
} else {
|
||||
NSDictionary *userActivityDictionary
|
||||
= self.bridge.launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
||||
NSUserActivity *userActivity
|
||||
= [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
|
||||
if (userActivity != nil) {
|
||||
initialURL = [JitsiMeetView conferenceURLFromUserActivity:userActivity];
|
||||
}
|
||||
}
|
||||
|
||||
resolve(initialURL != nil ? initialURL : (id)kCFNull);
|
||||
}
|
||||
|
||||
@end
|
|
@ -34,6 +34,4 @@
|
|||
|
||||
@property (nonatomic, readonly, strong) RCTBridge *bridge;
|
||||
|
||||
- (instancetype)initWithLaunchOptions:(NSDictionary *)launchOptions;
|
||||
|
||||
@end
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
*/
|
||||
@implementation RCTBridgeWrapper
|
||||
|
||||
- (instancetype)initWithLaunchOptions:(NSDictionary *)launchOptions {
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_bridge
|
||||
= [[RCTBridge alloc] initWithDelegate:self
|
||||
launchOptions:launchOptions];
|
||||
launchOptions:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ReactUtils_h
|
||||
#define ReactUtils_h
|
||||
|
||||
void registerReactFatalErrorHandler(void);
|
||||
|
||||
#endif /* ReactUtils_h */
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
|
||||
#import "ReactUtils.h"
|
||||
|
||||
/**
|
||||
* A `RCTFatalHandler` implementation which swallows JavaScript errors. In the
|
||||
* Release configuration, React Native will (intentionally) raise an unhandled
|
||||
* `NSException` for an unhandled JavaScript error. This will effectively kill
|
||||
* the application. `_RCTFatal` is suitable to be in accord with the Web i.e.
|
||||
* not kill the application.
|
||||
*/
|
||||
RCTFatalHandler _RCTFatal = ^(NSError *error) {
|
||||
id jsStackTrace = error.userInfo[RCTJSStackTraceKey];
|
||||
@try {
|
||||
NSString *name
|
||||
= [NSString stringWithFormat:@"%@: %@",
|
||||
RCTFatalExceptionName,
|
||||
error.localizedDescription];
|
||||
NSString *message
|
||||
= RCTFormatError(error.localizedDescription, jsStackTrace, 75);
|
||||
[NSException raise:name format:@"%@", message];
|
||||
} @catch (NSException *e) {
|
||||
if (!jsStackTrace) {
|
||||
@throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to register a fatal error handler for React. Our handler
|
||||
* won't kill the process, it will swallow JS errors and print stack traces
|
||||
* instead.
|
||||
*/
|
||||
void registerReactFatalErrorHandler() {
|
||||
#if !DEBUG
|
||||
// In the Release configuration, React Native will (intentionally) raise an
|
||||
// unhandled `NSException` for an unhandled JavaScript error. This will
|
||||
// effectively kill the application. In accord with the Web, do not kill the
|
||||
// application.
|
||||
if (!RCTGetFatalHandler()) {
|
||||
RCTSetFatalHandler(_RCTFatal);
|
||||
}
|
||||
#endif
|
||||
}
|
Loading…
Reference in New Issue