Sandbox Unity app in an existing iOS app

I HAVE CREATED A NEW WAY TO INTEGRATE UNITY WITHIN AN EXISTING IOS APP. YOU CAN FIND THE POST HERE.

The problem

After my previous blog post on how to sandbox an iOS app inside of a Unity project, I got some questions on how to do the same but put Unity inside of an existing iOS app.

After playing around with it, I found a solution which works well for me to add Unity app in an existing iOS app.

The solution to add Unity app in an existing iOS app

First I’ll create a simple Unity3D project which just shows an square on the stage.

Unity app in an existing iOS app
Create a simple Unity3D project

 

Now you’ll need to create the entry point for your custom code. You can do this by creating a folder Plugins in the Assets folder. Inside the Plugins folder you create a folder iOS and inside that folder you create a file TNAppController.mm (or whatever name you like).

Put the following code in this file.

//
//  TNAppController.m
//  
//
//  Created by Frederik Jacques on 08/09/14.
//  Copyright (c) 2014 Frederik Jacques. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "UnityAppController.h"
#import "UI/UnityView.h"
#import "UI/UnityViewControllerBase.h"


@interface TNAppController : UnityAppController

@property (nonatomic, strong) UINavigationController *navigationController;

- (void)createViewHierarchyImpl;

@end

@implementation TNAppController

- (void)createViewHierarchyImpl {

    _rootController = [[UIViewController alloc] init];
    _rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    _rootController.view = _rootView;
    
    UIViewController *vc = [[UIViewController alloc] init];
    vc.view.backgroundColor = [UIColor redColor];
    
    self.navigationController = [[UINavigationController alloc] initWithRootViewController:vc];
    [_rootView addSubview:self.navigationController.view];
}

@end

IMPL_APP_CONTROLLER_SUBCLASS(TNAppController)

Save the project to a folder, mine is /PROJECTS/StandaloneUnity/unity/UnityPart. After you’ve done this, build the project for iOS by pressing ⌘ + shift + b. Choose the iOS platform and set the build settings to whatever you like.  Hit ‘Build’ (if you haven’t saved your scene yet, don’t forget to save it first and then add the scene in the Build settings window). I’ve save the build under /PROJECTS/StandaloneUnity/unity/ios-build/UnityStandAloneIOSBuild.

Unity app in an existing iOS app
Add the scene to the build and provide the correct settings for your iOS app

Now we can create the iOS project in which you want to add the Unity iOS build.  Go to Xcode and create a new project.  Choose for the ’empty application’ template and save the project. My location is /PROJECTS/StandaloneUnity/ios/UnityStandAlone, so my directory structure in the end looks like this.

  • StandaloneUnity
    • ios
      • UnityStandAlone
    • unity
      • ios-build
      • UnityPart

Alright, now we can link up both projects.

Go to the ios project which has been generated by Unity and look for the folders ‘Classes’ and ‘Libraries’ and drag these to your own Xcode project.  Make sure to uncheck ‘Copy items into destination group’s folder’ and select ‘Create groups for any added folders’. Make sure you add the files to your target.

Unity app in an existing iOS app
Drag the folders to your Xcode project
Unity app in an existing iOS app
Settings for importing

Now we need to adjust the ‘Header search paths’ and the ‘Library search paths’ values in our project’s Build Settings.  If you want to share this project with other team members, the best thing to do is work with relative paths by using the ${SOURCE_ROOT} keyword.  This keyword will resolve the path to the directory where your project file is in

In my case the value for ‘Header search paths’ will be “${SOURCE_ROOT}/../../unity/ios-build/UnityStandAloneIOSBuild/Classes”.

The path for ‘Library search paths’ will be “${SOURCE_ROOT}/../../unity/ios-build/UnityStandAloneIOSBuild/Libraries”

If you try to build your project, you’ll get a ton of warnings. This is because the generated code from Unity is not written in ARC. So we’ll have to tell the compiler that these files don’t use ARC.  We can do this in ‘Build phases > Compile sources’ and give those files the ‘-fno-objc-arc’ flag (there are quite a few … but you only need to do this once, so bear with me).

These are the ones I flagged:

  • VideoViewController.mm
  • UnityViewControllerBase.mm
  • ScriptIntegration.mm
  • DisplayManager.mm
  • WWWConnection.mm
  • iAD.mm
  • DeviceSettings.mm
  • CVTextureCache.mm
  • GlesHelper.mm
  • Keyboard.mm
  • UnityAppController.mm
  • UnityAppController+Rendering.mm
  • SplashScreen.mm
  • AppDelegateListener.mm
  • iAD_Scripting.mm
  • RegisterClasses.ccp
  • iPhone_Sensors.mm
  • UnityAppController+ViewHandling.mm
  • LifeCycleListener.mm
  • UnityView.mm
  • CrashReporter.mm
  • CMVideoSampling.mm
  • RegisterMonoModules.ccp
  • EAGLContextHelper.mm
  • iPhone_Profiles.ccp
  • iPhone_OrientationSupport.mm
  • ActivityIndicator.mm
  • VideoPlayer.mm
  • RenderPluginDelegate.mm

If you now rebuild the project, you shouldn’t see the a lot of errors anymore (only a few ).

Remove the main.mm file from the Classes folder and rename the main.m file to main.mm in ‘Supporting Files’.  Overwrite the contents of this file with the following.

#import <UIKit/UIKit.h>

#include "RegisterClasses.h"
#include "RegisterMonoModules.h"
#include "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;

void UnityInitTrampoline();

// WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value)
const char* AppControllerClassName = "UnityAppController";

int main(int argc, char * argv[])
{
    
    @autoreleasepool {
        
        UnityInitTrampoline();
        if(!UnityParseCommandLine(argc, argv))
            return -1;
        
        RegisterMonoModules();
        NSLog(@"-> registered mono modules %pn", &constsection);
        
        return UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
        
    }
}

Now we need to add some imports to the precompiled header file of our project (the .pch file in ‘Supporting Files’.  Go to the group ‘Unity/Classes’ and find the .pch file there (probably it will be named something like ‘iPhone_target_Prefix.pch’ and copy its contents to the .pch file in you ‘Supporting Files’ group.

Now we’ll need to add a ‘Run script’ to our Build Phases.  Click on the little + sign at the top left corner when you are in the Build Phases tab and choose for ‘New run script build phase’ and drag it after ‘Target Dependencies’.  Now add the following script inside it.  Don’t forget to set the correct directory for your project :-)!

Unity app in an existing iOS app
Run script

Alright, next up is adding the right libraries. Add these:

  • Foundation.framework
  • UIKit.framework
  • OpenGLES.framework
  • QuartzCore.framework
  • OpenAL.framework
  • libiconv.2.dylib
  • libiPhone-lib.a
  • AudioToolbox.framework
  • CFNetwork.framework
  • MediaPlayer.framework
  • CoreLocation.framework
  • SystemConfiguration.framework
  • iAd.framework (set status to optional)
  • CoreMedia.framework
  • CoreVideo.framework
  • AVFoundation.framework (set status to optional)
  • CoreGraphics.framework
  • CoreMotion.framework (set status to optional)
  • GameKit.framework (set status to optional)

We are almost there. Only need to update a few more Build Settings.

Set the ‘Architectures’ to ‘armv7’

If you debug the app, you’ll see it will compile very slowly because of the dSYM file being generated, you can turn this off by setting ‘Debug information format’ for debug to ‘DWARF’ instead of ‘DWARF with dSYM file’.

At ‘other linker flags’ add the following values

  • -weak_framework
  • CoreMotion
  • -weak-lSystem

At ‘other c flags’ add ‘-mno-thumb’ value.

At ‘c++ language dialect’, select ‘compiler default’.

At ‘c++ standard library’, select ‘libstdc++ (GNU c++ standard library)’

And let’s also add 2 of our own by pressing the little + sign at the top left ‘Add User-Defined Setting’.

Key GCC_THUMB_SUPPORT value NO
Key GCC_USE_INDIRECT_FUNCTION_CALLS value NO

Alright … that should be it. Hook up an iOS device to your system, select it at the top left in Xcode and hit Build.  Normally if everything goes well, the application should compile. (you will get errors if you try to build with a simulator selected, this only works on a real device!) And you will see the following result.

Unity app in an existing iOS app
Compile and run the app

This is the code that gets executed from the TNAppController.mm file.  So that’s your entry point to add the rest of your application.

To show the Unity content you’ll need to create a new class TNUnityViewController for example and let it extend UnityDefaultViewController.  Rename the .m file to .mm.

Then add the following code to the .mm file.

- (void)loadView {
    CGRect bounds = [UIScreen mainScreen].bounds;
    
    self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, bounds.size.height, bounds.size.width)];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor purpleColor];
    [self.view addSubview:GetAppController().unityView];

    GetAppController().unityView.bounds = self.view.bounds; // do this because unity is acting weird :=)
    GetAppController().unityView.center = self.view.center;
            
}

Now present this view controller and you will see the contents of what you’ve created in Unity.  You also should add the -fno-objc-arc flag on this file as well!

Hope this helps some of you guys out to sandbox an Unity app in an existing iOS app!  If you have any questions, just put them in the comments!  I can’t upload the code to github because there is a 100MB limit per file and some of the unity generated files are +100MB, but if you contact me via email I can send them to you if you like!

 

 

Share this post

31 Responses

  1. Hi Frederik,

    First I want to thank you for this great post. I did all steps you describe in here and I came to this error which I am unable to fix:


    2014-11-06 10:53:15.114 VF[4530:1614129] -> registered mono modules 0x13a6cfc
    2014-11-06 10:53:15.883 VF[4530:1614129] Flurry: Starting session on Agent Version [Flurry_iOS_121_4.3.0]
    -> applicationDidFinishLaunching()
    Mono path[0] = '/private/var/mobile/Containers/Bundle/Application/9A31FA39-A3D2-40EF-8974-96BD452A48E3/VF .app/Data/Managed'
    Mono config path = '/private/var/mobile/Containers/Bundle/Application/9A31FA39-A3D2-40EF-8974-96BD452A48E3/VF.app/Data/Managed'
    2014-11-06 10:53:16.223 VF[4530:1614173] Memory Capacity of 12450 MiB
    2014-11-06 10:53:16.223 VF[4530:1614173] 3401 MiB Free memory available.
    Failed to load AOT module 'mscorlib' while running in aot-only mode.

    I already tried to delete the app from device, delete Derived data and run again with same error. I even tried to re-build my unity project and integrate again in iOS prj without any luck.

    I would appreciate any kind of hint or idea of how this can be fixed.

    Thank you,
    Razvan

    1. Hi Razvan,

      I’ll try to upload a videotutorial on how to do this tonight!
      Hope it will help you further.

      I’ll post the link at the top of the blog post!

      Kind regards,
      Frederik

  2. Hi frederik,

    could you send sample code to me?
    and how to extend UnityDefaultViewController, and present at navigation controller?
    thanks for your help.

    1. I think it’s necessary to say which of the header files need to be import.

      In my implementation, it looks like this:

      //.h

      #import
      #import “../UnityProject/angrybots/Classes/UI/UnityViewControllerBase.h”

      @interface MyUnityViewController : UnityDefaultViewController

      @end

      //.m

      #import “MyUnityViewController.h”
      #import “../UnityProject/angrybots/Classes/UnityAppController.h”
      #import “../UnityProject/angrybots/Classes/UI/UnityView.h”

      @implementation MyUnityViewController

      – (void)loadView {
      CGRect bounds = [UIScreen mainScreen].bounds;

      self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, bounds.size.height, bounds.size.width)];
      }

      – (void)viewDidLoad {
      [super viewDidLoad];

      self.view.backgroundColor = [UIColor purpleColor];
      [self.view addSubview:GetAppController().unityView];

      GetAppController().unityView.bounds = self.view.bounds; // do this because unity is acting weird :=)
      GetAppController().unityView.center = self.view.center;
      }

      @end

  3. Is it possible to do this using clang? I get linker error : ld: framework not found -stdlib=libc++ any help would be appreciated.

  4. Thank you again, my main goal is to add Unity View in iOS Native app,
    so please can you send me the project source code example ?

    I appreciate that, Thanks,

  5. Hi,
    first i got it partically working – means : currently i have the bug of unrecognized selector ([UINavigationItem screen]).

    But this just work only if i switch to armv7 on “architectures”.

    So if i understand right this way doesnt work with 64 Bit Apps ? As 1.Februar Apple just allow 64 Bit supported Apps for the App Store. Is there a way to get this working too ?

    1. No we have to wait until Unity released the 64 bit support.

      They said it would arrive in January, but at the moment there is not a new version out 🙁

  6. Now it is – and vuforia 4.0 is out there too.
    Got both running as “Unity Only” App with the new IL2CPP Scripting Backend.

    And now i try to integrate in in existing iOS App – i hate it -.-

    Android took me just some hours 🙂 (as there are only some .jars which are precompiled).
    And in iOS it will take years …but i don’t hope it.

  7. Hi frederik,

    I tried this in latest Unity 4.6.3 ( iOS 64 bit support) and vuforia latest 4.0 ( iOS 64 bit support) and not succeed getting more errors . I think unity and vuforia made lot of changes and unity generated iOS project contains more libraries and header files ( Inside Classes new folder Native with more header files). Can you update this post for latest support.

    Regards,
    P.Uthaman

  8. Hi uthaman,
    i had the same issue – got it working – and then did the same in Unity 5.
    I documented everything here : http://www.makethegame.net/unity/add-unity3d-to-native-ios-app-with-unity-5-and-vuforia-4-x/

    The good news what i figured out today: you dont need to add “-fno-objc-arc” flag to all unity files – as apple forced unity to have a build target “min iOS 5.x” which means you are required to used ARC.

    The next important step (as i got it first only working on 32 Bit devices but not 64 Bit):
    Change the Rendering Option to Open GLES 2 or Open GLES 3 – not Automatic or Metal.
    Both would cause a crash on iOS 64 Bit Devices as it seems that the Metal Support in Unity has a bug (and this was in 4.6.3 also as in 5).

  9. Hi Neo,

    Thanks for your response and detailed document for this latest integration. I will try this steps and let you know. The ultimate goal is iOS 64 Bit support then only we can submit the app to the App Store.

    Thanks,
    P.Uthaman

  10. Hi, going round and round in circles and cannot get my attempts or you supplied source to compile, I think the latter is out of date?

    Could you post the update project for IOS8 and Unity 5?

    I have now tried 3 times to follow your steps and get error after error and gave up. I then tried you supplied source but that appears incomplete and again will not compile…

    Please help…;-)

    Thanks

    1. Hi Paul,

      Yeah this guide is for Unity 4. I haven’t updated to 5 yet as our product works perfectly fine under 4 and we are not planning to do the payable upgrade. So I’m sorry but I won’t be able to do a write-up for Unity 5

      Cheers,
      Frederik

    1. I never work with storyboards, so I don’t know. Probably it will be possible though 🙂

  11. I want my iOS app start normally and when I clicked a button, It shows unity View, but i dont want to put all my code in TNAppController, is possible to call it from app delegate for example?

  12. I’m getting errors that Foundation.h is trying to import IL2CPP files. For example String.h. What would be the problem?

  13. My last error might of been because I added too many “Header search paths”, but now I get this in the error logs:

    Undefined symbols for architecture armv7:
    “UnityInitTrampoline()”, referenced from:
    _main in main.o
    “_UnityParseCommandLine”, referenced from:
    _main in main.o
    “_InitializeScriptingBackend”, referenced from:
    _main in main.o

  14. Hello ! What is the most up-to-date approach to this? I’ve tried a number of tutorials in this and I always run into trouble.

    I’m using Unity 5.5.x and the latest XCode and Vuforia.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts