Override app delegate in Unity for iOS and OSX (3/4)
Method swizzling

In the previous part of the tutorial series – Override app delegate in Unity for iOS and OSX (2/4) Notification Center – we override app delegate using notification center. As that method has some limitations, now we use method swizzling to extend app delegate in a safe but flexible way. This part of the series will be probably the most code intensive, as we add numerous new features to our plugin (as we do a bit of cleanup as well).

TL;DR

You may get the example project immediately by heading to GitHub (in that case you should really be aware of the folder structure at the end of the article, as there are numerous sub-projects can be found all around). However, I strongly recommend to take the next 15 minutes to understand what is going on, and read along (so the prerequisites).

See Unity.Blog.Override_App_Delegate at

Method swizzling in iOS

The Objective-C runtime is much like C# Reflection. From wikipedia Reflection article “…the ability of a computer program to examine, introspect, and modify its own structure and behavior at runtime…”. We gonna use the modify its own structure part.

Creating method implementations at runtime is difficult, but moving method implementations is way more easy (it is actually just switching the labels on ready-made methods). Years ago I already created a library called eppz! swizzler, that has two important feature we can use here. It can swap method implementations within a class, or swap between two classes (having the same signature of course).

Having this, if we create a distinct app delegate class with the features we want to use, we could gently swap the corresponding implementations with the Unity iOS / OSX player app delegate. Then simply call the original Unity implementation (that should be saved somewhere).

Override app delegate using method swizzling

Create a simple class OverrideAppDelegate, like it was already your new app delegate class. Take a look at the exact signature of the method you are going to override, this case it is application:didFinishLaunchingWithOptions:, and create an implementation. Also, create another method that will hold the original Unity application:didFinishLaunchingWithOptions: implementation. Make sure you come up with a unique method name here, as it should not be conflicted with other plugins, I chose _original_saved_by_Override_application:didFinishLaunchingWithOptions:.

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>


@interface OverrideAppDelegate : NSObject


-(BOOL)application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions;
-(BOOL)_original_saved_by_Override_application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions;


@end
See OverrideAppDelegate.h at
#import "OverrideAppDelegate.h"


@implementation OverrideAppDelegate


-(BOOL)application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions
{
    NSLog(@"[OverrideAppDelegate application:%@ didFinishLaunchingWithOptions:%@]", application, launchOptions);
    return [self _original_saved_by_Override_application:application didFinishLaunchingWithOptions:launchOptions];
}

-(BOOL)_original_saved_by_Override_application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions
{
    // Yet empty (original Unity implementation will be copied here).
    return YES;
}


@end
See OverrideAppDelegate.m at

There you have it, now you can use eppz! swizzler to actually move the methods around once the application loads the classes. In Override_iOS you can have all your swizzling code in one place, as we already made it to be loaded (some code hooked up to getMessage may hang around there for a while). So in swizzle you can implement actual swizzling (read in-line comments for more).

#import "Override_iOS.h"


__strong Override_iOS *_instance;



@implementation Override_iOS


+(void)load
{
    NSLog(@"[Override_iOS load]");
    [self swizzle];
    
    _instance = [Override_iOS new];
    _instance.URL = @"Greetings from iOS!";
}

+(Override_iOS*)instance
{ return _instance; }

+(void)swizzle
{
    NSLog(@"[Override_iOS swizzle]");
    
    // The Unity base app controller class (the class name stored in AppControllerClassName`).
    Class unityAppDelegate = NSClassFromString(@"UnityAppController");
    Class overrideAppDelegate = OverrideAppDelegate.class;
    
    // See log messages for the sake of this tutorial.
    [EPPZSwizzler setLogging:YES];

    // Add empty placholder to Unity app delegate.
    [EPPZSwizzler addInstanceMethod:@selector(_original_saved_by_Override_application:didFinishLaunchingWithOptions:)
                            toClass:unityAppDelegate
                          fromClass:overrideAppDelegate];
    
    // Save the original Unity app delegate implementation into.
    [EPPZSwizzler swapInstanceMethod:@selector(_original_saved_by_Override_application:didFinishLaunchingWithOptions:)
                  withInstanceMethod:@selector(application:didFinishLaunchingWithOptions:)
                             ofClass:unityAppDelegate];
    
    // Replace Unity app delegate with ours.
    [EPPZSwizzler replaceInstanceMethod:@selector(application:didFinishLaunchingWithOptions:)
                                ofClass:unityAppDelegate
                              fromClass:overrideAppDelegate];
}


@end
See Override_iOS.m at

May seem a bit confusing at first, but this way you can save the original implementation as well, instead of simply replace it. You can invoke the original implementation once you have done with your business (see line 23 in OverrideAppDelegate.m). You can see now, that using method swizzling enables you to actually override methods in a class hierarchy, without assuming a single thing about how it is further subclassed. These modifications will be now part of UnityAppController, like it was already implemented that way. So using method swizzling you won’t break the class hierarchy in any way.

If you run the built Unity Xcode project, you will see that overriding was just fine in console. More to that, you have your implementation called before (!) the Unity implementation (also you can see what EPPZSwizzler done in the background for you).

If you have setup the suggested additional Copy Files Phase in the library project, you can go along with this part of tutorial series even without start Unity Editor itself.

Unity iOS Player Console Log applicationDidFinishLaunching swizzle

Override Unity app delegate URL scheme (deep linking) methods

Back to deep linking, there are a couple more methods you want to override if dealing with URL schemes. Use application:openURL:options: for iOS 9.0 and application:openURL:sourceApplication:annotation: for backward compatibility down to iOS 4.2. Also, Unity iOS player uses the latter. The very same method we just hotfixed in the previous part of the tutorial series. Using swizzling, we can prevent the unwanted null reference exception simply by provide an empty dictionary as annotation, so the hotfix won’t be needed anymore. As a side-effect, we fixed our workflow as well.

As we want to override more and more method, first we should extract the swizzling sequence we have made before. It became somewhat longish indeed, but ultimately saves some redundancy in the long run.

...
+(void)swizzle
{
    NSLog(@"[Override_iOS swizzle]");
    [self replaceAppDelegateMethod:@selector(application:didFinishLaunchingWithOptions:)
                         fromClass:OverrideAppDelegate.class
                  savingOriginalTo:@selector(_original_saved_by_Override_application:didFinishLaunchingWithOptions:)];
}

+(void)replaceAppDelegateMethod:(SEL) unitySelector
                      fromClass:(Class) overrideAppDelegate
               savingOriginalTo:(SEL) savingOriginalSelector
{
    // The Unity base app controller class (the class name stored in `AppControllerClassName).
    Class unityAppDelegate = NSClassFromString(@"UnityAppController");
    
    // See log messages for the sake of this tutorial.
    [EPPZSwizzler setLogging:YES];
    
    // Add empty placholder to Unity app delegate.
    [EPPZSwizzler addInstanceMethod:savingOriginalSelector
                            toClass:unityAppDelegate
                          fromClass:overrideAppDelegate];
    
    // Save the original Unity app delegate implementation into.
    [EPPZSwizzler swapInstanceMethod:savingOriginalSelector
                  withInstanceMethod:unitySelector
                             ofClass:unityAppDelegate];
    
    // Replace Unity app delegate with ours.
    [EPPZSwizzler replaceInstanceMethod:unitySelector
                                ofClass:unityAppDelegate
                              fromClass:overrideAppDelegate];
}
...
See Override_iOS.m at

Now we should add the methods we want to override (and their original implementation placeholders) to our app delegate class OverrideAppDelegate.

#import "OverrideAppDelegate.h"


@implementation OverrideAppDelegate


#pragma mark Override implementations

-(BOOL)application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions
{
    NSLog(@"[OverrideAppDelegate application:%@ didFinishLaunchingWithOptions:%@]", application, launchOptions);
    return [self _original_saved_by_Override_application:application
                           didFinishLaunchingWithOptions:launchOptions];
}

-(BOOL)application:(UIApplication*) application
           openURL:(NSURL*) url
           options:(NSDictionary*) options
{
    NSLog(@"[OverrideAppDelegate application:%@ openURL:%@ options:%@]", application, url, options);
    return [self _original_saved_by_Override_application:application
                                                 openURL:url
                                                 options:options];
}

-(BOOL)application:(UIApplication*) application
           openURL:(NSURL*) url
 sourceApplication:(NSString*) sourceApplication
        annotation:(id) annotation
{
    NSLog(@"[OverrideAppDelegate application:%@ openURL:%@ sourceApplication:%@ annotation:%@]", application, url, sourceApplication, annotation);
    return [self _original_saved_by_Override_application:application
                                                 openURL:url
                                       sourceApplication:sourceApplication
                                              annotation:(annotation) ? annotation : [NSDictionary new]];
}


#pragma mark Original implementation placeholders

-(BOOL)_original_saved_by_Override_application:(UIApplication*) application
                 didFinishLaunchingWithOptions:(NSDictionary*) launchOptions
{ return YES; }


-(BOOL)_original_saved_by_Override_application:(UIApplication*) application
                                       openURL:(NSURL*) url
                                       options:(NSDictionary*) options
{ return YES; }


-(BOOL)_original_saved_by_Override_application:(UIApplication*) application
                                       openURL:(NSURL*) url
                             sourceApplication:(NSString*) sourceApplication
                                    annotation:(id) annotation
{ return YES; }


@end
See OverrideAppDelegate.m at

Then do the actual swizzlings in our swizzle method back in Override_iOS class.

...
+(void)swizzle
{
    NSLog(@"[Override_iOS swizzle]");
    
    [self replaceAppDelegateMethod:@selector(application:didFinishLaunchingWithOptions:)
                         fromClass:OverrideAppDelegate.class
                  savingOriginalTo:@selector(_original_saved_by_Override_application:didFinishLaunchingWithOptions:)];
    
    [self replaceAppDelegateMethod:@selector(application:openURL:options:)
                         fromClass:OverrideAppDelegate.class
                  savingOriginalTo:@selector(_original_saved_by_Override_application:openURL:options:)];
    
    [self replaceAppDelegateMethod:@selector(application:openURL:sourceApplication:annotation:)
                         fromClass:OverrideAppDelegate.class
                  savingOriginalTo:@selector(_original_saved_by_Override_application:openURL:sourceApplication:annotation:)];
}
...
See Override_iOS.m at

With overriding the designated URL scheme methods above, we can see the URL schemes working even if we don’t stop / relaunch the application. Build the plugin, then run the Unity iOS player application. Press (well, simulate) the Home Button in the Simulator ⇧+⌘+H (you may also Clear the Console in Xcode). Switch to Mobile Safari right in the Simulator, and type some link with the corresponding URL scheme: override://Greetings+from+Safari!, switch again to Mobile Safari, then try with another message. You can see the messages changing, however, on the Console Log only for now.

Extract deep link data management to a separate class

To Finish this part of the tutorial series up, let’s hookup the incoming messages to the UI within our Unity scene. As storing the deep link payload has actually nothing to do with swizzling, I found it suitable to extract to a new class called DeepLink. We can also move the singleton implementation from Override_iOS there (so Override_iOS doesn’t have to be singleton anymore).

As you are about to add features to your deep linking mechanisms, a separate class is will be more convenient place for it to grow. Also, you should create the C# counterpart DeepLink.cs as well in Unity, so you can start hooking up deep linking functionality to you app from there (that means you can also remove Override_C++.mm file from the project).

#import <Foundation/Foundation.h>


@interface DeepLink : NSObject


@property (nonatomic, strong) NSString *URL;
@property (nonatomic, strong) NSString *sourceAppliaction;
@property (nonatomic, strong) id annotation;

+(DeepLink*)instance;
-(void)reset;


@end
See DeepLink.h at
#import "DeepLink.h"


__strong DeepLink *_instance;


@implementation DeepLink


+(void)load
{
    NSLog(@"[DeepLink load]");
    _instance = [DeepLink new];
}

+(DeepLink*)instance
{ return _instance; }

-(instancetype)init
{
    self = [super init];
    if (self) [self reset];
    return self;
}

-(void)reset
{
    self.URL = [NSString new];
    self.sourceAppliaction = [NSString new];
    self.annotation = [NSDictionary new];
}

@end
See DeepLink.m at
#import "UnityString_C++.mm"
#import "DeepLink.h"


extern "C"
{
    
    void DeepLink_Reset()
    { return [[DeepLink instance] reset]; }

    const char* DeepLink_GetURL()
    { return UnityStringFromNSString([[DeepLink instance] URL]); }
    
    const char* DeepLink_GetSourceApplication()
    { return UnityStringFromNSString([[DeepLink instance] sourceAppliaction]); }
    
    
}
See DeepLink_C++.mm at

It is actually now just simply holds data statically, and provides a static interface towards Unity. In addition, it exposes a reset functionality to mark the instance that its data has been consumed. In the corresponding OverrideAppDelegate methods you can now feed the data into the singleton.

...
-(BOOL)application:(UIApplication*) application
           openURL:(NSURL*) url
           options:(NSDictionary*) options
{
    NSLog(@"[OverrideAppDelegate application:%@ openURL:%@ options:%@]", application, url, options);
    [DeepLink instance].URL = url.absoluteString;
    [DeepLink instance].sourceAppliaction = options[UIApplicationOpenURLOptionsSourceApplicationKey];
    [DeepLink instance].annotation = options[UIApplicationOpenURLOptionsAnnotationKey];
    return [self _original_saved_by_Override_application:application
                                                 openURL:url
                                                 options:options];
}

-(BOOL)application:(UIApplication*) application
           openURL:(NSURL*) url
 sourceApplication:(NSString*) sourceApplication
        annotation:(id) annotation
{
    NSLog(@"[OverrideAppDelegate application:%@ openURL:%@ sourceApplication:%@ annotation:%@]", application, url, sourceApplication, annotation);
    [DeepLink instance].URL = url.absoluteString;
    [DeepLink instance].sourceAppliaction = sourceApplication;
    [DeepLink instance].annotation = annotation;
    return [self _original_saved_by_Override_application:application
                                                 openURL:url
                                       sourceApplication:sourceApplication
                                              annotation:(annotation) ? annotation : [NSDictionary new]];
}
...
See OverrideAppDelegate.m at

The last piece for this part of the tutorial series is to create the C# counterpart. Actually very similar to what we had before, but as URL scheme opening can be happened while the app is running as well, here we keep polling for any new value on each frame. I’m not a big fan of polled events like this, but this code is short (and works for OSX as well), so in this tutorial series it will do the job just fine.

using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;


public class DeepLink : MonoBehaviour
{


	public Text label;


#if !UNITY_EDITOR && UNITY_IOS

	[DllImport("__Internal")]
	static extern void DeepLink_Reset();

	[DllImport("__Internal")]
	static extern string DeepLink_GetURL();	

	[DllImport("__Internal")]
	static extern string DeepLink_GetSourceApplication();		


	void Update()	
	{
		if (DeepLink_GetURL() == "") return;
		label.text = DeepLink_GetURL().Replace("override://", "");
		DeepLink_Reset();
	}

#endif


}
See DeepLink.cs at

Quick recap how we override app delegate in Unity for iOS

In the end, we have a really extensible plugin setup now for override app delegate in Unity. Every override releated code is nicely packed up to a separate iOS library project, resulting in a single file in Unity Assets folder. As a bonus, we can iterate plugin code (build the application in Xcode) without even starting Unity Editor. We can catch every incoming deep link URL scheme deep link. All regardless of iOS versions, regardless of the application has exited or just went to background. It does not need any additional Xcode project post processing hack. As a bonus, we fixed a Unity null reference issue when launching from Safari.

And the best of all, it can be safely used with any other plugin (!). This is the very reason we used method swizzling for override app delegate.

The other reason I chose to go with method swizzling is OSX. At the time of this writeup Unity OSX Standalone player source code is closed. I tried my best to get a Unity OSX Standalone Player Xcode project, contacted the hell out of Unity, without a single result. But for continue with override app delegate in Unity OSX player in the next part – Override app delegate in Unity for iOS and OSX (4/4) Inject code to Mach-O binary -, we can eventually use the very same codebase we established here.

DISCLAIMER. THE INFORMATION ON THIS BLOG (INCLUDING BUT NOT LIMITED TO ARTICLES, IMAGES, CODE SNIPPETS, SOURCE CODES, EXAMPLE PROJECTS, ETC.) IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE INFORMATION ON THIS BLOG (INCLUDING BUT NOT LIMITED TO ARTICLES, IMAGES, CODE SNIPPETS, SOURCE CODES, EXAMPLE PROJECTS, ETC.).

  • Alex

    Very useful! Thanks.

    • Wow, glad you like it. 👊
      I’m also putting this in production this week.

  • Hey so once I do the deeplinking or add anything to the Ovveride_iOS.m In unity xcode project I’m getting mach-o linker error, saying that I have duplicate symbols for _NSStringFromUnityString in Override_ios and OverrideAppDelegate.

    I’ve solved it by simply inlining the method, but I’d like to know why it won’t work as you’ve shown.

    So with method swizzling, we’re just inserting code into certain functions, now how would I add custom functions to unity app delegate?

    Also can I then use method swizzling to add my own viewController implementation? And how would I insert my own methods again?

    • Probably you put the plugin project into the Assets folder as well, so the symbols got imported from the Xcode project as well. Anyway, I’ll update all the plugin tutorials as it turned out that suffixing any folder with ~ prevents it from importing into the Unity project.

      So you can have the Xcode project in the Assets folder, just make sure to suffix the folder with a ~.

      • Happy that you have it working. 🙏.
        Do you mean that you’re a Unity employee?

        • Yes I work as a Unity Employee, but this plugin is for a side project. Basically was looking for a way to get google Sign In working, and as a result of your tutorials I managed both Android and iOS and mobile being my area I actually learn a lot about how the plugins work, which was a bit of a mystery before. Thank you!!!!

    • On “…how would I insert my own methods…”:

      I you take a closer look at EPPZSwizzler, you can see a method called +addInstanceMethod:toClass:fromClass:. You can introduce new methods with that (instead modifying existing ones).

      See corresponding unit tests for details on usage.

      *EDIT: Glad you’ve found it. 🙂