Last active
December 5, 2019 15:53
-
-
Save capnslipp/036f7c98f5ccf42e8428 to your computer and use it in GitHub Desktop.
iOS AppDelegate UINotifications, in Unity MonoBehaviour scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// @creator: Slipp Douglas Thompson | |
/// @license: Public Domain per The Unlicense. See <http://unlicense.org/>. | |
/// @why: Because this functionality should be built-into Unity. | |
/// @intended project path: Assets/Plugins/iOS-Bridge/AppDelegateLoudspeaker.cs | |
/// @intersource: https://gist.github.com/capnslipp/036f7c98f5ccf42e8428 | |
using UnityEngine; | |
/// @note: Using direct 1-to-1 handlers/calls here (instead of a single method taking a string arg) for a few of reasons: | |
/// 1. There's a fixed number of these and they'll change only infrequently, if ever. | |
/// • If notifications were to be added/removed, we'd have to also add/remove a method in AppDelegateLoudspeaker.m— and one parallel change per file seems pretty reasonable. | |
/// 2. If bad something happens, we'll see the exact name of the notification in the backtrace. | |
/// 3. The C# compiler [should be able to] optimize these non-variable use-cases better, which is important because we already know the `ComponentBroadcastAllExtension.BroadcastAllMessage()` call is spendy. | |
static class AppDelegateLoudspeaker | |
{ | |
// Native-Side Notification Handlers | |
static void _BroadcastUIApplicationDidRegisterForRemoteNotificationsNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidRegisterForRemoteNotificationsNotification"); | |
} | |
static void _BroadcastUIApplicationDidFailToRegisterForRemoteNotificationsNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidFailToRegisterForRemoteNotificationsNotification"); | |
} | |
static void _BroadcastUIApplicationDidReceiveRemoteNotificationNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidReceiveRemoteNotificationNotification"); | |
} | |
static void _BroadcastUIApplicationDidReceiveLocalNotificationNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidReceiveLocalNotificationNotification"); | |
} | |
static void _BroadcastUIApplicationDidOpenURL() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidOpenURL"); | |
} | |
static void _BroadcastUIApplicationDidReceiveMemoryWarningNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidReceiveMemoryWarningNotification"); | |
} | |
static void _BroadcastUIApplicationSignificantTimeChangeNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationSignificantTimeChangeNotification"); | |
} | |
static void _BroadcastUIApplicationWillChangeStatusBarFrameNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationWillChangeStatusBarFrameNotification"); | |
} | |
static void _BroadcastUIApplicationWillChangeStatusBarOrientationNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationWillChangeStatusBarOrientationNotification"); | |
} | |
static void _BroadcastUIApplicationDidFinishLaunchingNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidFinishLaunchingNotification"); | |
} | |
static void _BroadcastUIApplicationDidBecomeActiveNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidBecomeActiveNotification"); | |
} | |
static void _BroadcastUIApplicationWillResignActiveNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationWillResignActiveNotification"); | |
} | |
static void _BroadcastUIApplicationDidEnterBackgroundNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationDidEnterBackgroundNotification"); | |
} | |
static void _BroadcastUIApplicationWillEnterForegroundNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationWillEnterForegroundNotification"); | |
} | |
static void _BroadcastUIApplicationWillTerminateNotification() { | |
ComponentBroadcastAllExtension.BroadcastAllMessage("OnUIApplicationWillTerminateNotification"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// @creator: Slipp Douglas Thompson | |
/// @license: Public Domain per The Unlicense. See <http://unlicense.org/>. | |
/// @why: Because this functionality should be built-into Unity. | |
/// @intended project path: Assets/Plugins/iOS/AppDelegateLoudspeaker.h | |
/// @intersource: https://gist.github.com/capnslipp/036f7c98f5ccf42e8428 | |
#pragma once | |
#include "PluginBase/AppDelegateListener.h" | |
@interface AppDelegateLoudspeaker : NSObject<AppDelegateListener> | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// @creator: Slipp Douglas Thompson | |
/// @license: Public Domain per The Unlicense. See <http://unlicense.org/>. | |
/// @why: Because this functionality should be built-into Unity. | |
/// @intended project path: Assets/Plugins/iOS/AppDelegateLoudspeaker.m | |
/// @intersource: https://gist.github.com/capnslipp/036f7c98f5ccf42e8428 | |
#import "AppDelegateLoudspeaker.h" | |
#pragma mark Mono Type and Function Prototypes/Forward Declarations | |
typedef void* MonoDomain; | |
typedef void* MonoAssembly; | |
typedef void* MonoImage; | |
typedef void* MonoClass; | |
typedef void* MonoObject; | |
typedef void* MonoMethodDesc; | |
typedef void* MonoMethod; | |
typedef void* gpointer; | |
typedef int gboolean; | |
MonoDomain * mono_domain_get(); | |
MonoAssembly * mono_domain_assembly_open(MonoDomain *domain, const char *assemblyName); | |
MonoImage * mono_assembly_get_image(MonoAssembly *assembly); | |
MonoMethodDesc * mono_method_desc_new(const char *methodString, gboolean useNamespace); | |
MonoMethodDesc * mono_method_desc_free(MonoMethodDesc *desc); | |
MonoMethod * mono_method_desc_search_in_image(MonoMethodDesc *methodDesc, MonoImage *image); | |
MonoObject * mono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc); | |
gpointer mono_object_unbox(MonoObject *obj); | |
#pragma mark AppDelegateLoudspeaker | |
@interface AppDelegateLoudspeaker () { | |
MonoImage *_monoImage; | |
} | |
- (MonoImage *)fetchMonoImage; | |
@end | |
@implementation AppDelegateLoudspeaker | |
#pragma mark Create/Destroy | |
- (id)init | |
{ | |
self = [super init]; | |
if (!self) | |
return nil; | |
_monoImage = [self fetchMonoImage]; | |
UnityRegisterAppDelegateListener(self); | |
return self; | |
} | |
- (MonoImage *)fetchMonoImage | |
{ | |
MonoDomain *domain = mono_domain_get(); | |
NSString *assemblyPath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"Data/Managed/Assembly-CSharp-firstpass.dll"]; | |
MonoAssembly *assembly = mono_domain_assembly_open(domain, assemblyPath.UTF8String); | |
MonoImage *image = mono_assembly_get_image(assembly); | |
return image; | |
} | |
- (void)dealloc | |
{ | |
UnityUnregisterAppDelegateListener(self); | |
_monoImage = NULL; | |
#if !__has_feature(objc_arc) | |
[super dealloc]; | |
#endif | |
} | |
#pragma mark Util | |
/// @arg methodName: The title-case name the notification method on the C#-side AppDelegateLoudspeaker class. | |
- (void)monoInvokeLoudspeakerNotification:(NSString *)notificationName | |
{ | |
NSString *methodDescStr = [[NSString alloc] initWithFormat:@"%@:_Broadcast%@()", NSStringFromClass(self.class), notificationName]; | |
MonoMethodDesc *methodDesc = mono_method_desc_new(methodDescStr.UTF8String, FALSE); | |
MonoMethod *method = mono_method_desc_search_in_image(methodDesc, _monoImage); | |
mono_method_desc_free(methodDesc); | |
#if !__has_feature(objc_arc) | |
[methodDescStr release]; | |
#endif | |
mono_runtime_invoke(method, NULL /* obj */, NULL /* params */, NULL /* exc */); | |
} | |
#pragma mark AppDelegateListener Handlers | |
- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidRegisterForRemoteNotificationsNotification"]; | |
} | |
- (void)didFailToRegisterForRemoteNotificationsWithError:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidFailToRegisterForRemoteNotificationsNotification"]; | |
} | |
- (void)didReceiveRemoteNotification:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidReceiveRemoteNotificationNotification"]; | |
} | |
- (void)didReceiveLocalNotification:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidReceiveLocalNotificationNotification"]; | |
} | |
- (void)onOpenURL:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidOpenURL"]; | |
} | |
- (void)applicationDidReceiveMemoryWarning:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidReceiveMemoryWarningNotification"]; | |
} | |
- (void)applicationSignificantTimeChange:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationSignificantTimeChangeNotification"]; | |
} | |
- (void)applicationWillChangeStatusBarFrame:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationWillChangeStatusBarFrameNotification"]; | |
} | |
- (void)applicationWillChangeStatusBarOrientation:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationWillChangeStatusBarOrientationNotification"]; | |
} | |
#pragma mark LifeCycleListener Handlers | |
- (void)didFinishLaunching:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidFinishLaunchingNotification"]; | |
} | |
- (void)didBecomeActive:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidBecomeActiveNotification"]; | |
} | |
- (void)willResignActive:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationWillResignActiveNotification"]; | |
} | |
- (void)didEnterBackground:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationDidEnterBackgroundNotification"]; | |
} | |
- (void)willEnterForeground:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationWillEnterForegroundNotification"]; | |
} | |
- (void)willTerminate:(NSNotification*)notification { | |
[self monoInvokeLoudspeakerNotification:@"UIApplicationWillTerminateNotification"]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// @creator: Slipp Douglas Thompson | |
/// @license: Public Domain per The Unlicense. See <http://unlicense.org/>. | |
/// @why: Because this functionality should be built-into Unity. | |
/// @intended project path: Assets/Plugins/UnityEngine Extensions/ComponentBroadcastAllExtensions.cs | |
/// @intersource: https://gist.github.com/capnslipp/036f7c98f5ccf42e8428 | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
using UObject=UnityEngine.Object; | |
public static class ComponentBroadcastAllExtension | |
{ | |
/// @todo: Profile to see if it would be better to just SendMessage to every Transform rather than Where for the roots. | |
static Transform[] GetAllRootTransforms() | |
{ | |
Transform[] allTransforms = UObject.FindObjectsOfType<Transform>(); | |
Transform[] rootTransforms = new List<Transform>(allTransforms).Where(t => (t.parent == null)).ToArray(); | |
return rootTransforms; | |
} | |
/// Rather expensive (because of GetAllRootTransforms()). Use sparingly. | |
public static void BroadcastAllMessage(this Component @this, string methodName) { | |
BroadcastAllMessage(methodName); | |
} | |
/// Rather expensive (because of GetAllRootTransforms()). Use sparingly. | |
/// Assumes SendMessageOptions.DontRequireReceiver because checking if any one of the root GameObjects has it would be a PitA (because Unity doesn't use normal catchable exceptions). | |
public static void BroadcastAllMessage(string methodName) | |
{ | |
Component[] roots = GetAllRootTransforms(); | |
foreach (Component root in roots) | |
root.BroadcastMessage(methodName, SendMessageOptions.DontRequireReceiver); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
public class UsageExample : MonoBehaviour | |
{ | |
void OnUIApplicationWillResignActiveNotification() | |
{ | |
Debug.Log("UIApplicationWillResignActiveNotification!"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment