Unity as a Library×Swiftの記事はシリーズになっています。
記事を順番に読み進めると、Unity as a LibraryをSwiftで使えるようになります。
- Unity as a LibraryでXCFrameworkを生成する方法(Swift)
- Unity as a LibraryのXCFrameworkを生成するGitHub Actions
- UnityFrameworkが提供している各クラスの主要メソッド一覧(Objective-C)
- UnityFrameworkをSwiftで使う方法
- Unity as a LibraryでUnityからSwiftの処理を呼ぶ方法 ←イマココ
はじめに
本記事は どすこい塾 Advent Calendar 2025 の18日目の記事です。
昨日も @uhooi で UnityFrameworkをSwiftで使う方法 でした。
NaitveCallProxy を使い、Unity as a LibraryでUnity側からSwift側の処理を呼ぶ方法を紹介します。
環境
- OS:macOS Sequoia 15.6(24G84)
- Swift:6.2.1
NativeCallProxyの導入
まず NativeCallProxy を導入します。
モジュールマップの作成
NativeCallProxy 自体はすでに存在するのですが、私の環境ではそのままだと呼び出せませんでした。
そのためモジュールマップを作成します。
module NativeCallProxy {
header "./../unity-framework/UnityFramework.xcframework/ios-arm64/UnityFramework.framework/Headers/NativeCallProxy.h"
export *
}
NativeCallProxyの公開と追加
UnityFrameworkをSwiftで使う方法 で作成した Pacakge.swift にて NativeCallProxy のライブラリを公開します。
// 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 へ追加します。
// 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プロトコルに従ってデリゲートメソッドを実装する
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日目の記事でした。
明日は未定です。