diff --git a/ios/RNUnity.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/RNUnity.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/RNUnity.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/RNUnity/RNUnity.xcodeproj/project.pbxproj b/ios/RNUnity/RNUnity.xcodeproj/project.pbxproj index 92b8359..f7f985d 100644 --- a/ios/RNUnity/RNUnity.xcodeproj/project.pbxproj +++ b/ios/RNUnity/RNUnity.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 0053E483231940930014F962 /* UnityResponderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0053E482231940930014F962 /* UnityResponderView.m */; }; 0053E4862319420A0014F962 /* UnityResponderViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0053E4852319420A0014F962 /* UnityResponderViewManager.m */; }; + 7261D468277A43BA00D323AC /* UnityNativeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 7261D467277A43BA00D323AC /* UnityNativeModule.m */; }; + 7261D46B277A43FD00D323AC /* UnityUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7261D46A277A43FD00D323AC /* UnityUtils.mm */; }; B3E7B58A1CC2AC0600A0062D /* RNUnity.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNUnity.m */; }; /* End PBXBuildFile section */ @@ -30,6 +32,10 @@ 0053E4852319420A0014F962 /* UnityResponderViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnityResponderViewManager.m; sourceTree = ""; }; 0053E4872319422D0014F962 /* UnityResponderViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnityResponderViewManager.h; sourceTree = ""; }; 134814201AA4EA6300B7C361 /* libRNUnity.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNUnity.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 7261D466277A438500D323AC /* UnityNativeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnityNativeModule.h; sourceTree = ""; }; + 7261D467277A43BA00D323AC /* UnityNativeModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnityNativeModule.m; sourceTree = ""; }; + 7261D469277A43E800D323AC /* UnityUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnityUtils.h; sourceTree = ""; }; + 7261D46A277A43FD00D323AC /* UnityUtils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = UnityUtils.mm; sourceTree = ""; }; B3E7B5881CC2AC0600A0062D /* RNUnity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNUnity.h; sourceTree = ""; }; B3E7B5891CC2AC0600A0062D /* RNUnity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNUnity.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -56,6 +62,10 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( + 7261D46A277A43FD00D323AC /* UnityUtils.mm */, + 7261D469277A43E800D323AC /* UnityUtils.h */, + 7261D466277A438500D323AC /* UnityNativeModule.h */, + 7261D467277A43BA00D323AC /* UnityNativeModule.m */, 0053E4872319422D0014F962 /* UnityResponderViewManager.h */, 0053E4852319420A0014F962 /* UnityResponderViewManager.m */, 0053E4842319410E0014F962 /* UnityResponderView.h */, @@ -105,6 +115,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 58B511D21A9E6C8500147676; @@ -122,9 +133,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7261D468277A43BA00D323AC /* UnityNativeModule.m in Sources */, B3E7B58A1CC2AC0600A0062D /* RNUnity.m in Sources */, 0053E4862319420A0014F962 /* UnityResponderViewManager.m in Sources */, 0053E483231940930014F962 /* UnityResponderView.m in Sources */, + 7261D46B277A43FD00D323AC /* UnityUtils.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/RNUnity/UnityNativeModule.h b/ios/RNUnity/UnityNativeModule.h new file mode 100644 index 0000000..34f3d4e --- /dev/null +++ b/ios/RNUnity/UnityNativeModule.h @@ -0,0 +1,11 @@ +// +// UnityNativeModule.h +// RNUnity +// + +#import +#import +#import "UnityUtils.h" + +@interface UnityNativeModule : NSObject +@end diff --git a/ios/RNUnity/UnityNativeModule.m b/ios/RNUnity/UnityNativeModule.m new file mode 100644 index 0000000..bfe7ab7 --- /dev/null +++ b/ios/RNUnity/UnityNativeModule.m @@ -0,0 +1,68 @@ +// +// UnityNativeModule.m +// RNUnityView +// + +#import "UnityNativeModule.h" + +@implementation UnityNativeModule + +@synthesize bridge = _bridge; + +RCT_EXPORT_MODULE(UnityNativeModule); + +- (id)init +{ + self = [super init]; + if (self) { + [UnityUtils addUnityEventListener:self]; + } + return self; +} + +- (NSArray *)supportedEvents +{ + return @[@"onUnityMessage"]; +} + ++ (BOOL)requiresMainQueueSetup +{ + return YES; +} + +RCT_EXPORT_METHOD(isReady:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +{ + resolve(@([UnityUtils isUnityReady])); +} + +RCT_EXPORT_METHOD(createUnity:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +{ + [UnityUtils createPlayer:^{ + resolve(@(YES)); + }]; +} + +RCT_EXPORT_METHOD(postMessage:(NSString *)gameObject methodName:(NSString *)methodName message:(NSString *)message) +{ + UnityPostMessage(gameObject, methodName, message); +} + +RCT_EXPORT_METHOD(pause) +{ + UnityPauseCommand(); +} + +RCT_EXPORT_METHOD(resume) +{ + UnityResumeCommand(); +} + +- (void)onMessage:(NSString *)message { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_bridge.eventDispatcher sendDeviceEventWithName:@"onUnityMessage" + body:message]; +#pragma clang diagnostic pop +} + +@end diff --git a/ios/RNUnity/UnityUtils.h b/ios/RNUnity/UnityUtils.h new file mode 100644 index 0000000..d6a33d6 --- /dev/null +++ b/ios/RNUnity/UnityUtils.h @@ -0,0 +1,39 @@ +#import + +#ifndef UnityUtils_h +#define UnityUtils_h + +#ifdef __cplusplus +extern "C" { +#endif + +void InitArgs(int argc, char* argv[]); + +bool UnityIsInited(void); + +void InitUnity(); + +void UnityPostMessage(NSString* gameObject, NSString* methodName, NSString* message); + +void UnityPauseCommand(); + +void UnityResumeCommand(); + +#ifdef __cplusplus +} // extern "C" +#endif + +@protocol UnityEventListener +- (void)onMessage:(NSString *)message; +@end + +@interface UnityUtils : NSObject + ++ (BOOL)isUnityReady; ++ (void)createPlayer:(void (^)(void))completed; ++ (void)addUnityEventListener:(id)listener; ++ (void)removeUnityEventListener:(id)listener; + +@end + +#endif /* UnityUtils_h */ diff --git a/ios/RNUnity/UnityUtils.mm b/ios/RNUnity/UnityUtils.mm new file mode 100644 index 0000000..ee1c2ad --- /dev/null +++ b/ios/RNUnity/UnityUtils.mm @@ -0,0 +1,176 @@ +#include "RegisterMonoModules.h" +#include "RegisterFeatures.h" +#include +#import +#import "UnityInterface.h" +#import "UnityUtils.h" +#import "UnityAppController.h" + +// Hack to work around iOS SDK 4.3 linker problem +// we need at least one __TEXT, __const section entry in main application .o files +// to get this section emitted at right time and so avoid LC_ENCRYPTION_INFO size miscalculation +static const int constsection = 0; + +bool unity_inited = false; + +int g_argc; +char** g_argv; + +void UnityInitTrampoline(); + +extern "C" void InitArgs(int argc, char* argv[]) +{ + g_argc = argc; + g_argv = argv; +} + +extern "C" bool UnityIsInited() +{ + return unity_inited; +} + +extern "C" void InitUnity() +{ + if (unity_inited) { + return; + } + unity_inited = true; + + UnityInitStartupTime(); + + @autoreleasepool + { + UnityInitTrampoline(); + UnityInitRuntime(g_argc, g_argv); + + RegisterMonoModules(); + NSLog(@"-> registered mono modules %p\n", &constsection); + RegisterFeatures(); + + // iOS terminates open sockets when an application enters background mode. + // The next write to any of such socket causes SIGPIPE signal being raised, + // even if the request has been done from scripting side. This disables the + // signal and allows Mono to throw a proper C# exception. + std::signal(SIGPIPE, SIG_IGN); + } +} + +extern "C" void UnityPostMessage(NSString* gameObject, NSString* methodName, NSString* message) +{ + UnitySendMessage([gameObject UTF8String], [methodName UTF8String], [message UTF8String]); +} + +extern "C" void UnityPauseCommand() +{ + dispatch_async(dispatch_get_main_queue(), ^{ + UnityPause(1); + }); +} + +extern "C" void UnityResumeCommand() +{ + dispatch_async(dispatch_get_main_queue(), ^{ + UnityPause(0); + }); +} + +@implementation UnityUtils + +static NSHashTable* mUnityEventListeners = [NSHashTable weakObjectsHashTable]; +static BOOL _isUnityReady = NO; + ++ (BOOL)isUnityReady +{ + return _isUnityReady; +} + ++ (void)handleAppStateDidChange:(NSNotification *)notification +{ + if (!_isUnityReady) { + return; + } + UnityAppController* unityAppController = GetAppController(); + + UIApplication* application = [UIApplication sharedApplication]; + + if ([notification.name isEqualToString:UIApplicationWillResignActiveNotification]) { + [unityAppController applicationWillResignActive:application]; + } else if ([notification.name isEqualToString:UIApplicationDidEnterBackgroundNotification]) { + [unityAppController applicationDidEnterBackground:application]; + } else if ([notification.name isEqualToString:UIApplicationWillEnterForegroundNotification]) { + [unityAppController applicationWillEnterForeground:application]; + } else if ([notification.name isEqualToString:UIApplicationDidBecomeActiveNotification]) { + [unityAppController applicationDidBecomeActive:application]; + } else if ([notification.name isEqualToString:UIApplicationWillTerminateNotification]) { + [unityAppController applicationWillTerminate:application]; + } else if ([notification.name isEqualToString:UIApplicationDidReceiveMemoryWarningNotification]) { + [unityAppController applicationDidReceiveMemoryWarning:application]; + } +} + ++ (void)listenAppState +{ + for (NSString *name in @[UIApplicationDidBecomeActiveNotification, + UIApplicationDidEnterBackgroundNotification, + UIApplicationWillTerminateNotification, + UIApplicationWillResignActiveNotification, + UIApplicationWillEnterForegroundNotification, + UIApplicationDidReceiveMemoryWarningNotification]) { + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleAppStateDidChange:) + name:name + object:nil]; + } +} + ++ (void)createPlayer:(void (^)(void))completed +{ + if (_isUnityReady) { + completed(); + return; + } + + [[NSNotificationCenter defaultCenter] addObserverForName:@"UnityReady" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { + _isUnityReady = YES; + completed(); + }]; + + if (UnityIsInited()) { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + UIApplication* application = [UIApplication sharedApplication]; + + // Always keep RN window in top + application.keyWindow.windowLevel = UIWindowLevelNormal + 1; + + InitUnity(); + + UnityAppController *controller = GetAppController(); + [controller application:application didFinishLaunchingWithOptions:nil]; + [controller applicationDidBecomeActive:application]; + + [UnityUtils listenAppState]; + }); +} + +extern "C" void onUnityMessage(const char* message) +{ + for (id listener in mUnityEventListeners) { + [listener onMessage:[NSString stringWithUTF8String:message]]; + } +} + ++ (void)addUnityEventListener:(id)listener +{ + [mUnityEventListeners addObject:listener]; +} + ++ (void)removeUnityEventListener:(id)listener +{ + [mUnityEventListeners removeObject:listener]; +} + +@end diff --git a/package.json b/package.json index 6a1e10b..c570122 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-unity-play", - "version": "0.1.6", + "version": "0.1.7", "description": "React Native package to use Unity as library with React Native app", "main": "index.js", "scripts": {