Add Unity3D in a native iOS application

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

The problem

For a project I need to implement an augmented reality feature inside a native iOS application.  We chose to go with Unity3D and Vuforia to do the augmented reality bit, as it’s free and lots of people are saying it’s the best solution. The only problem when working with Unity3D is that the exported iOS project is not easy to implement in an existing project as we only need Unity3D for 2 views inside a project with some dozen other native UIViews.

I started looking around on different blogs and fora and noticed there were a lot of people asking the same question.  Everybody has different approaches, some worked, some didn’t.  A lot of these solutions worked under Unity 3, but are broke in Unity 4. So with this blogpost I hope to help some people out who are having troubles under Unity 4+ and Xcode 5+. I’ve put the Xcode  and Unity3D project on GitHub, so feel free to download it, copy and/or alter it to fit your needs!  Hope it can save you some hours I’ve lost while trying to get it to work ๐Ÿ™‚

tl;dr how to add Unity3D in a native iOS application

Read it … bitch ๐Ÿ˜‰

The Unity3D part

Well … if you want to use Unity3D in your project you’ll first have to create the Unity3D part! Open up Unity3D and create a new project.  I’ll call mine ‘NativeUnity’. When Unity3D creates a new project, you get an empty scene.  Go to File > Save to save the scene.  I’ll call it ‘scene1’. Go nuts and put some content in it so we can actually see something. Add Unity3D in a native iOS application Ok, now let’s build this so it can run on an iOS device and we know for sure that everything works correctly. Go to File > Build Settings. Click on ‘Add Current’ to add the current scene to your build. Add Unity3D in a native iOS application

Next choose iOS as your platform and click on ‘Player Settings’ at the bottom of the dialog.  This will open the ‘Inspector panel’ and let you set all the settings for the Xcode project.  In my case I will change the following settings (but they can -and probably will- be different of yours).

  • Default orientation : Landscape right
  • Bundle identifier: be.thenerd.unityNative
  • Target device: iPad
  • Target iOS version: 7.0

Now click on ‘Build & Run’ in the Build Settings dialog. Unity will ask you where to put the Xcode project. I will put it in a folder called ‘ios-builds’ outside of my Unity project and call it ‘unity-native-ipad’. Add Unity3D in a native iOS application Click ‘Save’ and let Unity sprinkle its magic and create the Xcode project (this can take some time).  If all goes well, your app will boot on your device and you should see the scene you’ve made. Add Unity3D in a native iOS application Ok … so that’s that, but how can we now use this view as a part of normal native app instead of a Unity app. First we’ll need to override the ‘UnityAppController’ class with our own version. To do this we need to create in the Assets folder of our Unity project a ‘Plugins’ folder and inside this folder add a ‘iOS’ folder. So your structure needs to look like this:

  • Assets
    • Plugins
      • iOS

Make sure you name everything correct otherwise it will not work!  Unity checks if these folders exists and copy all their contents to the Xcode project. You can read more about this if you are interested on this page at the ‘Automated plugin integration’ section. Now you need to create a file in the iOS folder. I’ll call mine TNAppController.mm, but you can call yours whatever you want it to be.  Now do another build in Unity, and you should see the file appear in your Xcode project in the Libraries folder.

Add Unity3D in a native iOS application

Now the important part:ย don’t edit the file in this Xcode project, because it will be overwritten every time you do a new Unity build! Instead go to Unity and double-click on the file (it will also open in Xcode). Now we can override Unity’s AppController class with the following code.

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

@interface TNAppController : UnityAppController

- (void)createViewHierarchyImpl;

@end

@implementation TNAppController

- (void)createViewHierarchyImpl {

}

@end

IMPL_APP_CONTROLLER_SUBCLASS(TNAppController)
Swift

Especially the IMPL_APP_CONTROLLER_SUBCLASS bit is important, as this is the way to set a custom app controller!  What happens is IMPL_APP_CONTROLLER_SUBCLASS is defined in UnityAppController.h and this will set our AppController as the one to be loaded! Now in the ‘createViewHierarchyImpl’ methode we can set our own view hierarchy. So for this example I’ll create a UINavigationController which has 3 view controllers.

  1. A hello view controller
  2. A unity view controller with our scene
  3. A goodbye view controller

First we’ll need to create the different view controllers in our Unity Xcode project. So open that Xcode project.  To keep the Unity code (which resides in the ‘Classes’) folder separated  from our code I create a group called ‘My_Projectname’ (of course you can change this :)). In this folder I’ll create the 3 view controllers. Make sure to rename your CoolUnitySceneViewController.m file to CoolUnitySceneViewController.mm! This is because we will need to run some Objective-C++ code in that file!

  1. HelloViewController (subclass UIViewController)
  2. CoolUnitySceneViewController (subclass UIViewController)
  3. GoodByeViewController (subclass UIViewController)

So your structure should look something like this

. Add Unity3D in a native iOS application

In HelloViewController.m we’ll add a button to go to the next view controller in the viewDidLoad: method.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIButton *btnNext = [UIButton buttonWithType:UIButtonTypeSystem];
    [btnNext setTitle:@"Go to my cool scene" forState:UIControlStateNormal];
    btnNext.frame = CGRectMake(0, 0, 400, 44);
    btnNext.center = CGPointMake(self.view.bounds.size.height / 2, self.view.bounds.size.width / 2);
    [self.view addSubview:btnNext];
    
    [btnNext addTarget:self action:@selector(gotoUnityScene:) forControlEvents:UIControlEventTouchUpInside];
}

#pragma mark - Private methods
- (void)gotoUnityScene:(id)sender {
    NSLog(@"[HelloVC] Go to unity scene");
}
Swift

Now go to the TNAppController.mm file in the same project (so the Unity project). ย Because of the code hinting it’s easier to write the code here at this point and then just copy it over to the actual file in the Unity plugins folder (but don’t build in the meanwhile or it will be overwritten!). In the ‘createViewHierarchyImpl’ method we’ll first set the ‘_rootController’ property to an instance of UIViewControllerย and set the ‘_rootView’ property to an empty view. Then set the _rootView property as the view of the _rootController.

- (void)createViewHierarchyImpl {
    
    _rootController = [[UIViewController alloc] init];
    _rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    _rootController.view = _rootView;

}
Swift

Now we can create the HelloViewController (don’t forget to import the header file at the top).

HelloViewController *helloVC = [[HelloViewController alloc] initWithNibName:nil bundle:nil];
Swift

We want to push this onto an UINavigationController object, but we first need to create it. Create a private property for the navigation controller.

@interface TNAppController : UnityAppController

@property (nonatomic, strong) UINavigationController *navController;

- (void)createViewHierarchyImpl;

@end
Swift

Let’s create the navigation controller and set the hello view controller as the root view controller. ย Then we can add the navigation controller’s view as a subview of the _rootView.

self.navController = [[UINavigationController alloc] initWithRootViewController:helloVC];
[_rootView addSubview:self.navController.view];
Swift

Ok, that’s it. Now copy the contents of this file to the TNAppController file in your Unity project. Now go back to your Unity project and hitย โŒ˜+b to build again. ย The TNAppController file will now be overwritten, but the files in our ‘My_ProjectName’ will be untouched! If all goes well, you should see the following. Add Unity3D in a native iOS application Now the part you have been waiting for ย (I guess …), let’s hook up a view controller which loads the Unity3D scene. Go to your CoolUnitySceneViewController.h file and import the following headers.

#import "UI/UnityView.h"
#import "UI/UnityViewControllerBase.h"
#include "UnityAppController+ViewHandling.h"
Swift

Now go to the implementation file (CoolUnitySceneViewController.mm) and locate the viewDidLoad: method and add this bit of code.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    [self.view addSubview:GetAppController().unityView];
    
    GetAppController().unityView.frame = self.view.frame;

    UIButton *btnNext = [UIButton buttonWithType:UIButtonTypeSystem];
    [btnNext setTitle:@"Go to last scene" forState:UIControlStateNormal];
    btnNext.frame = CGRectMake(0, 0, 400, 44);
    btnNext.center = CGPointMake(self.view.bounds.size.height / 2, self.view.bounds.size.width / 2);
    [self.view addSubview:btnNext];
    
    [btnNext addTarget:self action:@selector(goToLastScene:) forControlEvents:UIControlEventTouchUpInside];
    
}

#pragma mark - Private methods
- (void)goToLastScene:(id)sender {
    NSLog(@"[CoolUnitySceneVC] Go to the last scene");    
}
Swift

That’s it … we grab the unityView property of the AppController (which we get via the utility method ‘GetAppController’ defined in UnityAppController.h and add it to our view. Then we add a button which lays on top of the unity view. ย When we click on this button we execute the goToLastScene method and there we will load the last view controller (don’t forget to import the view controller at the top of the file).

- (void)goToLastScene:(id)sender {
    NSLog(@"[CoolUnitySceneVC] Go to the last scene");
    
    GoodByeViewController *goodByeVC = [[GoodByeViewController alloc] initWithNibName:nil bundle:nil];
    [self.navigationController pushViewController:goodByeVC animated:YES];
    
}
Swift

In the viewDidLoad: method of GoodByeViewController you can now add the following code.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    UILabel *lblTheEnd = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 400, 44)];
    lblTheEnd.text = @"The end";
    lblTheEnd.center = CGPointMake(self.view.bounds.size.height / 2, self.view.bounds.size.width / 2);
    
    [self.view addSubview:lblTheEnd];
}
Swift

Now run the app again and you should see the following screens if you click on the buttons. Add Unity3D in a native iOS application Add Unity3D in a native iOS application

I hope this will help some of you out to add Unity3D in a native iOS application!  If you have any remarks, please put them in the comments!  I’ve only started working with Unity since yesterday, so it could be there is an easier method, but I kinda like this one already!

I’ve put all the code on GitHub, so you can check out the project.  For the iPhone project I only uploaded the files I’ve mentioned above, because otherwise the folder was 450MB big!

Reference material

http://forum.unity3d.com/threads/unity-appcontroller-subclassing.191971/

http://forum.unity3d.com/threads/unity-4-5-post-process-build-player-iphone_view-m.248400/

Share this post

54 Responses

    1. Hm it should work as I just did it this week. I’ll try to make a video tutorial this weekend showing the different steps!

      1. I think that the problem is “bounds”, in this line:

        _rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

        1. in IOS8 createViewHierarchyImpl method is deprecated,you can override createUI method
          – (void)createUI{
          _rootController = [[UIViewController alloc] init];
          _rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
          _rootView.backgroundColor = [UIColor redColor];
          _rootController.view = _rootView;

          HelloViewController *helloVC = [[HelloViewController alloc] initWithNibName:nil bundle:nil];

          self.navController = [[UINavigationController alloc] initWithRootViewController:helloVC];

          [_rootView addSubview:self.navController.view];
          [_window addSubview:_rootView];
          _window.rootViewController = _rootController;
          [_window bringSubviewToFront:_rootView];
          [_window makeKeyAndVisible];
          }
          this method is declared in file UnityAppController+ViewHandling.h

  1. hi, Frederik,

    so great to see this blog, awesome. I have two questions, first, how to add gesture to the unity view in native app for rotating the 3D model? second, if I have several 3d models, each one is a scene in unity, How to import them together into one native app and load them respectively in native app?

    Thanks in advance,
    Jack

  2. Man you kick butt. I’ve found solution of this topic for over a week. As you said, ‘A lot of these solutions worked under Unity 3, but are broke in Unity 4’. Today, Problem ends here.

    I tested with Unity 4.5.5f1, XCode 6.1 (6A1052d), iOS 8 simulator Version 8.1 (550.3). It works perfectly.

    Additionally, interface builder works OK. If you drop everything but unity view in CoolUnitySceneViewController, you can resize the frame of the view of that controller, add to any other view as subview. It reacts well.

  3. Hello, thanks for the tutorial.

    Now I have a little problem. I doesn’t know how to switch from NavViewController to ViewController.

    The project I have needs the VuforiaAR System, I just follow your tutorial and it works with Vuforia and your example. I add my original project and it have ViewController instead of NavViewController. I just doesn’t know where to make this switch and add my project instead of your NavView.

    I hope you can helpme a little.
    Sorry if my english is so rude.

  4. Hello great tutorial very useful!
    Is there any way to remove the top navigation bar (the one with the back button)?

    Thanks!

    1. In the custom AppController.mm file under the line:
      self.navController = [[UINavigationController alloc] initWithRootViewController:helloVC];
      add the line:
      self.navController.navigationBarHidden = YES;

  5. thank for tutorial

    i try your tutorial but i can’t.

    this tutorial is so hard to me. but i should do this.

    can you send me source code??

    i just need your help

  6. hi, thanks for the tutorial, manage to get it work, I’m getting close to what I want but I’m stuck at getting the IOS View to only appear when I tap on a button in Unity..so its more like the other way round. Instead of having the IOS View shown at first, I want Unity View to show first, then hide Unity when I tap on button to show IOS View..

  7. hi, Frederik,

    Thank you for nice stuff. I have some problems.
    – Exported your unity project for ios
    – Copied and pasted your other files which are HelloViewController etc.
    – and tried to run in xcode
    But in TYAppController.mm file and line of
    _rootController = [[UnityDefaultViewController alloc] init];
    use of undeclared identifier “UnityDefaultViewController” exception

    Can you help me please ?

      1. Hi frederik,

        Thank you for your quick response. I will try with your source at night (about 5 hours later ). I hope that help to me.

        Thank you my friend.

        1. Thank you for tutorial,
          But also I have some problems like
          – Use of undeclared identifier ‘UnityDefaultViewController’
          – CoolUnitySceneViewController.h:12:10: ‘UnityAppController+ViewHandling.h’ file not found

          I’m not unity developer, so can you please share the source code, or help to solve problem ?

          Thank you

  8. hi, Frederik
    thank for tutorial

    i have one question, can i pass an array of string from HelloViewController.m to ‘unity view’ ?

  9. Hi Frederik,

    I followed your great tutorial: thank you for this!

    I got a little problem: for the file “UnityViewControllerBase.h”:
    /Classes/UI/UnityViewControllerBase.h:52:8: Expected identifier or ‘(‘
    /Classes/UI/UnityViewControllerBase.h:53:8: Expected identifier or ‘(‘

    How could this be? I copied the scripts from the Git Repo.

    Best,

    Oli

      1. Hi Oliver,

        I haven’t tested with Unity 5 beta yet.
        Probably they will have changed some stuff in the generated IOS builds from Unity.
        When the final is out, I will post an update if I found out how to embed Unity inside a native IOS app!

          1. Incase anyone else is looking for it here, I fixed the above issue by changing those bottom two extern “C” methods to this:

            // this is helper to add proper rotation handling methods depending on ios version
            //FIX HERE
            #ifdef __cplusplus
            extern “C” {
            #endif
            void AddViewControllerRotationHandling(Class class_, IMP willRotateToInterfaceOrientation, IMP didRotateFromInterfaceOrientation, IMP viewWillTransitionToSize);
            #ifdef __cplusplus
            }
            #endif

            #ifdef __cplusplus
            extern “C” {
            #endif
            void AddViewControllerDefaultRotationHandling(Class class_);
            #ifdef __cplusplus
            }
            #endif

  10. Hello!
    Help me please.
    My project doesn’t go in TNAppController. Why is it happening?
    (I create new file in my project and copy code)

  11. HI,

    We are also trying to implement IOS native UI with Unity3d 4.6.2 using this link.

    We done the same with Vuforia 3.0.7 SDK & Unity3d 4.6.2 , working fine.

    But when we try with Vuforia 4.0 SDK beta and unity3d 4.6.2, its not working. IOS native buttons are displayed but Vuforia Camera is not initialized. Showing black screen. How to solve this one?

    Can anyone tried?

    It would be great if i get suggestions.

    1. I got it working with Unity 4.6.2 & latest beta of Vuforia (4.0.19 or something).
      Unity now generates a custom class which gets loaded VuforiaNativeRendererController.

      In this one you’ll have to do all your initialization of your own code.

      1. Would you be willing to sell the project with 4.6.2 with vuforia 4 working?

        Or do any side work implementing this type of thing, really struggling with the new il2cpp project merging into my old ios project.

        email me if you do either lol

      2. Are you saying that we need to move the code from the TNAppController into the new VuforiaNativeRenderController? I’ve hit this issue with an app that need to be updated for the app store and we’ve needed to obviously add 64bit support, so project was updated to Vuforia 4.2 and Unity 4.6.5

  12. Hi,
    as unity 5 was yesterday released i tried the whole day to implement it (as i got it working with 4.6.x – things changed again).

    I wrote a tutorial about it (hope it helps someone). If you want – the-nerd- you are allowed to take out the parts for your tutorial to update it ๐Ÿ™‚ As you helped me a lot in the past with yours – this is just fair ๐Ÿ™‚

    http://www.makethegame.net/unity/add-unity3d-to-native-ios-app-with-unity-5-and-vuforia-4-x/

  13. Had this working with Unity 4.6, but just updated to 5 and getting the
    Expected identifier or โ€˜(โ€˜ error.

  14. I’m trying to get this to work with a storyboard that has a navigation controller as the initial view controller.

    Can you advise as no joy yet…

    Thanks

  15. hi,
    great tutorial!

    i follow this tutorial with unity 4.6 and work fine!
    but now i tried this tutorial with unity 5.0.1 and doen’t work…
    i changed
    createViewHierarchyImpl
    willStartWithViewController
    but nothing…

    there is a solution for this problem?

    thanks a lot

  16. In this tutorial says how to add other project add to unity . Is there any way to add unity xcode project add to another existing project?

  17. Hello, Thanks for the tutorial which explains how to integrate unity in an existing xcode project. Currently i created a unity project and trying to create in .mm file under Assets->Plugins->iOS in unity project. but when i tap on create button, i count find a option that allows me to create a .mm file??? could you please tell me how to create a .mm file in unity project and use it in my xcode project

    1. Just go to Plugins folder in your Unity project (use Finder) and create txt file (or cope any text file), then change extension to .mm and open it by Xcode.

  18. i have an issue in relaunching Unity-App-Code from Core-IOS-App-Code, please help me to make it dynamically working. As i have many other modules so here i am loading Unity-Code when required to launch it, but failed to release after use of it.

Leave a Reply

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

Related Posts