diff --git a/example/unity/Assets/Plugins/iOS/NativeCallProxy.h b/example/unity/Assets/Plugins/iOS/NativeCallProxy.h index aecfee6..18bc274 100644 --- a/example/unity/Assets/Plugins/iOS/NativeCallProxy.h +++ b/example/unity/Assets/Plugins/iOS/NativeCallProxy.h @@ -11,5 +11,6 @@ __attribute__ ((visibility("default"))) @interface FrameworkLibAPI : NSObject +(void) registerAPIforNativeCalls:(id) aApi; ++(void) unregisterAPIforNativeCalls; @end diff --git a/example/unity/Assets/Plugins/iOS/NativeCallProxy.mm b/example/unity/Assets/Plugins/iOS/NativeCallProxy.mm index 55661e7..38ce685 100644 --- a/example/unity/Assets/Plugins/iOS/NativeCallProxy.mm +++ b/example/unity/Assets/Plugins/iOS/NativeCallProxy.mm @@ -9,12 +9,19 @@ +(void) registerAPIforNativeCalls:(id) aApi api = aApi; } ++(void) unregisterAPIforNativeCalls +{ + api = NULL; +} + @end extern "C" { void sendMessageToMobileApp(const char* message) { - return [api sendMessageToMobileApp:[NSString stringWithUTF8String:message]]; + if (api != NULL) { + [api sendMessageToMobileApp:[NSString stringWithUTF8String:message]]; + } } } diff --git a/ios/RNUnityView.h b/ios/RNUnityView.h index 23824e4..622adb2 100644 --- a/ios/RNUnityView.h +++ b/ios/RNUnityView.h @@ -31,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onPlayerQuit; - (void)unloadUnity; -- (void)pauseUnity:(BOOL * _Nonnull)pause; +- (void)pauseUnity:(BOOL)pause; - (void)postMessage:(NSString* _Nonnull )gameObject methodName:(NSString* _Nonnull)methodName message:(NSString* _Nonnull) message; @end @@ -54,7 +54,7 @@ NS_ASSUME_NONNULL_END @property (nonatomic, copy) RCTBubblingEventBlock _Nullable onPlayerQuit; - (void)unloadUnity; -- (void)pauseUnity:(BOOL * _Nonnull)pause; +- (void)pauseUnity:(BOOL)pause; - (void)postMessage:(NSString* _Nonnull )gameObject methodName:(NSString* _Nonnull)methodName message:(NSString* _Nonnull) message; diff --git a/ios/RNUnityView.mm b/ios/RNUnityView.mm index a4caa74..cee3e69 100644 --- a/ios/RNUnityView.mm +++ b/ios/RNUnityView.mm @@ -61,6 +61,14 @@ - (void)initUnityModule { array[count] = NULL; [[self ufw] runEmbeddedWithArgc: gArgc argv: array appLaunchOpts: appLaunchOpts]; + + // Free the strdup'd strings and the malloc'd array + for (unsigned i = 0; i < count; i++) + { + free(array[i]); + } + free(array); + [[self ufw] appController].quitHandler = ^(){ NSLog(@"AppController.quitHandler called"); }; [self.ufw.appController.rootView removeFromSuperview]; @@ -90,7 +98,7 @@ - (void)layoutSubviews { } } -- (void)pauseUnity:(BOOL * _Nonnull)pause { +- (void)pauseUnity:(BOOL)pause { if([self unityIsInitialized]) { [[self ufw] pause:pause]; } @@ -102,6 +110,7 @@ - (void)unloadUnity { [main makeKeyAndVisible]; if([self unityIsInitialized]) { + [NSClassFromString(@"FrameworkLibAPI") unregisterAPIforNativeCalls]; [[self ufw] unloadApplication]; } } @@ -123,7 +132,7 @@ - (void)unityDidUnload:(NSNotification*)notification { [self setUfw: nil]; if (self.onPlayerUnload) { - self.onPlayerUnload(nil); + self.onPlayerUnload(@{@"message": @"unloaded"}); } } } @@ -134,7 +143,7 @@ - (void)unityDidQuit:(NSNotification*)notification { [self setUfw: nil]; if (self.onPlayerQuit) { - self.onPlayerQuit(nil); + self.onPlayerQuit(@{@"message": @"quit"}); } } } @@ -157,15 +166,11 @@ - (void)postMessage:(NSString *)gameObject methodName:(NSString*)methodName mess - (void)prepareForRecycle { [super prepareForRecycle]; - if ([self unityIsInitialized]) { - [[self ufw] unloadApplication]; - - NSArray *viewsToRemove = self.subviews; - for (UIView *v in viewsToRemove) { - [v removeFromSuperview]; - } - - [self setUfw:nil]; + // Only detach the Unity view from this component — do NOT unload the + // singleton Unity runtime, as that would break it for any future views. + NSArray *viewsToRemove = self.subviews; + for (UIView *v in viewsToRemove) { + [v removeFromSuperview]; } } @@ -178,15 +183,39 @@ - (instancetype)initWithFrame:(CGRect)frame { static const auto defaultProps = std::make_shared(); _props = defaultProps; - self.onUnityMessage = [self](NSDictionary* data) { - if (_eventEmitter != nil) { - auto gridViewEventEmitter = std::static_pointer_cast(_eventEmitter); + __weak RNUnityView *weakSelf = self; + self.onUnityMessage = ^(NSDictionary* data) { + RNUnityView *strongSelf = weakSelf; + if (strongSelf && strongSelf->_eventEmitter != nil) { + auto gridViewEventEmitter = std::static_pointer_cast(strongSelf->_eventEmitter); facebook::react::RNUnityViewEventEmitter::OnUnityMessage event = { .message=[[data valueForKey:@"message"] UTF8String] }; gridViewEventEmitter->onUnityMessage(event); } }; + + self.onPlayerUnload = ^(NSDictionary* data) { + RNUnityView *strongSelf = weakSelf; + if (strongSelf && strongSelf->_eventEmitter != nil) { + auto eventEmitter = std::static_pointer_cast(strongSelf->_eventEmitter); + facebook::react::RNUnityViewEventEmitter::OnPlayerUnload event = { + .message=[[data valueForKey:@"message"] UTF8String] + }; + eventEmitter->onPlayerUnload(event); + } + }; + + self.onPlayerQuit = ^(NSDictionary* data) { + RNUnityView *strongSelf = weakSelf; + if (strongSelf && strongSelf->_eventEmitter != nil) { + auto eventEmitter = std::static_pointer_cast(strongSelf->_eventEmitter); + facebook::react::RNUnityViewEventEmitter::OnPlayerQuit event = { + .message=[[data valueForKey:@"message"] UTF8String] + }; + eventEmitter->onPlayerQuit(event); + } + }; } return self; diff --git a/ios/RNUnityViewManager.mm b/ios/RNUnityViewManager.mm index f9b79bd..76db227 100644 --- a/ios/RNUnityViewManager.mm +++ b/ios/RNUnityViewManager.mm @@ -13,10 +13,8 @@ @implementation RNUnityViewManager RCT_EXPORT_VIEW_PROPERTY(onPlayerUnload, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(onPlayerQuit, RCTBubblingEventBlock) -RNUnityView *unity; - - (UIView *)view { - unity = [[RNUnityView alloc] init]; + RNUnityView *unity = [[RNUnityView alloc] init]; UIWindow * main = [[[UIApplication sharedApplication] delegate] window]; if(main != nil) { @@ -41,18 +39,18 @@ + (BOOL)requiresMainQueueSetup { RCTLogError(@"Cannot find NativeView with tag #%@", reactTag); return; } - [unity postMessage:(NSString *)gameObject methodName:(NSString *)methodName message:(NSString *)message]; + [view postMessage:(NSString *)gameObject methodName:(NSString *)methodName message:(NSString *)message]; }]; } -RCT_EXPORT_METHOD(pauseUnity:(nonnull NSNumber*) reactTag pause:(BOOL * _Nonnull)pause) { +RCT_EXPORT_METHOD(pauseUnity:(nonnull NSNumber*) reactTag pause:(BOOL)pause) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { RNUnityView *view = (RNUnityView*) viewRegistry[reactTag]; if (!view || ![view isKindOfClass:[RNUnityView class]]) { RCTLogError(@"Cannot find NativeView with tag #%@", reactTag); return; } - [unity pauseUnity:(BOOL * _Nonnull)pause]; + [view pauseUnity:pause]; }]; } @@ -63,7 +61,7 @@ + (BOOL)requiresMainQueueSetup { RCTLogError(@"Cannot find NativeView with tag #%@", reactTag); return; } - [unity pauseUnity:(BOOL * _Nonnull)false]; + [view pauseUnity:NO]; }]; } @@ -74,7 +72,7 @@ + (BOOL)requiresMainQueueSetup { RCTLogError(@"Cannot find NativeView with tag #%@", reactTag); return; } - [unity unloadUnity]; + [view unloadUnity]; }]; } diff --git a/src/UnityView.tsx b/src/UnityView.tsx index 0b21cea..5b020da 100644 --- a/src/UnityView.tsx +++ b/src/UnityView.tsx @@ -66,7 +66,7 @@ export default class UnityView extends React.Component { } componentWillUnmount() { - if (this.ref.current) { + if (this.ref.current && !this.props.androidKeepPlayerMounted) { Commands.unloadUnity(this.ref.current); } } diff --git a/unity/Assets/Plugins/iOS/NativeCallProxy.h b/unity/Assets/Plugins/iOS/NativeCallProxy.h index aecfee6..18bc274 100644 --- a/unity/Assets/Plugins/iOS/NativeCallProxy.h +++ b/unity/Assets/Plugins/iOS/NativeCallProxy.h @@ -11,5 +11,6 @@ __attribute__ ((visibility("default"))) @interface FrameworkLibAPI : NSObject +(void) registerAPIforNativeCalls:(id) aApi; ++(void) unregisterAPIforNativeCalls; @end diff --git a/unity/Assets/Plugins/iOS/NativeCallProxy.mm b/unity/Assets/Plugins/iOS/NativeCallProxy.mm index 55661e7..38ce685 100644 --- a/unity/Assets/Plugins/iOS/NativeCallProxy.mm +++ b/unity/Assets/Plugins/iOS/NativeCallProxy.mm @@ -9,12 +9,19 @@ +(void) registerAPIforNativeCalls:(id) aApi api = aApi; } ++(void) unregisterAPIforNativeCalls +{ + api = NULL; +} + @end extern "C" { void sendMessageToMobileApp(const char* message) { - return [api sendMessageToMobileApp:[NSString stringWithUTF8String:message]]; + if (api != NULL) { + [api sendMessageToMobileApp:[NSString stringWithUTF8String:message]]; + } } }