Skip to content

Instantly share code, notes, and snippets.

@capnslipp
Last active December 5, 2019 15:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save capnslipp/036f7c98f5ccf42e8428 to your computer and use it in GitHub Desktop.
Save capnslipp/036f7c98f5ccf42e8428 to your computer and use it in GitHub Desktop.
iOS AppDelegate UINotifications, in Unity MonoBehaviour scripts
/// @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");
}
}
/// @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
/// @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
/// @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);
}
}
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