2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity as a LibraryでUnityからSwiftの処理を呼ぶ方法

Last updated at Posted at 2026-01-08

Unity as a Library×Swiftの記事はシリーズになっています。
記事を順番に読み進めると、Unity as a LibraryをSwiftで使えるようになります。

はじめに

本記事は どすこい塾 Advent Calendar 2025 の18日目の記事です。
昨日も @uhooiUnityFrameworkをSwiftで使う方法 でした。

NaitveCallProxy を使い、Unity as a LibraryでUnity側からSwift側の処理を呼ぶ方法を紹介します。

環境

  • OS:macOS Sequoia 15.6(24G84)
  • Swift:6.2.1

NativeCallProxyの導入

まず NativeCallProxy を導入します。

モジュールマップの作成

NativeCallProxy 自体はすでに存在するのですが、私の環境ではそのままだと呼び出せませんでした。
そのためモジュールマップを作成します。

loki-ios/Packages/unity-framework/NativeCallProxy/module.modulemap
module NativeCallProxy {
  header "./../unity-framework/UnityFramework.xcframework/ios-arm64/UnityFramework.framework/Headers/NativeCallProxy.h"
  export *
}

NativeCallProxyの公開と追加

UnityFrameworkをSwiftで使う方法 で作成した Pacakge.swift にて NativeCallProxy のライブラリを公開します。

loki-ios/Packages/unity-framework/Package.swift
// swift-tools-version: 6.2

import PackageDescription

let package = Package(
    name: "UnityFramework",
    platforms: [
        .iOS(.v18),
    ],
    products: [
        .library(
            name: "UnityFramework",
            targets: ["UnityFramework"],
        ),
+         .library(
+             name: "NativeCallProxy",
+             targets: ["NativeCallProxy"],
+         ),
    ],
    dependencies: [
    ],
    targets: [
        .binaryTarget(
            name: "UnityFramework",
            path: "./UnityFramework.xcframework",
        ),
+         .systemLibrary(
+             name: "NativeCallProxy",
+             path: "./NativeCallProxy",
+         ),
    ],
)

公開された NativeCallProxy をiOSアプリ側の Package.swift へ追加します。

loki-ios/Packages/LokiPackage/Package.swift
// swift-tools-version: 6.2

import PackageDescription

let package = Package(
    name: "LokiPackage",
    platforms: [
        .iOS(.v18),
    ],
    dependencies: [
        .package(path: "./../unity-framework"),
    ],
    targets: [
        .target(
            name: "UnityCore",
            dependencies: [
                "UnityFramework",
+                 "NativeCallProxy",
            ],
            path: "Sources/Core/Unity",
        ),
    ],
)

NativeCallProxyの使い方

NativeCallProxy の使い方を紹介します。

以下の3ステップのみです。

  • NativeCallProxy をインポートする
  • FrameworkLibAPI.registerAPIforNativeCalls(self) でデリゲートを登録する
  • NativeCallsProtocol プロトコルに従ってデリゲートメソッドを実装する
loki-ios/Packages/LokiPackage/Sources/Core/Unity/Unity.swift
import MachO
+ import NativeCallProxy
import UnityFramework

@MainActor
public final class Unity: NSObject {
    public static let shared = Unity()
    private let unityFramework: UnityFramework

    var view: UIView? {
        unityFramework.appController()?.rootView
    }

    override init() {
        self.unityFramework = Self.loadUnityFramework()
        super.init()
    }

    public func run() {
        unityFramework.register(self)
+         FrameworkLibAPI.registerAPIforNativeCalls(self)
        unityFramework.setDataBundleId("com.unity3d.framework")

        // スレッド優先度が逆転するのを避けるため、一時的に下げる
        let originalPriority = Thread.current.threadPriority
        Thread.current.threadPriority = 0.5

        unityFramework.runEmbedded(withArgc: CommandLine.argc, argv: CommandLine.unsafeArgv, appLaunchOpts: nil)
        unityFramework.appController()?.window.isHidden = true

        // 元のスレッド優先度に戻す
        Thread.current.threadPriority = originalPriority
    }

    public func unload() {
        unityFramework.unloadApplication()
    }
}

// MARK: - UnityFrameworkListener

extension Unity: @MainActor UnityFrameworkListener {
    public func unityDidUnload(_ notification: Notification!) {
        // TODO: アンロード後の処理を実装する
        print(#function)
    }

    public func unityDidQuit(_ notification: Notification!) {
        // TODO: クィット後の処理を実装する
        print(#function)
    }
}

+ // MARK: - NativeCallsProtocol
+ 
+ extension Unity: @MainActor NativeCallsProtocol {
+   // TODO: サンプルのメソッドなので削除する
+   public func updateUnityShopItem() {
+     print(#function)
+   }
+ 
+   // TODO: サンプルのメソッドなので削除する
+   public func itemPlacedInAR() {
+     print(#function)
+   }
+ }
+ 
// MARK: - Privates

private extension Unity {
    static func loadUnityFramework() -> UnityFramework {
        let bundlePath = Bundle.main.bundlePath
        let frameworkPath = bundlePath + "/Frameworks/UnityFramework.framework"
        let bundle = Bundle(path: frameworkPath)!
        if !bundle.isLoaded {
            bundle.load()
        }
        let ufwc = bundle.principalClass as! UnityFramework.Type
        let ufw = ufwc.getInstance()!
        if ufw.appController() == nil {
            ufw.setExecuteHeader(#dsohandle.assumingMemoryBound(to: mach_header_64.self))
        }
        return ufw
    }
}

リスナーの登録とほぼ同じ流れです。

updateUnityShopItem()itemPlacedInAR() はサンプルのメソッドなので、必要なシグニチャに変えてください。

おわりに

モジュールマップの作成がややこしいですが、Unity側からSwift側の処理を呼べるようになりました。

以上 どすこい塾 Advent Calendar 2025 の18日目の記事でした。
明日は未定です。

参考リンク

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?