Can you have more than one subclass of UnityAppCon

2020-07-18 07:48发布

问题:

I need to add some code to application:didFinishLaunchingWithOptions: in UnityAppController that configures Google AdWords Conversion Tracking using their Objective-C SDK.

Editing this file manually each time Unity generates the Xcode project seems error prone, so I'm thinking about adding a post build step using the PostProcessBuild attribute that would apply a patch for generated Unity code.

However, patch files are hard to maintain so I'm looking for alternative solutions. It seems that subclassingUnityAppController via the IMPL_APP_CONTROLLER_SUBCLASS macro and overriding application:didFinishLaunchingWithOptions: there can be one of them.

But after I do this another third party plugin (Google Play Games) that also subclasses UnityAppController using the same method stops working because its app controller is no longer called. Here is a relevant piece of code from that plugin:

@interface GPGSAppController : UnityAppController {
}

@end

IMPL_APP_CONTROLLER_SUBCLASS(GPGSAppController)

So I was wondering if it's possible to subclass the Unity app controller from multiple places. I could not find any documentation for IMPL_APP_CONTROLLER_SUBCLASS online.

Or maybe there is a better approach for adding custom initialization code in Unity on iOS?

回答1:

I ended up swizzling UnityAppController methods and doing initialization in my implementation of them.

Here is my solution in case anyone is interested:

#import <objc/runtime.h>

...

#import "UnityAppController.h"

namespace {

typedef BOOL (*ApplicationDidFinishLaunchingWithOptionsImp)(UnityAppController *appController,
                                                            SEL selector,
                                                            UIApplication *application,
                                                            NSDictionary *launchOptions);

ApplicationDidFinishLaunchingWithOptionsImp OriginalApplicationDidFinishLaunchingWithOptions;

BOOL ApplicationDidFinishLaunchingWithOptions(UnityAppController *appController,
                          SEL selector,
                          UIApplication *application,
                          NSDictionary *launchOptions) {
    // Initialize Google Play Games, etc
    return OriginalApplicationDidFinishLaunchingWithOptions(appController, selector, application, launchOptions);
}

IMP SwizzleMethod(SEL selector, Class klass, IMP newImp) {
    Method method = class_getInstanceMethod(klass, selector);
    if (method != nil) {
        return class_replaceMethod(klass, selector, newImp, method_getTypeEncoding(method));
    }
    return nil;
}

} // anonymous namespace

@interface AppController : UnityAppController

@end

@implementation AppController

+ (void)load {
    OriginalApplicationDidFinishLaunchingWithOptions = (ApplicationDidFinishLaunchingWithOptionsImp)
        SwizzleMethod(@selector(application:didFinishLaunchingWithOptions:),
                      [UnityAppController class],
                      (IMP)&ApplicationDidFinishLaunchingWithOptions);
}


@end

Simply save this file as AppController.mm and add it to your Assets folder. Unity will recognize it as an Objective-C++ source file and automatically include it in the generated Xcode project.

If you need to include frameworks or modify your Xcode project in other ways take a look at PostProcessBuildAttribute and Xcode API.