Building an iOS app with Targets

AR with surface detection

In this tutorial we are going to walk you through all necessary steps to integrate Onirix with your native Swift app. The goal of the tutorial is to create a simple marker-based AR experience with a 'back button' to leave the application.

You find the used 3D model of the Arcade Machines here. Credits go to Brad Crankshaw.

Setup and Tools

These are the tools we are going to need:

  1. Onirix Studio: the base for our AR project.
  2. Unity is the most widely used 3D engine for mobile applications. Unity is cross-platform, which allows developers to deploy the same app on multiple platforms.
  3. Onirix SDK in its mobile version, allows us to create marker-based experiences
  4. XCode a Swift project.
  5. Automation scripts by Jiulong Wang
  6. The Full code is available on Github (Automation scripts, Unity and XCodes projects)

Step 1: Create a new project in Onirix and an app in Unity

We start with creating a new project in Onirix Studio. For the type we choose 'Targets' and use the scene editor to add our AR content. Both, Surface or Marker work in in this tutorial. For more information about how to create a Unity app, please see Hello World with Targets (marker) or Hello World with Targets (surface).

Step 2: Export to iOS

Before we can build for iOS, we need to create a folder called Editor into Scripts folder and drag & drop the file XcodePostBuild.cs script into it.

We have to edit two parameters inside this script:

  • XcodeProjectRoot Change the XCode project path. This is the path from root folder of our Unity project to the folder containing the XCode .xcodeproj file.
  • XcodeProjectName Change the XCode project name.

XcodePostBuild edit

The screenshot above shows the parameters that we have to change. onirix-showroom is the Unity project folder and market is the XCode project folder.

To build the Unity project for iOS, save it to your XCode project folder.

Export Unity project

Remember: Before building for iOS you have to check a few settings settings. Go to Hello World with Targets (surface) to know which ones are relevant.

Step 3: Import to iOS

Change to your XCode project.

Step 3.1: The Unity folder

First, we are going to check if a Unity folder has been created (during theUnity build) inside our XCode project. This Unity folder must contain a file named Exports.xconfig and inside this file you should find the following parameters:

  • UNITY_RUNTIME_VERSION The version of Unity that has created the export.
  • UNITY_IOS_EXPORT_PATH The Unity export path.
  • In this case these are the following parameters. Depending on your version of Unity, those might be different.

    UNITY_RUNTIME_VERSION = 2018.2.7f1;
    UNITY_IOS_EXPORT_PATH = /.../Workspaces/onirix-ios-integration/market/onirix-showroom-export;

In the next step, we have to move the following files (you can find them at the beginning of the tutorial) to your Unity folder. If you don't have this folder, create it.

  • Bridging-Header.h
  • rsync_exclude
  • Unity.xcconfig
  • UnityUtils.h
  • UnityUtils.mm

Now drag & drop your Unity folder into your XCode navigator. Select Copy items if needed and Create groups in the appering dialog.

In this step we are going to copy some folders from your Unity export folder and paste it into our new Unity folder inside XCode. We are going to copy the following folders. For details check the video below.

  • Libraries
  • Classes
  • Data
  • Frameworks

Move the folders Classes and Libraries to your Unity folder inside the XCode navigator. Select Copy items if needed and Create groups in the appearing dialog.

And finally, move the folder data to the same place but do NOT select Copy Items if needed nor Create groups. Only select Create folder references.

Step 3.2: XCode settings

Go to project info and set:

  • iOS Deployment Target to 12.
  • Configurations profile to Unity.

Set configuration profile

Now add the following frameworks to your XCode project:

  • ARKit.framework adds some AR features.
  • opencv2.framework for images recognition, used to detect markers. opencv2.framework can be found inside the Unity / Frameworks folder.

Add frameworks

In order to use the camera of a mobile device, we have to ask for permission in Info.plist. Open the file and add a new row:

  • Key: Privacy -Camera Usage Description
  • Value: ${PRODUCT_NAME} camera use

Please see the video below for more details.

Step 4: Let's write some code

In this tutorial we are going to create a simple view controller with a 'back buttom'. When the experience is triggered, the Unity project will be loaded and when the user presses the 'back button', the experience will disappear.

Step 4.1: AppDelegate.swift

Let's do some changes to the application's core. In AppDelegate.swift we have to load the UnityAppController and to manage it through its life cycle. To do so, we need to add a boolean variable to save Unity's state. Add the following lines to your AppDelegate.swift :

@objc var currentUnityController: UnityAppController!
var isOnirixRunning = false

Now, go to application function and add the following lines in order to initialize the currentUnityController:

unity_init(CommandLine.argc, CommandLine.unsafeArgv)
currentUnityController = UnityAppController()
currentUnityController.application(application, didFinishLaunchingWithOptions: launchOptions)

When a native Swift app life cyle event is fired, we have to communicate this change to UnityAppController, by calling the same function (just if Unity is running). This is how it works:

func applicationWillResignActive(_ application: UIApplication) {

    // your code here

    if isOnirixRunning 
    {
        currentUnityController.applicationWillResignActive(application)
    }
}
func applicationDidEnterBackground(_ application: UIApplication) {

    // your code here

    if isOnirixRunning 
    {
        currentUnityController.applicationDidEnterBackground(application)
    }
}
func applicationWillEnterForeground(_ application: UIApplication) {

    // your code here

    if isOnirixRunning {
        currentUnityController.applicationWillEnterForeground(application)
    }
}
func applicationDidBecomeActive(_ application: UIApplication) {

    // your code here

    if isOnirixRunning 
    {
        currentUnityController.applicationDidBecomeActive(application)
    }
}

Finally, we add a few functions that allow us to start and stop our application.

func startOnirix() {
    if !isOnirixRunning
    {
        isOnirixRunning = true
        currentUnityController.applicationDidBecomeActive(application!)
    }
}
func stopOnirix() {
    if isOnirixRunning {
        currentUnityController.applicationWillResignActive(application!)
        isOnirixRunning = false
    }
}

Step 4.2: Launching Onirix

To use Onirix, your application has to call the AppDelegate.startOnirix() and the AppDelegate.stopOnirix() methods. For this tutorial, we create a new UIViewController called OnirixController. When the OnirixController appears, it launches the Onirix view. When disappears, it removes the Onirix view.

To load the Onirix view, add this function to your controller:

func loadOnirixView()
{
    if let unityView = UnityGetGLView()
    {
        unityView.isHidden = false
    }

    if let appDelegate = UIApplication.shared.delegate as? AppDelegate
    {
        appDelegate.startOnirix()
        NotificationCenter.default.addObserver(self, selector: #selector(handleUnityReady), name: NSNotification.Name("UnityReady"), object: nil)
    }
}

In this function we load AppDelegate and call startOnirix. Then we add an observer for the UnityReady notification. This notification will be raised by the UnityController.applicationWillResignActive method when the Unity app is ready. To handle this notification, we have to add the following:

@objc func handleUnityReady() {
    showUnitySubView()
}

The showUnitySubView method creates the Unity view and inserts it as a subview.

func showUnitySubView() {
    if let unityView = UnityGetGLView() {
        view?.insertSubview(unityView, at: 0)
        first = false
    }
}

In order to load Unity when our controller appears, we have to override viewDidAppear method by adding a call to loadOnirixView:

override func viewDidAppear(_ animated: Bool)
{
    super.viewDidAppear(animated)
    loadOnirixView()
}

In this tutorial, we are going to add a Back button to our controller. By click the controller, it disappears and the main screen is shown again. Add this function to remove the Unity view:

func removeOnirixView()
{
    if let appDelegate = UIApplication.shared.delegate as? AppDelegate
    {
        appDelegate.stopOnirix()
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name("UnityReady"), object: nil)

        if let unityView = UnityGetGLView()
        {
            view?.willRemoveSubview(unityView);
            unityView.isHidden = true
        }
    }
}

And call viewWillDisappear to overwrite the method:

override func viewWillDisappear(_ animated: Bool)
{
    removeOnirixView()
    super.viewWillDisappear(animated)
}

Congratulations for completing this tutorial! If you ran into any problems, please get in touch with our support. We are always ready to answer your questions!