Quickstart in iOS Swift

Integrate the ARwayKit SDK with iOS Swift

In this quickstart guide, we will be integrating the ARwayKit SDK with iOS Swift. This example will be using Unity as a Library to integrate into an iOS app.

Installed Versions

In this guide, we will be using the following versions:

  • Xcode: 14.3

  • Unity: 2022.3.17f1 LST

Download the ARwayKit SDK

Download the ARwayKit SDK project from GitHub as a ZIP. You can contact us for access to the ARwayKit SDK.

Create a Basic Project in Xcode

For this example a new iOS app called "SwiftUnity" will be created with interface "Storyboard" and language "Swift".

Generate the Build for the iOS Platform

Make sure to add the Account ID and Secret Key variables to the Unity project. Follow the guide for Building from the Source Code for instructions.

Updating the Addressables Groups

When opening the project in Unity for the first time, you will need to build the Addressables Groups.

  1. Switch the build platform to either iOS or Android

    • "File-> Build Settings..." then select the Android or iOS as the Platform.

  2. Click "Switch Platform" on the bottom right.

  3. Navigate to "Window -> Asset Management -> Addressable -> Groups".

  4. In the Addressables Groups window, click on "Build -> New Build -> Default Build Script".

  5. In the Console window, you should see the success message "Addressable content successfully built".

When making any changes that affect localization strings, you will need to update the Addressables Groups.

Building the Project

Steps

  1. In Unity select File -> Build Settings.

  2. Switch the Platform to iOS.

  3. Select option "Build Project".

  1. Export the ARwayKit Unity SDK to a new folder and name it "iOSBuild" and place it in the base directory for the iOS project.

Setup Xcode workspace

Xcode workspace allows to work on multiple projects simultaneously and combine their products

  1. Open your Xcode project.

  2. All the steps are done from just created Workspace project.

Add UnityFramework.framework

With this step we add Unity player in the form of a framework to NativeiOSApp, it does not change the behavior of NativeiOSApp yet

  1. Select NativeiOSApp target from NativeiOSApp project.

  2. In "General" tab / "Frameworks, Libraries, and Embedded Content" press +.

  3. Add Unity-iPhone/UnityFramework.framework.

  4. In "Build Phases" tab, expand "Link Binary With Libraries".

  5. Remove UnityFramework.framework from the list (select it and press - ).

Make Data folder to be part of the UnityFramework

By default Data folder is part of Unity-iPhone target, we change that to make everything encapsulated in one single framework file.

  1. Change Target Membership for Data folder to UnityFramework.

Next, open Info.plist and add the following rows:

  • Privacy - Microphone Usage Description: Used for creating audio content

  • Privacy - Camera Usage Description: Used for AR Content and mapping

  • Privacy - Location Usage Description: Used to attach maps with global coordinates and public map searching

  • Privacy - Location When in Usage Description: Used to attach maps with global coordinates and public map searching

  • Privacy - Photo Library Usage Description: This app requires access to save media

  • Privacy - Photo Library Additions Usage Description: This app requires access to save media

Swift Code

To create an entry point to SwiftUI, create a new SwiftUI view called ContentView.swift and add a single button to launch ARway.

ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Button(action: {
            Unity.shared.show()
        }) {
            Text("Launch ARway!")
        }
    }
}

Next, update the ViewController.swift file to match the following.

ViewController.swift
import UIKit
import SwiftUI

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let vc = UIHostingController(rootView: ContentView())
        addChild(vc)
        vc.view.frame = self.view.frame
        view.addSubview(vc.view)
        vc.didMove(toParent: self)
    }
}

Next, implement the connection between the Unity SDK and the Swift app. A new singleton called Unity.swift is created with the following code.

Unity.swift
import Foundation
import UnityFramework

class Unity: UIResponder, UIApplicationDelegate {
    static let shared = Unity()

    private let dataBundleId = "com.unity3d.framework"
    private let frameworkPath = "/Frameworks/UnityFramework.framework"

    private lazy var unityFramework: UnityFramework? = {
        guard let bundle = loadUnityFramework() else {
            return nil
        }
        let ufw = bundle.principalClass?.getInstance()
        ufw?.setDataBundleId(dataBundleId)
        ufw?.register(self)
        return ufw
    }()

    private var hostMainWindow: UIWindow?

    private var isInitialized: Bool {
        unityFramework?.appController() != nil
    }

    func show() {
        guard isInitialized else {
            initWindow()
            return
        }
        showWindow()
    }

    func setHostMainWindow(_ hostMainWindow: UIWindow?) {
        self.hostMainWindow = hostMainWindow
    }

    private func initWindow() {
        guard !isInitialized else {
            showWindow()
            return
        }
        guard let unityFramework = unityFramework else {
            print("ERROR: Was not able to load Unity")
            return unloadWindow()
        }
        self.unityFramework = unityFramework
        unityFramework.runEmbedded(withArgc: CommandLine.argc, argv: CommandLine.unsafeArgv, appLaunchOpts: nil)
    }

    private func showWindow() {
        guard let unityFramework = unityFramework else {
            return
        }
        unityFramework.showUnityWindow()
    }

    private func unloadWindow() {
        guard let unityFramework = unityFramework else {
            return
        }
        unityFramework.unloadApplication()
    }

    private func loadUnityFramework() -> Bundle? {
        let bundlePath = Bundle.main.bundlePath + frameworkPath
        guard let bundle = Bundle(path: bundlePath), !bundle.isLoaded else {
            return nil
        }
        do {
            try bundle.loadAndReturnError()
        } catch {
            print("ERROR: Failed to load Unity framework: \(error)")
            return nil
        }
        guard unityFramework?.appController() == nil else {
            return bundle
        }
        let machineHeader = UnsafeMutablePointer<MachHeader>.allocate(capacity: 1)
        machineHeader.pointee = _mh_execute_header
        unityFramework?.setExecuteHeader(machineHeader)
        return bundle
    }
}

extension Unity: UnityFrameworkListener {
    func unityDidUnload(_ notification: Notification!) {
        unityFramework?.unregisterFrameworkListener(self)
        unityFramework = nil
        hostMainWindow?.makeKeyAndVisible()
    }
}

Next update the AppDelegate.swift file to pass the main window reference to Unity.

AppDelegate.swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Unity.shared.setHostMainWindow(window)
        return true
    }
}

You should now be able to build and run the project on an iOS device.

To run the app, you will need to use a physical device and not a simulator.

Additional information about integrating Unity as a library into a standard iOS app can be found in the following links:

https://github.com/Unity-Technologies/uaal-example/blob/uaal-example/19LTS-21LTS/docs/ios.md

https://docs.unity3d.com/2019.3/Documentation/Manual/UnityasaLibrary.html

Last updated