Skip to main content

Integrating Unity and Vuforia with a iOS Swift Project

Tags

Integration

Last week I started building an iOS application in Swift 3.0 and Xcode 8 and was able to get a good portion of the application completed.  Towards the end of last week I became aware of the need to integrate a Unity / Vuforia exported project into my existing iOS Swift project to enable an augmented reality feature in the iOS application I was working on.  At first, I did not think anything of this request because in the past I had only minor difficulties integrating previous augmented reality platforms such as Metaio and Wikitude into my previous Objective-C iOS projects, and I did not think that this one was going to be any different.  One thing I can now say now after successfully completing the integration is that there is a lot more involved than I initially thought there was.  However, in saying that, I think the context of my situation is important to keep in mind.  Typically, I am under the impression that most developers use the Xcode project exported from Unity as the starting point for their application.  That would certainly be an easier approach than integrating a Unity exported Xcode project into an existing Xcode Swift based project.  When you integrate a Unity exported project into your existing project there is a lot of project setup that needs to happen with folder / group structure and altering your project build settings, and these are the settings that I am going to cover in this post, how to successfully integrate an exported Xcode Unity project to an existing Swift based Xcode project. 

Before You Get Started

In the following example I am using Unity 5.4, Vuforia 6, Xcode 8.0 with iOS 10.01 and Swift 3.0.  Although it certainly can be, this example does not assume that your Unity / Vuforia scene needs to be loaded as the RootViewController and can be loaded anywhere needed in your project.  This example is compiled as a combination of my own experience and troubleshooting, multiple documentation resources online, and a big help from a Github repo by Blitz Agency who should be credited for a lot of the code that is used in this post from.  Another great resource to check out if you are developing your project in Objective-C is a this post and video by Frederik Jacques (AKA the-nerd).

Folder Structure

Project and Folder Structure

The folder structure in your project is important.  At the end of the day it can be setup any way you choose, but ultimately you will have to be aware of your project structure because you will need to make the necessary changes in your Build Settings to recognize certain files in your project structure.  In my example that is represented in the screen shot above I am recreating the folder and project structure that comes straight out of the Unity exported project.   Setting up the folder structure like this will ultimately be less work on my end that setting up a custom folder structure.

The folder structure that you setup is very important because it needs to expose all of the .cpp, .m, and .mm files to be compiled and it also needs to expose any binary files to be linked when clang compiles your project.  The tricky part is that the folders that are added to your iOS project from Unity need to be added as Xcode groups but also retain their folder structure in the Xcode project as well.  There are four folders from your exported Unity project that need to be added to your existing Xcode project. The Classes, Libraries, Data, and QCAR folders.  This may differ from project to project and how your Unity export setting are configured, but Classes and Libraries will contain all of your code and binary files and usually Data and QCAR contain data, media, and Unity / Vuforia configuration files.  There are a couple of different methods to adding these folders to your project, but the method and folder structure that worked best for this project was:

  • Create a new Xcode group for Libraries and Classes.
  • Once you have created these groups open the right window pane in Xcode and with the newly created group selected, click the folder icon and create a static folder reference in the root of your project. Do this for both of the Libraries and Classes folders so you have a group for these folders and a static folder in your Xcode project.
  • Now drag in all of the code files from your Unity Classes folder to your new Classes group in Xcode. Make sure you drag in the code files only in the root of this folder and NOT the folders inside the Classes folder yet.
  • Now, just as you did before with Classes and Libraries, create a new Xcode group for the folders inside Classes folder. Currently, the folders are UI, Unity, Native, UnityAds, and PluginBase. After you have a new group, create the static folder references for these groups to in your Xcode project directory inside Classes.
  • After all of your static folder references are created inside Classes in Xcode, drag all of the code for the folders UI, Unity, UnityAds, and PluginBase from Unity into Xcode. As for the Native folder, some people say that the .h assembly files are not needed to keep in the Xcode project and complain about Xcode slowing down if they are left in. I can completely understand these files slowing down Xcode, but I have never been successful removing them from the project. So in this example they have been left in, use your own judgment as to what works for you though. The Native folder does need the .cpp files though.
  • Next drag in all of the code and binaries from the root of the of the Libraries folder in Unity to your Libraries group in Xcode. Again, just the code and not the sub folders.
  • Now, inside your Libraries group, create groups for this folder structure: Plugins > iOS > VuforiaMediaSource > src. After that create static folder references in Xcode using the folder icon in the right pane of Xcode as described above.
  • Drag in the code and binaries from Libraries > Plugins > iOS in your Unity project to your Xcode project.
  • Drag in the code from Libraries > Plugins > iOS > VuforiMediaSource > src in your Unity project to your Xcode project.
  • From your Unity project, drag in the static folder for libil2cpp from the Libraries directory in Unity to your Xcode Libraries group. This folder does not need to be created as a group first.
  • After the code files have been added, navigate out to your Build Phases tab in Xcode and make sure that all of the new .cpp, .m, and .mm files are showing up in your Compile Sources. If they are not, then Xcode is not recognizing them for some reason and you need to go back and make sure that Xcode knows that these files need to be compiled.  
  • From Unity drag in the Data and QCAR folders as static folder references in Xcode and not as groups. Drag these folders into the root of your Xcode project.
Framework Binaries

Frameworks and Binaries

After our folder structure is setup and all of the files have been added to our project we need to ensure that the all of the binaries added to project are showing being recognized so that clang knows to link them at compile time.  Also, we want to ensure that all of the necessary frameworks that Unity and Vuforia use have been added to the project to so there will be no issues at compile time or at run time.  I have added a screen shot of all of the needed binaries and frameworks above, but just to make sure you have these added, navigate to the xcodeproj file > Build Phases > Link Binary With Libraries tab and add any of the need frameworks that are missing from your project.  Here is a list:

  • UIKit
  • MediaPlayer
  • iAD (Optional)
  • CoreVideo
  • Security
  • OpenAL
  • OpenGLES
  • QuartzCore
  • SystemConfiguration
  • Foundation
  • CoreLocation
  • CoreMedia
  • CoreMotion (Optional)
  • AVFoundation
  • CFNetwork
  • CoreGraphics
  • AudioToolbox
  • CoreText
  • libiconv.2.tbd
  • libQCARUnityPlayer.a (Added from Libraries Group)
  • libVuforia.a (Added from Libraries Group)
  • libVuforiaMedia.a (Added from Libraries Group)
  • libiPhone-lib.a (Added from Libraries Group)

Objective-C Bridge and Prefix Headers

If a bridging header has not already been added to your project it is a good time to add one now.  A bridging header will expose needed objects and methods to Swift that are created in Objective-C.  To add a new bridging header to your project click on your xcodeproj file > File > New > File... > Header File.  Name your file ProjectName-Bridging-Header.h, where "ProjectName," is the name of your project.  Set this file a side for a moment and we will come back to it later.

Now, you may have noticed that the folder Classes contained a file called: Prefix.pch.  This is a prefix header and can help Xcode compile faster and reduce the amount of files that are needed to include into each of your project files throughout the codebase.  We will create a custom prefix header also just in case your project gets a bit more complex and there are more source files you need to precompile throughout your project.  To create a custom pch file click on your xcodeproj file > File > New > File... > PCH File.  Name your file PrefixHeader.pch.  We will use the one included from unity as a starting point.  Here is the code you must add to your PrefixHeader.pch file below:

#ifndef PrefixHeader_h
#define PrefixHeader_h
 
#ifdef __OBJC__
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
#endif
 
#include "Preprocessor.h"
#include "UnityTrampolineConfigure.h"
#include "UnityInterface.h"
 
#ifndef __OBJC__
#if USE_IL2CPP_PCH
    #include "il2cpp_precompiled_header.h"
#endif
#endif
 
#ifndef TARGET_IPHONE_SIMULATOR
    #define TARGET_IPHONE_SIMULATOR 0
#endif
 
#define printf_console printf
#endif /* PrefixHeader_h */
Linker flags

Build Settings

There are quite a few build settings that need to added or altered.  These are the build settings that worked for my project with Xcode 8.0, iOS 10.0.1, and Swift 3.0.  To alter these build setting, navigate to xcodeproj file > Build Settings >

  • Build Options: Enabled Bitcode: No
  • Linking: Other Linker Flags: -lc++ -weak_framework CoreMotion -weak-ISystem
  • Write Link Map File: Yes
Search Paths
  • Search Paths: Header Search Paths:
    • $(SRCROOT)/Libraries
    • $(SRCROOT)/Classes/Unity
    • $(SRCROOT)/Classes/PluginBase
    • $(SRCROOT)/Classes/UI
    • $(SRCROOT)/Classes
    • $(SRCROOT)
    • $(SRCROOT)/Classes/Native
    • $(SRCROOT)/Libraries/bdwgc/include
    • $(SRCROOT)/Libraries/libil2cpp/include
  • Library Search Paths
    • $(inherited)
    • $(SRCROOT)
    • $(PROJECT_DIR)/Libraries
    • $(PROJECT_DIR)/Libraries/Plugins/iOS
  • Apple LLVM 7.1 - Custom Compiler Flags: Other C Flags: -DINIT_SCRIPTING_BACKEND=1
  • Apple LLVM 7.1 - Custom Compiler Flags: Other C++ Flags: -DINIT_SCRIPTING_BACKEND=1
  • Apple LLVM 7.1 - Language: C Language Dialect: C99[-std=c99]
  • Apple LLVM 7.1 - Language: Precompiled Prefix Header: Yes
  • Apple LLVM 7.1 - Language: Prefix Header: /path/to/custom/prefix/header/added/above/PrefixHeader.pch
  • Apple LLVM 7.1 - Language - C++: C++ Language Dialect: C++11 [-std=c++11]
  • Apple LLVM 7.1 - Language - C++: C++ Standard Library: libc++ (LLVM C++ standard library with C++11 support)
  • Apple LLVM 7.1 - Language - C++: Enabled C++ Runtime Types: No
  • Apple LLVM 7.1 - Language - Modules: Enabled Modules (C and Objective-C): No
  • Apple LLVM 7.1 - Preprocessing: Enabled Foundation Assertions: Yes
  • Apple LLVM 7.1 - Preprocessing: Enabled Strict Checking of objc_msgSend Calls: No
  • Apple LLVM 7.1 - Warnings - All Languages: Empty Loop Bodies: No
  • Apple LLVM 7.1 - Warnings - All Languages: Implicit Boolean Conversions: No
  • Apple LLVM 7.1 - Warnings - All Languages: Implicit Constant Conversions: No
  • Apple LLVM 7.1 - Warnings - All Languages: Implicit Conversion to 32 Bit Type: No
  • Apple LLVM 7.1 - Warnings - All Languages: Implicit Enum Conversions: No
  • Apple LLVM 7.1 - Warnings - All Languages: Implicit Integer to Pointer Conversions: No
  • Apple LLVM 7.1 - Warnings - All Languages: Mismatched Return Type: Yes
  • Apple LLVM 7.1 - Warnings - All Languages: Uninitialized Variables: No
  • Apple LLVM 7.1 - Warnings - All Languages: Unreachable Code: No
  • Apple LLVM 7.1 - Warnings - All Languages: Unused Functions: No
  • Apple LLVM 7.1 - Warnings - Objective C: Direct Usage of 'isa': Yes
  • Apple LLVM 7.1 - Warnings - Objective C: Duplicate Method Definitions: No
  • Apple LLVM 7.1 - Warnings - Objective C: Overriding Deprecated Objective-C Methods: Yes
  • Apple LLVM 7.1 - Warnings - Objective C: Undeclared Selector: No
  • Apple LLVM 7.1 - Warnings - Objective C: Unintentional Root Class: Yes
  • Static Analyzer - Analysis Policy: Misuse of 'nonnull': No
  • Swift Compiler - Code Generation: Objective-C Bridging Header: /path/to/custom/bridging/header.h
  • User-Defined: GCC_THUMB_SUPPORT: No (May have to add by hand)
  • User-Defined: GCC_USE_INDIRECT_FUNCTION_CALLS: No (May have to add by hand)
  • User-Defined: MTL_ENABLE_DEBUG_INFO: No (May have to add by hand)
  • User-Defined: UNITY_RUNTIME_VERSION: 5.3.5f1 (May have to add by hand)
  • User-Defined: UNITY_SCRIPTING_BACKEND: il2cpp (May have to add by hand)

Run Script

From your Unity project, add the MapFileParse and MapFileParse.sh files to the root of your Xcode project.  Under the Build Phases tab of your xcodeproj file add a Run Script to make sure that the following two commands below run every time your project compiles.  This script will use the MapFileParse shell script to add new data to the data directory to be used by Unity and Vuforia.  Make sure that the shell environment is set to /bin/sh.

 

"$PROJECT_DIR/MapFileParser.sh"
rm -rf "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data/Raw/QCAR"

Code Integrtion

Now, the moment we have all been waiting for, the code integration!  The first thing that you need to do is locate the main.mm file in the Classes group.  The main.mm should look like this:

#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#include <csignal>
 
// 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();
        UnityParseCommandLine(argc, argv);
 
        RegisterMonoModules();
        NSLog(@"-> registered mono modules %p\n", &constsection);
        RegisterFeatures();
 
        // iOS terminates open sockets when an application enters background mode.
        // The next write to any of such socket causes SIGPIPE signal being raised,
        // even if the request has been done from scripting side. This disables the
        // signal and allows Mono to throw a proper C# exception.
        std::signal(SIGPIPE, SIG_IGN);
 
        UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
    }
 
    return 0;
}

We need to either delete or rename the main.mm file to anything but main.mm.  I chose to delete the file because it is not needed anymore except for reference if you want to look at it.  Next, create a new Objective-C++ file called UnityHook by clicking on your xcodeproj file > File > New > File... > Cocoa Touch Class and name it UnityHook.  When Xcode creates this file change the name of the .m file to UnityHook.mm so Xcode knows to compile this file as Objective-C++.  This will be the entry point fire up Unity when your Swift project runs.  

Add the following to your UnityHook.h file:

#ifndef UnityUtils_h
#define UnityUtils_h
 
 
void custom_unity_init(int argc, char* argv[]);
 
#endif /* UnityUtils_h */

Add the following to your UnityHook.mm file, if your project does not recognize <csignal> right away, that is OK.  You may have to build your project first for this to happen.

#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#include <csignal>
 
// 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();
 
 
extern "C" void custom_unity_init(int argc, char* argv[]){
    UnityInitTrampoline();
    UnityParseCommandLine(argc, argv);
 
    RegisterMonoModules();
    NSLog(@"-> registered mono modules %p\n", &constsection);
    RegisterFeatures();
 
    // iOS terminates open sockets when an application enters background mode.
    // The next write to any of such socket causes SIGPIPE signal being raised,
    // even if the request has been done from scripting side. This disables the
    // signal and allows Mono to throw a proper C# exception.
    std::signal(SIGPIPE, SIG_IGN);
}

Now, go back to that ProjectName-Bridging-Header.h you created earlier and added the following headers to it to expose these objects to Swift.

#import "UnityHook.h"
#import "UnityAppController.h"
#import "Unity/UnityInterface.h"

Out in the folder Libraries/Plugins/iOS folder there is a file called VuforiaNativeRendererController.mm.  We need to comment out the last line to make the file look like this:

#import "UnityAppController.h"
#import "VuforiaRenderDelegate.h"
 
 
// Unity native rendering callback plugin mechanism is only supported 
// from version 4.5 onwards
#if UNITY_VERSION>434
 
// Exported methods for native rendering callback
extern "C" void VuforiaSetGraphicsDevice(void* device, int deviceType, int eventType);
extern "C" void VuforiaRenderEvent(int marker);
 
#endif
 
// Controller to support native rendering callback
@interface VuforiaNativeRendererController : UnityAppController
{
}
- (void)shouldAttachRenderDelegate;
@end
 
@implementation VuforiaNativeRendererController
 
- (void)shouldAttachRenderDelegate
{
    self.renderDelegate = [[VuforiaRenderDelegate alloc] init];
 
// Unity native rendering callback plugin mechanism is only supported 
// from version 4.5 onwards
#if UNITY_VERSION>434
    UnityRegisterRenderingPlugin(&VuforiaSetGraphicsDevice, &VuforiaRenderEvent);
#endif
}
@end
 
// This line below
//IMPL_APP_CONTROLLER_SUBCLASS(VuforiaNativeRendererController)

Next out in our Classes folder there should be a file named UnityAppController.h.  This is the header file that we are going to work with to interact with Unity in Swift over the bridge.  There are a couple of alteration that we need to make to this file.  Here they are listed below:

  • Add UIKit.  For some reason when this file is loaded over the bridge now if the UIKit objects are recognized.
  • Add the class declaration for UnityViewControllerBasel before the inteface declaration for UnityAppController.  This lets clang know that the type UnityViewControllerBase WILL be loaded.
  • Expose all six of the application state functions as prototypes in the header.
  • Comment out the existing delegate function and create a new one that conforms with Swift.
#pragma once
 
#import <QuartzCore/CADisplayLink.h>
// ******** ADDED ********
#import <UIKit/UIKit.h>
#include "PluginBase/RenderPluginDelegate.h"
 
@class UnityView;
@class DisplayConnection;
@class UnityViewControllerBase;
 
 
@interface UnityAppController : NSObject<UIApplicationDelegate>
{
    UnityView*          _unityView;
    CADisplayLink*      _displayLink;
 
    UIWindow*           _window;
    UIView*             _rootView;
    UIViewController*   _rootController;
    UIView*             _snapshotView;
 
    DisplayConnection*  _mainDisplay;
 
    // we will cache view controllers for fixed orientation
    // auto-rotation view contoller goes to index=0
    UnityViewControllerBase * _viewControllerForOrientation[5];
 
#if !UNITY_TVOS
    UIInterfaceOrientation  _curOrientation;
#endif
 
    id<RenderPluginDelegate>    _renderDelegate;
}
 
// ** ADDED ** All six of these function prototypes were added
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
- (void)applicationWillResignActive:(UIApplication*)application;
- (void)applicationDidEnterBackground:(UIApplication*)application;
- (void)applicationWillEnterForeground:(UIApplication*)application;
- (void)applicationDidBecomeActive:(UIApplication*)application;
- (void)applicationWillTerminate:(UIApplication*)application;
 
 
// override it to add your render plugin delegate
- (void)shouldAttachRenderDelegate;
 
// this one is called at the very end of didFinishLaunchingWithOptions:
// after views have been created but before initing engine itself
// override it to register plugins, tweak UI etc
- (void)preStartUnity;
 
// this one is called at first applicationDidBecomeActive
// NB: it will be started with delay 0, so it will run on next run loop iteration
// this is done to make sure that activity indicator animation starts before blocking loading
- (void)startUnity:(UIApplication*)application;
 
// this is a part of UIApplicationDelegate protocol starting with ios5
// setter will be generated empty
@property (retain, nonatomic) UIWindow* window;
 
@property (readonly, copy, nonatomic) UnityView*            unityView;
@property (readonly, copy, nonatomic) CADisplayLink*        unityDisplayLink;
 
@property (readonly, copy, nonatomic) UIView*               rootView;
@property (readonly, copy, nonatomic) UIViewController*     rootViewController;
@property (readonly, copy, nonatomic) DisplayConnection*    mainDisplay;
 
#if !UNITY_TVOS
@property (readonly, nonatomic) UIInterfaceOrientation      interfaceOrientation;
#endif
 
@property (nonatomic, retain) id                            renderDelegate;
@property (nonatomic, copy)                                 void(^quitHandler)();
 
@end
 
// Put this into mm file with your subclass implementation
// pass subclass name to define
 
#define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) \
@interface ClassName(OverrideAppDelegate)       \
{                                               \
}                                               \
+(void)load;                                    \
@end                                            \
@implementation ClassName(OverrideAppDelegate)  \
+(void)load                                     \
{                                               \
    extern const char* AppControllerClassName;  \
    AppControllerClassName = #ClassName;        \
}                                               \
@end                                            \
 
// ******** Commented Out ********
//inline UnityAppController*    GetAppController()
//{
//  return (UnityAppController*)[UIApplication sharedApplication].delegate;
//}
 
// ******** ADDED ********
NS_INLINE UnityAppController* GetAppController()
{
    NSObject<UIApplicationDelegate>* delegate = [UIApplication sharedApplication].delegate;
    UnityAppController* currentUnityController = (UnityAppController *)[delegate valueForKey:@"currentUnityController"];
    return currentUnityController;
}
 
 
#define APP_CONTROLLER_RENDER_PLUGIN_METHOD(method)                         \
do {                                                                        \
    id<RenderPluginDelegate> delegate = GetAppController().renderDelegate;  \
    if([delegate respondsToSelector:@selector(method)])                     \
        [delegate method];                                                  \
} while(0)
 
#define APP_CONTROLLER_RENDER_PLUGIN_METHOD_ARG(method, arg)                \
do {                                                                        \
    id<RenderPluginDelegate> delegate = GetAppController().renderDelegate;  \
    if([delegate respondsToSelector:@selector(method:)])                    \
        [delegate method:arg];                                              \
} while(0)
 
 
 
// these are simple wrappers about ios api, added for convenience
void AppController_SendNotification(NSString* name);
void AppController_SendNotificationWithArg(NSString* name, id arg);
 
void AppController_SendUnityViewControllerNotification(NSString* name);

Next, in the UnityAppController.mm file, there are a few changes needed to be make.  Below are the outlined changes.

  • Add the VuforiaRenderedDelegate.h to the bottom of the import list.
  • Remove the empty shouldAttachRendereDelegate method and replace it with the VudoriaRenderDelegate.
// ******** ADDED ********
// Add to top
#import "VuforiaRenderDelegate.h"
 
 
 
 
/*
   ******** START ADDED ********
   Replace the following method stub:
   - (void)shouldAttachRenderDelegate {}
   with what is below:
 */
 
// Unity native rendering callback plugin mechanism is only supported
// from version 4.5 onwards
#if UNITY_VERSION>434
 
// Exported methods for native rendering callback
extern "C" void VuforiaSetGraphicsDevice(void* device, int deviceType, int eventType);
extern "C" void VuforiaRenderEvent(int marker);
 
#endif
 
- (void)shouldAttachRenderDelegate
{
    self.renderDelegate = [[VuforiaRenderDelegate alloc] init];
 
    // Unity native rendering callback plugin mechanism is only supported
    // from version 4.5 onwards
#if UNITY_VERSION>434
    UnityRegisterRenderingPlugin(&VuforiaSetGraphicsDevice, &VuforiaRenderEvent);
#endif
}
 
/*
   ******** END ADDED ********
 */

Next, it is time to finally get into the Swift part of our project.  Navigate out to the AppDelegate.swift file and remove the @UIApplicationMain decorator above the class declaration for the AppDelegate.

import UIKit
 
//@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
 
    var window: UIWindow?
}

Next, we need to create a custom main.swift file.  Notice that this is not loaded into the project but can be overridden by added a custom one.  In our main.swift file we will make a call to the UnityHook file to load up Unity before the actual Swift project is fired up.  

import Foundation
// overriding @UIApplicationMain
// http://stackoverflow.com/a/24021180/1060314
 
custom_unity_init(CommandLine.argc,CommandLine.unsafeArgv)
// Entry point to the application
UIApplicationMain(CommandLine.argc,
                  UnsafeMutableRawPointer(CommandLine.unsafeArgv)
                    .bindMemory(
                        to: UnsafeMutablePointer<Int8>.self,
                        capacity: Int(CommandLine.argc)),
                  nil,
                  NSStringFromClass(AppDelegate.self)
)

Next, in the AppDelegate.swift file we can load the UnityAppController object and call the delegate life cycle functions from here when needed.  Alter the AppDelegate.swift file to look like the following below:

import UIKit
 
//@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
 
    var window: UIWindow?
    var currentUnityController: UnityAppController!
 
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
 
        currentUnityController = UnityAppController()
        currentUnityController.application(application, didFinishLaunchingWithOptions: launchOptions)
        return true
    }
 
    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
        currentUnityController.applicationWillResignActive(application)
    }
 
    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        currentUnityController.applicationDidEnterBackground(application)
    }
 
    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
        currentUnityController.applicationWillEnterForeground(application)
    }
 
    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
        currentUnityController.applicationDidBecomeActive(application)
    }
 
    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
        currentUnityController.applicationWillTerminate(application)
    }
}

And so finally, the moment we have all been waiting for!  Now we can load up Unity anywhere in our project by adding the following code below to create a UnityView and load it to the UIViewController.

class MyARBrowserViewController: UIViewController {
 
    //
    // ViewDidLoad
    //
    override func viewDidLoad() {
 
        super.viewDidLoad()
 
        let unityView = UnityGetGLView()
 
        self.view.addSubview(unityView)
        unityView.translatesAutoresizingMaskIntoConstraints = false
    }
}

That is it!  Hopefully you found this post helpful and if you did or your happened to see an issue with this post I would love to hear your feedback.  Leave a comment or start a discussion and I will update the post.  Thank you very much!

Upadted for for iOS 10, Swift 3, and Vuforia 6 on 10/1/2016.

Member for

3 years 9 months
Matt Eaton

Long time mobile team lead with a love for network engineering, security, IoT, oss, writing, wireless, and mobile.  Avid runner and determined health nut living in the greater Chicagoland area.

Comments

I do Unity Swift integration and now I have the Unity project as a Subview in my existing Xcode project.
In the Unity folder classes (inside my Xcode existing project) I have many .cpp files with extern"c" functions and I can see the value in the Xcode console (because I put in the native Unity project a debug log for see the value) but I can not find the way to pass the value in my swift project.
how can I access a unity value (for example a score value) into my Swift code? I see the link you gave me, but I don't know if that is the way because my integration is already done.

Thank you very much!

Abhinav Mandloi

Fri, 09/08/2017 - 05:52 PM

1. /Users/admin/Documents/iOSSwiftDemo/iOSSwift/UnitySwiftDemo/UnitySwiftDemo/ViewController.swift:18:25: Use of unresolved identifier 'UnityGetGLView'
2. /Users/admin/Documents/iOSSwiftDemo/iOSSwift/UnitySwiftDemo/UnitySwiftDemo/AppDelegate.swift:15:33: Use of undeclared type 'UnityAppController'
3. /Users/admin/Documents/iOSSwiftDemo/iOSSwift/UnitySwiftDemo/UnitySwiftDemo/AppDelegate.swift:20:34: Use of unresolved identifier 'UnityAppController'
4. /Users/admin/Documents/iOSSwiftDemo/iOSSwift/UnitySwiftDemo/main.swift:13:1: Use of unresolved identifier 'custom_unity_init'
5. /Users/admin/Documents/iOSSwiftDemo/iOSSwift/UnitySwiftDemo/main.swift:15:1: Use of unresolved identifier 'UIApplicationMain'

Thanks in advance:)

Gourav

Thu, 09/14/2017 - 02:23 PM

I am facing this issue on running the App.

Ld /Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Products/Debug-iphoneos/SampleAR.app/SampleAR normal arm64
cd /Users/gjamwal/Documents/Study/SampleAR
export IPHONEOS_DEPLOYMENT_TARGET=10.0
export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk -L/Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Products/Debug-iphoneos -L/Users/gjamwal/Documents/Study/SampleAR -L/Users/gjamwal/Documents/Study/SampleAR/../../../Desktop/Unity/geoardemo40/Libraries -L/Users/gjamwal/Documents/Study/SampleAR/../../../Desktop/Unity/geoardemo40/Libraries/Plugins/iOS -L/Users/gjamwal/Documents/Study/SampleAR/../../../Desktop/Unity/geoardemo40/Libraries/Plugins -F/Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Products/Debug-iphoneos -filelist /Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Intermediates/SampleAR.build/Debug-iphoneos/SampleAR.build/Objects-normal/arm64/SampleAR.LinkFileList -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -map -Xlinker /Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Intermediates/SampleAR.build/Debug-iphoneos/SampleAR.build/SampleAR-LinkMap-normal-arm64.txt -miphoneos-version-min=10.0 -dead_strip -Xlinker -object_path_lto -Xlinker /Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Intermediates/SampleAR.build/Debug-iphoneos/SampleAR.build/Objects-normal/arm64/SampleAR_lto.o -Xlinker -export_dynamic -Xlinker -no_deduplicate -stdlib=libc++ -fobjc-arc -fobjc-link-runtime -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos -Xlinker -add_ast_path -Xlinker /Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Intermediates/SampleAR.build/Debug-iphoneos/SampleAR.build/Objects-normal/arm64/SampleAR.swiftmodule -lc++ -weak_framework CoreMotion -weak-lSystem -framework AVKit -framework MediaToolbox -framework Security -liconv.2 -framework UIKit -framework SystemConfiguration -framework QuartzCore -framework OpenGLES -framework OpenAL -framework MediaPlayer -weak_framework iAd -framework Foundation -framework CoreVideo -weak_framework CoreMotion -framework CoreMedia -framework CoreLocation -framework CoreGraphics -framework CFNetwork -weak_framework AVFoundation -framework AudioToolbox -framework CoreText -liPhone-lib -lVuforia -lil2cpp -lVuforiaUnityPlayer -Xlinker -dependency_info -Xlinker /Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Intermediates/SampleAR.build/Debug-iphoneos/SampleAR.build/Objects-normal/arm64/SampleAR_dependency_info.dat -o /Users/gjamwal/Library/Developer/Xcode/DerivedData/SampleAR-emojapyyoaotzvavcbfdyovamdxe/Build/Products/Debug-iphoneos/SampleAR.app/SampleAR

Undefined symbols for architecture arm64:
"_AppControllerClassName", referenced from:
+[VuforiaNativeRendererController(OverrideAppDelegate) load] in VuforiaNativeRendererController.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Can anyone help me to fix this? I have tried everything but nothing worked.

Penney

Fri, 09/15/2017 - 07:24 AM

Hi, i am developing native ios with unity, When my app startup, i just want to load Unityengine relative library, set graphic setting , and show my custom native UIVIew ,rather than immediately show unity view .
I think i can show my custom view in the front and process unity view in the background, but i don't think it is a better way .

Yes, this is difficult situation.  This is possible but it would take a bit of tweaking to get right.  The current tutorial loads right to a root view controller that instantiates Unity and Vuforia, and then allows you to place a GLView from there where needed.  You would have to take the instantiation code out of this view controller and figure out a new logical place to start it without causing a user experience lag.  Then place you view as needed.  This can be done, but is outside the scope of this tutorial.

Esteban

Fri, 09/15/2017 - 03:18 PM

Good morning

my thanks for the good tutorial helped me a lot.

but it is generating an error, when I try to compile the app, in the archibo AppDelegate, in the following line.

currentUnityController.application (application, didFinishLaunchingWithOptions: launchOptions)

with the following error

0x100f6f9c8 <+112>: mov w8, w19
0x100f6f9cc <+116>: ldr x9, [x21, # 0x528]
0x100f6f9d0 <+120>: ldrsw x9, [x9, # 0xbc]
0x100f6f9d4 <+124>: umulh x9, x9, x23
0x100f6f9d8 <+128>: add w19, w19, # 0x1; = 0x1
0x100f6f9dc <+132>: cmp x8, x9, lsr # 6
0x100f6f9e0 <+136>: b.lo 0x100f6f9c0; <+104> at MetadataCache.cpp: 165
0x100f6f9e4 <+140>: adrp x19, 1063
0x100f6f9e8 <+144>: ldr x8, [x19, # 0x510]
-> 0x100f6f9ec <+148>: ldrsw x0, [x8, # 0x30]
0x100f6f9f0 <+152>: orr w1, wzr, # 0x8
0x100f6f9f4 <+156>: bl 0x100f6210c; il2cpp :: utils :: Memory :: Calloc (unsigned long, unsigned long)
0x100f6f9f8 <+160>: adrp x8, 1063
0x100f6f9fc <+164>: str x0, [x8, # 0x530]
0x100f6fa00 <+168>: ldr x8, [x21, # 0x528]
0x100f6fa04 <+172>: ldrsw x8, [x8, # 0xa4]
0x100f6fa08 <+176>: mov x9, # 0x4ec4000000000000
0x100f6fa0c <+180>: movk x9, # 0xec4e, lsl # 32

in the log appears

2017-09-15 09: 04: 39.159060-0500 JockeyPlaza [4813: 1906768] [DYMTLInitPlatform] platform initialization successful

2017-09-15 09: 04: 39.587745-0500 JockeyPlaza [4813: 1906724] -> registered mono modules 0x101022c78
-> applicationDidFinishLaunching ()
JockeyPlaza was compiled with optimization - stepping may behave oddly; variables may not be available.

Any help would be very helpful at this time thank you very much.

Kaung Zay Thu

Wed, 09/20/2017 - 04:30 AM

I got issue `Texture at stencilAttachment has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)`

I also found solution by disabling GPU Frame Capture in the application's Scheme. Find that setting under Edit Scheme > Run > Options > GPU Frame Capture. Set it to Disabled.

Kaung Zay Thu

Sun, 10/08/2017 - 09:36 AM

Hello @matt_eaton,
i tried to terminate unity by following
func stopUnity() {
if isUnityRunning {
isUnityRunning = false
currentUnityController!.applicationWillResignActive(application!)
}
}

but when i start unity by clicking button again, it resumes the unity and it does not start from beginning.
Please help me.

Thank you

Sahil Mahajan

Mon, 10/23/2017 - 06:39 PM

Hi

I have implemented the same but When I add UnityGetGLView() as subview in my viewController's view it just blink the splash screen and then gone. I am not sure what I am missing while implementing.

Attaching the console logs. Hope it helps you

2017-10-24 00:02:22.556249+0530 UnityTest[2390:1059936] -> registered mono modules 0x10192ce70
-> applicationDidFinishLaunching()
Player data archive not found at `/var/containers/Bundle/Application/ACC82FD7-60AD-4939-97A0-56DA894F7777/UnityTest.app/Data/data.unity3d`, using local filesystem
2017-10-24 00:02:24.028424+0530 UnityTest[2390:1059936] [MC] Lazy loading NSBundle MobileCoreServices.framework
2017-10-24 00:02:24.034992+0530 UnityTest[2390:1059936] [MC] Loaded MobileCoreServices.framework
2017-10-24 00:02:24.060986+0530 UnityTest[2390:1059936] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2017-10-24 00:02:24.333189+0530 UnityTest[2390:1059936] refreshPreferences: HangTracerEnabled: 0
2017-10-24 00:02:24.333335+0530 UnityTest[2390:1059936] refreshPreferences: HangTracerDuration: 500
2017-10-24 00:02:24.333414+0530 UnityTest[2390:1059936] refreshPreferences: ActivationLoggingEnabled: 0 ActivationLoggingTaskedOffByDA:0
-> applicationDidBecomeActive()
GfxDevice: creating device client; threaded=1
Initializing Metal device caps: Apple A7 GPU
Initialize engine version: 2017.1.1f1 (5d30cf096e79)
UnloadTime: 0.979208 ms

I m using xcode 9.0 and swift 4.0.

vijay kumar

Fri, 12/22/2017 - 10:23 AM

I Integrated the unity in iOS(Objective C) it is working fine but the problem is unity is running in background. i dont want like that when i tap on the button only unity should start running. Please help.

I think one option to try would be to pause Unity running in the background.  That way Unity is not starting and stopping.  In the UnityAppController.mm file create an Objective-C file that Swift can cause that will pause Unity running until you need it.  A code example on how to pause and un-pause Unity is available in applicationWillResignActive in UnityAppController.mm.  Hope That helps.

Damini

Thu, 04/12/2018 - 11:11 PM

Hi Matt,
If I comment this line IMPL_APP_CONTROLLER_SUBCLASS(VuforiaNativeRendererController) from VuforiaNativeRendererController.mm, the orientation does not work properly. In iPhone, it does not rotate and in iPad, it rotates on 2/3rd of the screen and that too not properly.

If I uncomment the same line, my rotation works properly but I am unable to see native content in the project.
What could be the possible reason for the same?
Any help would be greatly appreciated.
Thank you so much in advance.

Florentin

Sat, 08/18/2018 - 05:58 PM

Hi Matt. Is possible to make a video on youtube doing this tutorial and integrating Unity into a simple Swift project ? Can be any model behind ( a square, a dice...anything). Will be great if you can do this video using Unity 2017/2018 and xCode 9 with Swift 4. All the best !

HI, great tutorial, everything works well, besides one simple issue I'm having relating to auto-rotation. When I rotate my device (iPhoneX) - e.g. from portrait to landscape right/left, unity seems to double rotate and cuts the screen in half leaving the other half pitch black.
Any thoughts??? Thanks!!
Build details:
Unity 2018.2.7f1
Xcode 10.0
iOS 10