更新履歴
2022/06/12 更新
- Apple Silicon搭載Macでの動作確認結果について追記
- UnityとXcodeのバージョンを更新
- Unity 2021.3.4f1
- Xcode 13.4.1
Unity as a Library(以降、UaaL
と省略)をiOSネイティブアプリに組み込んだ上でシミュレーターで動かそうとした所、以下のような課題に直面しました。
-
Unityが実機向け or シミュレーター向けのどちらかでしかビルドできない問題
- UnityのiOSビルドは元より「実機向け」か「シミュレーター向け」かのどちらかでしかビルド出来ないという制約が存在するが、UaaLを組み込んだネイティブアプリ側でも同じ制約が発生してしまう
- → 例えば「Player Settings -> Target SDK」に
DeviceSDK
を指定してビルドしたUaaLをネイティブアプリに組み込んだ場合には、ネイティブアプリ側も実機でしかビルドできなくなる
- → 例えば「Player Settings -> Target SDK」に
- UnityのiOSビルドは元より「実機向け」か「シミュレーター向け」かのどちらかでしかビルド出来ないという制約が存在するが、UaaLを組み込んだネイティブアプリ側でも同じ制約が発生してしまう
- それ以前に「Target SDK」に
SimulatorSDK
を指定してビルドしたUaaLをネイティブアプリに組み込んだ場合には、ビルド時にエラーが発生する
今回はその後者で挙げている「ビルドエラーの回避方法」及び、前者で挙げている課題に対するアプローチとしてタイトルにもある「UaaLをXCFramework
化することで実機とシミュレーターの両方で動かせるようにする手順」について解説していければと思います。
もし間違いや冗長な箇所などあればコメントにて教えて頂けると幸いです。
詳細については各章にて記載しますが、今回提案する方法は一部理解しきれていない状態で適用しているところがあるので、導入については自己責任でお願いします。。
▼ バージョン
- Unity 2021.3.4f1
- Xcode 13.4.1
▼ 環境
- MacBook Pro (16-inch, 2019)
- 2.4 GHz 8コアIntel Core i9
- Mac Studio (2022)
- M1 Max
今回紹介する手法はApple Siliconを搭載したMacでも適用可能ですが...シミュレーターで実行する際には「XcodeをRosetta経由で実行しておく」必要があります。
※軽く検証した話をこちらで追記
▼ 想定読者
こちらはUaaLのiOSに特化した記事となるので、想定読者としては「UnityとiOSネイティブアプリ両方の開発知識を多少なりとも持っている方」を対象に書いてます。
ここらについては深くまで解説しない点もあるのでご了承下さい。
(例えばプロジェクトの作成やビルド手順と言った基礎的なところなど)
▼ 省略表記について
冒頭で書いた「Unity as a Library → UaaL
」と言う省略の他にも、記事中では以下のような省略(言い回し)で解説していきます。
- Player Settings -> Target SDKに「DeviceSDK」を指定したビルド → 実機向けのビルド
- Player Settings -> Target SDKに「SimulatorSDK」を指定したビルド → シミュレーター向けのビルド
サンプルプロジェクト
今回動かすサンプルはこちら。
ネイティブアプリ側はUIKit+SwiftUIを併用して実装しており、UaaLはSwiftUIのパーツの一部として組み込むような構成となってます。
その上でUnity上とネイティブアプリ上にある両方のスライダーからはUnity上に表示されているキューブの回転速度を双方向から設定できるようにしてます。
ネイティブプラグインの導入の有無でビルド結果が変わってエラーが出たりすると怖い1ので、今回はプラグインを実装している前提で「Unity⇔ネイティブ」の双方向でデータのやり取りを出来るところまで用意してます。
(ちなみにネイティブプラグインはSwiftオンリーで実装)
プロジェクトはGitHubにて公開しているので、詳細はこちらを御覧ください。
その上で各章ごとにブランチを切っているので順に解説していきます。
シミュレーター向けにビルドしたUaaLをネイティブアプリに組み込むとビルドエラーが発生する問題を解消する
XCFramework
化の話に入る前に先ずはこちらの章から解説します。
(これを解消しない分にはシミュレーターのビルドは通らないままなので...)
--
この章ではUnity側でシミュレーター向けにビルドしたものをUaaL公式サンプルと同じ手順でネイティブアプリに組み込み、その際にネイティブアプリ側でシミュレーター向けにビルドしようとした際に恐らくは発生するであろうビルドエラーの回避方法について解説していければと思います。
後述するXCFramework
化の話と混ざらないように、この章では敢えてUaaL公式サンプルと同じ手順をベースに解説していきます。
(その為、UaaLを組み込んだネイティブアプリ側では実機とシミュレーターの両方で実行することは不可のままなので注意)
完成品のサンプルは feature/xcworkspace ブランチを御覧ください。
(動作確認方法はREADMEを参照)
発生するビルドエラーについて
具体的な解説に入る前に、「同じエラーが発生しているのか?」の認識合わせとして現在私の手元で発生しているビルドエラーについても触れておきます。
エラーの内容としては、ネイティブアプリ本体(サンプルだとUaaLExample
と言うターゲット)のビルドフェーズにて以下のようなリンクエラーが発生しており、 エラーログを引用するとこのように出力されてます。
# Build target UaaLExample
# Project UaaLExample| Configuration Debug | Destination iPhone 13 Pro Max | SDK Simulator - iOS 15.5
Showing Recent Messages
Ld (長いので省略)
Undefined symbols for architecture x86_64:
"protocol descriptor for UnityFramework.NativeCallsProtocol", referenced from:
protocol conformance descriptor for UaaLExample.Unity : UnityFramework.NativeCallsProtocol in UaaLExample in Unity.o
"type metadata accessor for UnityFramework.FrameworkLibAPI", referenced from:
UaaLExample.Unity.application(_: __C.UIApplication, didFinishLaunchingWithOptions: [__C.UIApplicationLaunchOptionsKey : Any]?, onReadyHandler: () -> ()) -> () in Unity.o
"_OBJC_CLASS_$_UnityFramework", referenced from:
objc-class-ref in Unity.o
"static UnityFramework.FrameworkLibAPI.registerAPIforNativeCalls(UnityFramework.NativeCallsProtocol) -> ()", referenced from:
UaaLExample.Unity.application(_: __C.UIApplication, didFinishLaunchingWithOptions: [__C.UIApplicationLaunchOptionsKey : Any]?, onReadyHandler: () -> ()) -> () in Unity.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ここで表示されるエラーログの内容自体は各々の環境やプロジェクトの内容(更に言うと実装しているネイティブプラグインのクラス名など)によって変わってくるかと思われますが、もし近い内容のエラーが出ているとしたら、恐らくは次で話す回避方法を適用できる可能性があります。
逆にこれとは全く違う系統のエラーが発生している場合には別要因である可能性があるために、今回の回避方法は適用できないかもしれないのでご了承下さい。
回避方法について
先に結論から言うと、Xcode上からUnity-iPhone -> UnityFramework
の「Build Settings」を開き、「Linking -> Other Linker Flags」から以下の2つを取り除くことでビルドが通るようになります。
-Wl,-undefined,dynamic_lookup
-Wl,-exported_symbol,il2cpp*
エラーの原因について
エラーの原因についても軽く考察していきます。
「実機向けにビルドしたUaaL」と「シミュレーター向けにビルドしたUaaL」を用意して、それぞれをネイティブアプリに組み込んだ状態でビルド時のログを見比べてみた所、後者のシミュレーター向けの方ではUnityFramework
のビルドフェーズにて以下の警告が出力されているのを確認しました。
# Build target UnityFramework
# Project Unity-iPhone | Configuration Debug | Destination iPhone 13 Pro Max | SDK Simulator - iOS 15.5
Showing Recent Messages
Ld (長いので中略) -Wl,-undefined,dynamic_lookup -Wl,-exported_symbol,_il2cpp_\* (長いので中略)
ld: warning: -undefined dynamic_lookup is deprecated on iOS Simulator
ld: warning: cannot export hidden symbol _il2cpp_no_exceptions from ~/Library/Developer/Xcode/DerivedData/(プロジェクトによって変わるので省略)/Build/Intermediates.noindex/Unity-iPhone.build/Debug-iphonesimulator/UnityFramework.build/Objects-normal/x86_64/Il2CppOptions.o
ld: warning: cannot export hidden symbol _il2cpp_runtime_stats from ~/(プロジェクトのあるディレクトリ)/UaaL-Examples-iOS/UnityProject/Builds/UnityFramework/Libraries/libil2cpp.a(External_il2cpp_builds_libil2cpp_0_8tnvs.o)
ld: warning: cannot export hidden symbol _il2cpp_defaults from ~/(プロジェクトのあるディレクトリ)/UaaL-Examples-iOS/UnityProject/Builds/UnityFramework/Libraries/libil2cpp.a(External_il2cpp_builds_libil2cpp_vm_5_8tnvs.o)
上記の内容からldコマンドを叩いている所(リンク実行時)で怒られているのが見えてきたので、更に両者のリンク時の設定(Xcode上の「Build Settings -> Linking」)を見比べてみた所、シミュレーター向けにビルドした場合には「Other Linker Flags」に先程挙げた以下2つのパラメータが渡されているのを確認しました。
(逆に実機向けにビルドした場合には以下2つのパラメータは渡されていない)
-Wl,-undefined,dynamic_lookup
-Wl,-exported_symbol,il2cpp*
正直な所、私自身がldコマンド周りについてあまり詳しくないので、これらがどの様に作用しているのかまでについては説明できるレベルまで理解しきれていませんが...今回の警告内容を見るに恐らくはこれらが影響しているだろうと仮定して、試しに両者をパラメータから削除してみた所、ビルドが通るようになったという経緯があります。
記載の通り詳細まで理解しきれていない状態で導入している回避方法にはなりますが...シミュレーター向けの時のみ発生しているエラーと言うのもあるので、私個人としては「一旦は適用しつつも様子見で良いかな...」という感じで取り入れてます。。
→ 今後、仮に上記の設定変更が影響して別要因のエラーが発生するなどと言った現象を確認した場合には随時追記していきます。
その上でもし今回のエラー原因について詳しい方が居ましたら、コメントなりで教えて頂けると幸いです。。
[PostProcessBuild]
を用いて設定変更を自動化する
今回の回避方法の適用先はUnityがビルド時に出力するXcodeプロジェクトの範囲内となるために、PostProcessBuildAttributeを用いたEditor拡張を実装することで設定変更を自動化することが可能です。
(所謂、こちらの記事の内容)
以下に今回サンプルプロジェクトに導入しているコードを載せます。
static class XcodePostProcess
{
// 参考: https://tech.mirrativ.stream/entry/2020/10/20/100000
[PostProcessBuild]
static void OnPostProcessBuild(BuildTarget target, string path)
{
if (target != BuildTarget.iOS) return;
var projectPath = PBXProject.GetPBXProjectPath(path);
var project = new PBXProject();
project.ReadFromString(File.ReadAllText(projectPath));
var targetGuid = project.GetUnityFrameworkTargetGuid();
// `Data`フォルダを[Build Phase -> Copy Bundle Resources]に追加
var dataPathGuid = project.FindFileGuidByProjectPath("Data");
var resPhaseGuid = project.GetResourcesBuildPhaseByTarget(targetGuid);
project.AddFileToBuildSection(targetGuid, resPhaseGuid, dataPathGuid);
// Swift version
project.SetBuildProperty(targetGuid, "SWIFT_VERSION", "5.0");
// NOTE: iOS Simulatorのエラー対策
// → この章で解説している内容
if (PlayerSettings.iOS.sdkVersion == iOSSdkVersion.SimulatorSDK)
{
var removeLdFlags = new[]
{
"-Wl,-undefined,dynamic_lookup",
"-Wl,-exported_symbol,_il2cpp_*"
};
project.UpdateBuildProperty(targetGuid, "OTHER_LDFLAGS", new string[] { }, removeLdFlags);
}
project.WriteToFile(projectPath);
}
}
上記のXcodePostProcess
ではこの章で解説した回避方法の適用の他にも、以下の設定も自動で適用するようにしてます。
- Dataフォルダを
UnityFramework
に加える- → UaaL公式サンプルの手順で言う、「6. Make Data folder to be part of the UnityFramework」のステップに該当
- Swiftバージョンの指定 2
他にも自動で適用したい設定がある場合には、こちらで変更を加えることが可能です。(e.g. bitcodeの生成など)
参考: 【Unity】MirrativのEmbedding Unityを更新した話: 実践 Unity as a Library
UaaLをXCFramework
化することで実機とシミュレーターの両方で動かせるようにする
シミュレーターのビルドエラーの回避方法が分かったので、ここからはタイトルにもある「UaaLをXCFramework
化することで実機とシミュレーターの両方で動かせるようにする」手順について解説していきます。
ちなみに前の章で解説したエラーは回避方法を適用しないと、この章で解説するXCFramework
化を行っても発生するので注意。
完成品のサンプルは feature/xcframework ブランチを御覧ください。
(動作確認方法はREADMEを参照)
その前にXCFramework
とは何か?
XCFramework
とはXcode11で対応されたバイナリフレームワークの形式であり、例えばこちらを用いることで「実機向け(arm64)」「シミュレーター向け(Intel Macだとx86_64)」と言ったアーキテクチャごとに異なる実態を持つフレームワークを1つに纏めることができ、実際にそれを組み込むアプリ側からは実機やシミュレーター、各種プラットフォームなどを意識せずに「1つのフレームワーク」として透過的に取り扱うことが出来るようになります。
(拡張子は.xcframework
)
ここらについてはWWDCの講演及び以下の記事が参考になります。
- Binary Frameworks in Swift - WWDC19
- OpenCV を XCFramework にして Swift Package Manager 経由で iOS で使ってみた
- xcframeworkを作成する(第1回)
【補足】 そもそもフレームワークとは何か?
一応こちらについても補足しておくと、フレームワーク(.framework
)とはAppleプラットフォーム上で利用可能なライブラリの形式の一つであり、公式ドキュメントから一部和訳して引用すると以下のように記載されてます。
動的共有ライブラリ、nibファイル、イメージファイル、ローカライズ文字列、ヘッダーファイル、リファレンスドキュメントなどの共有リソースを単一のパッケージにカプセル化した階層的なディレクトリです。
詳細については公式ドキュメント及び以下の記事が参考になります。
ちなみにUaaLも実態としてはフレームワークであり、iOSビルドの出力結果(Unity-iPhone.xcodeproj
)にある UnityFramework.framework
と言うターゲットがそれに該当します。 3
実機向けとシミュレーター向けのUnityFramework.framework
を1つに纏めることが可能
話を戻すと、今回はそのXCFramework
を利用することで 「実機向けにビルドしたUaaL(UnityFramework.framework
)」と「シミュレーター向けにビルドしたUaaL」を「1つのXCFramework
」として纏めることが可能となります。
その上でネイティブアプリ側での方でも統合されたXCFramework
を組み込むようにすれば、両方のアーキテクチャのバイナリが含まれているので実機/シミュレーターを意識せずとも実行できるようになります。
ちなみに、この手順については裏を返すとUinty側は実機とシミュレーターの両方をビルドする必要があったり、他にも次で解説するXCFramework
をビルドするまでに様々なステップが入ることになるので、ビルド時間に関しては必然的に伸びることになります。
なので、これから解説する手順を実際に導入するにあたっては、それぞれのマシンスペックやワークフローなどを踏まえた上で検討していくのが良いかと思います。
XCFramework
のビルド手順
大まかな手順としては以下のようになります。
- UnityのiOSビルドを実行して実機 & シミュレーター向けの2つの
Unity-iPhone.xcodeproj
を生成 - ビルドした
.xcodeproj
から.xcarchive
をビルド -
.xcarchive
から.xcframework
をビルド -
[BUILD_LIBRARY_FOR_DISTRIBUTION]
を有効にしている都合で、このままだとUnityFramework.xcframework
を組み込んだ際にエラーが発生するので対策
サンプルプロジェクトの方ではこれらの手順全てを以下のシェルスクリプトで実装してコマンドライン上から実行できるようにしているので、こちらから引用しつつ解説していきます。
(シェルスクリプト全体はこちら↓)
※余談: macOSだと拡張子を.command
にすることでシェルスクリプトをダブルクリックで実行できるようになります。
▼ 1. UnityのiOSビルドを実行して実機 & シミュレーター向けの2つのUnity-iPhone.xcodeproj
を生成
こちらは単純にUnity上でEditor拡張として実装しているビルドメソッドをコマンドライン上から呼び出しているだけです。
ここで呼び出されているコードは以下を御覧ください。
# UnityEditorのパス
UNITY_EDITOR_PATH="/Applications/Unity/Hub/Editor/2021.3.4f1/Unity.app/Contents/MacOS/Unity"
# ビルド結果の出力先
OUTPUT_PATH="${CURRENT_DIR}/Builds"
# 1. UnityのiOSビルドを実行して実機&シミュレーター向けの2つの`Unity-iPhone.xcodeproj`を生成
# https://docs.unity3d.com/Manual/EditorCommandLineArguments.html
${UNITY_EDITOR_PATH} -batchmode \
-nographics \
-silent-crashes \
-quit \
-buildTarget iOS \
-projectPath ${CURRENT_DIR} \
-executeMethod UaaLExample.Editor.BuildMenu.BuildIOSForAllSDK
osascript -e 'display notification "Success Build UnityProject" sound name "Bip"'
# ビルドに成功して両方の`Unity-iPhone.xcodeproj`が生成されているかチェック
DEVICE_BUILD_PATH="${OUTPUT_PATH}/DeviceSDK/Unity-iPhone.xcodeproj"
SIMULATOR_BUILD_PATH="${OUTPUT_PATH}/SimulatorSDK/Unity-iPhone.xcodeproj"
if [ ! -d $DEVICE_BUILD_PATH ]; then
echo "iOS実機向けのビルドが存在しない"
return 1
fi
if [ ! -d $SIMULATOR_BUILD_PATH ]; then
echo "iOSシミュレーター向けのビルドが存在しない"
return 1
fi
完了したら./UnityProject/Builds
以下に実機向けのビルドとシミュレーター向けのビルドの2つが生成されるので、問題なければ次の手順に進みます。
▼ 2. ビルドした.xcodeproj
から.xcarchive
をビルド
前の手順でビルドした成果物(実機&シミュレーター向けのUnity-iPhone.xcodeproj
)からxcarchive
をビルドします。
(実際に.xcodeproj
に対して本格的なビルドが走るタイミングとなるので、ビルド時間の方は長くなる点に注意)
ここでビルドしたxcarchive
にはUnityFramework.framework
が含まれるので、それを次の手順で利用します。
# ビルド結果の出力先
OUTPUT_PATH="${CURRENT_DIR}/Builds"
# 2. ビルドした`.xcodeproj`から`.xcarchive`をビルド
DEVICE_BUILD_ARCHIVE_PATH="${OUTPUT_PATH}/UnityFramework-Device.xcarchive"
SIMULATOR_BUILD_ARCHIVE_PATH="${OUTPUT_PATH}/UnityFramework-Simulator.xcarchive"
# 前回のビルド結果が残っている場合には先に削除
rm -rfv ${DEVICE_BUILD_ARCHIVE_PATH}
rm -rfv ${SIMULATOR_BUILD_ARCHIVE_PATH}
# 実機向けの`.xcarchive`をビルド
xcodebuild archive \
-project "${DEVICE_BUILD_PATH}" \
-scheme "UnityFramework" \
-destination="iOS" \
-archivePath "${DEVICE_BUILD_ARCHIVE_PATH}" \
-sdk iphoneos \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
osascript -e 'display notification "Success Build UnityFramework-Device.xcarchive" sound name "Bip"'
# シミュレーター向けの`.xcarchive`をビルド
xcodebuild archive \
-project "${SIMULATOR_BUILD_PATH}" \
-scheme "UnityFramework" \
-destination="iOS Simulator" \
-archivePath "${SIMULATOR_BUILD_ARCHIVE_PATH}" \
-sdk iphonesimulator \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
osascript -e 'display notification "Success Build UnityFramework-Simulator.xcarchive" sound name "Bip"'
▼ 3. .xcarchive
から.xcframework
をビルド
前の手順でビルドした.xcarchive
にはUnityFramework.framework
が含まれているので、それらを以下のコマンドからUnityFramework.xcframework
にビルドします。
(この手順はそんなにビルド時間は掛からないはず)
# 3. `.xcarchive`から`.xcframework`をビルド
XCFRAMEWORK_PATH="${OUTPUT_PATH}/UnityFramework.xcframework"
# 前回のビルド結果が残っている場合には先に削除
rm -rfv ${XCFRAMEWORK_PATH}
# 2の手順でビルドした実機&シミュレーター向けの`.xcarchive`から`.xcframework`をビルド
xcodebuild -create-xcframework \
-framework "${DEVICE_BUILD_ARCHIVE_PATH}/Products/Library/Frameworks/UnityFramework.framework" \
-framework "${SIMULATOR_BUILD_ARCHIVE_PATH}/Products/Library/Frameworks/UnityFramework.framework" \
-output ${XCFRAMEWORK_PATH}
osascript -e 'display notification "Success Build UnityFramework.xcframework" sound name "Bip"'
▼ 4. [BUILD_LIBRARY_FOR_DISTRIBUTION]
を有効にしている都合で、このままだとUnityFramework.xcframework
を組み込んだ際にエラーが発生するので対策
前の手順でUnityFramework.xcframework
が作られるので終わりかと思いきや...現状のままだとこれを組み込んでもビルドエラーが発生します。
原因について調べてみた所、以下のフォーラムが参考になりました。
どうやら「2の手順」で.xcarchive
をビルドする際に[BUILD_LIBRARY_FOR_DISTRIBUTION]
と言うオプションを有効にしているのが影響してフォーラムで挙げられているようなエラーが出ているみたいでした。
なので今回はワークアラウンドとして上記のフォーラムで提案されている解決策を採用し、以下のように「.xcframework
に含まれる*.swiftinterface
を全検索しつつ、sedコマンドでUnityFramework.
から始まる文字列を空文字に置換する」処理を挟むようにしてます。
# 4. [BUILD_LIBRARY_FOR_DISTRIBUTION]を有効にしている都合で、このままだと`UnityFramework.xcframework`を組み込んだ際にエラーが発生するので対策
# 参考: https://developer.apple.com/forums/thread/123253
cd ${XCFRAMEWORK_PATH}
# エラー対策として、`.xcframework`に含まれる`*.swiftinterface`を全検索しつつ、sedコマンドで`UnityFramework.`を空文字に置換する
find . -name "*.swiftinterface" -exec sed -i -e 's/UnityFramework\.//g' {} \;
# `.swiftinterface-e` と言うファイルが出来るので削除
find . -name "*.swiftinterface-e" -exec rm -f {} \;
osascript -e 'display notification "Fix \".swiftinterface\" in xcframework" sound name "Bip"'
return 0
このフローについてはネイティブプラグインの実装言語や内容によって変わってくるかもしれない点に注意。
(少なくとも今回のサンプルプロジェクトに含まれているプラグインだと必要な模様)
こちらについても若干知識に自信が無い所となるので...もし間違っていたりしたらコメントなりで教えて頂けると幸いです。。
【補足】 [BUILD_LIBRARY_FOR_DISTRIBUTION]
について
こちらのオプションを付けている意図についても軽く補足しておくと、こちらはSwiftの 「Module Stability」を有効にするために設定してます。
このModule Stabilityと言うのは「異なるバージョンのコンパイラから生成されたモジュール(フレームワーク)をインポート出来るようにする為の機能」であり、有効にすると内部的に*.swiftinterface
と言うファイルが生成されるようになります。
(その上でXCFramework
化を行う上では必要っぽい)
- 参考
おわりに
まだ理解できていないところや知識不足な点は残りつつも、一先ずは冒頭で挙げた課題を解決することが出来ました。
特にUnityに於いては「Unity単体で使う分には」シミュレーター向けにビルドすことはあまり無い?印象4がありますが、UaaLとしてネイティブアプリに組み込む上ではシミュレーターで動かすことも多くなるかと思われたので、物によっては役立てられるのかなと言う所感はあります。
→ 例えばUaaLをネイティブUIのパーツとして組み込んだ状態で、各機種/各OSバージョンごとのシミュレーターでレイアウトを確認したいときとか5
ただ、途中でも警告した通りに XCFramework
化の手順はビルド時間が必然的に伸びてしまうので、ここらについてはワークフローなどを踏まえた上で考えていく必要があるかもです。
Apple Silicon対応について
更新履歴にも記載している通り、Apple Siliconの件については途中から追記する形となりましたが、動かしてみたら案の定とでも言うべきか...Unityがシミュレーター向けにビルドした際に吐き出すバイナリがx86_64
で固定されている影響か、「ビルドするにはRosettaを経由する必要がある」ことが分かりました...。
(大体想像は付いていた...)
軽く粘ってみたが...難しそう..?
まぁ動くといえば動くので及第点には達したかな...と言う所感はありつつも、それでもパフォーマンスが気になるので、何とかならないかと軽く調査をしてみましたが...結論から言えば現時点での調査範囲では難しそうでした..。
参考程度に今回調査した内容を残しておきます。
(もしこれ以外にも進展があれば随時追記/更新していきます)
▼ シミュレーター向けにビルドした方のXcodeProjectの設定を見直してみる
UnityがiOSビルドで出力するXcodeProjectは恐らくはシミュレーター向けに設定されているであろうから、これを実機向けに変えることによってビルドが通らないか?と言うアプローチです。
とりあえずはシミュレーター向けにビルドしたUnity-iPhone.xcodeproj
の「Build Settings」を見直しつつ、以下のように設定を変更してみました。
-
Architectures
-
Architectures
: x86_64 → arm64 -
Base SDK
: iphonesimulator (SDK not found) → iOS -
Supported Platforms
: iphonesimulator → iOS
-
この状態でUaaLとして組み込んだネイティブアプリ側の方でビルドしてみたところ、変更前まで出ていた「Precompile bridging header
のコンパイルエラー」は解消されたものの...今度はUnityFrameworkのビルドプロセスにて、以下のリンクエラーが出るようになりました。
Ignoring file (プロジェクトのパス)/UnityProject/Builds/UnityFramework/Libraries/libiPhone-lib.dylib, building for iOS Simulator-arm64 but attempting to link with file built for iOS Simulator-x86_64
Ignoring file (プロジェクトのパス)/UnityProject/Builds/UnityFramework/Libraries/libil2cpp.a, building for iOS Simulator-arm64 but attempting to link with file built for iOS Simulator-x86_64
Ignoring file (プロジェクトのパス)/UnityProject/Builds/UnityFramework/Libraries/baselib.a, building for iOS Simulator-arm64 but attempting to link with file built for iOS Simulator-x86_64
Undefined symbol: _RegisterModule_SharedInternals
Undefined symbol: (以下省略..)
どうやらUnityがビルド時に出力するライブラリ(上記で言うlibiPhone-lib.dylib
, libil2cpp.a
, baselib.a
)が「Simulator-x86_64
」向けにビルドされている影響でリンクできずにエラーになっている模様...。
▼ [余談] Apple Siliconがarm64
ならiOS Simulatorもarm64
で実行される → なら実機向けのライブラリ(arm64)を使えば行ける説?
結論から言うと、これも行けません。
確かにApple Siliconの場合にはiOS Simulatorもarm64
で実行されるようになると言う認識ですが..何故か同じarm64
でも「実機向けとシミュレーター向けで環境が別れている」のでビルドを通すことができません。
仮に無理やり上述のUnityが出すライブラリ(libiPhone-lib.dylib
, libil2cpp.a
, baselib.a
)を実機向けビルドで出力したものに差し替えたとしても、以下のようなリンクエラーが出てしまいます。6
Ld (長いので省略)
ld: in (プロジェクトのパス)/UnityProject/Builds/UnityFramework/Libraries/libil2cpp.a(CultureInfo_jpug7.o), building for iOS Simulator, but linking in object file built for iOS, for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
それにしてもlibiPhone-lib
...シミュレーター向けだと「Dynamic Library(.dylib)」で出て、実機向けだと「Static Library(.a)」で出るんだ...まぁ実機向けもdylibだと審査通らないと思うのでそれはそう。。
続き(未定)
今回サンプルプロジェクトを作っていく中で幾つか話足りない点が出てきたので、元気があれば別途記事にて解説するかも...。
(それよりもMac Studioのセットアップ + この記事の補足のほうが先..)
具体的に言うと以下のような点があります。
- iOSネイティブアプリ側でUaaL + SwiftUIを導入する際のtips
-
UaaL(
UnityFramework
)のsendMessageToGOWithNameを使わずに、ネイティブからUnityのメソッドを呼ぶ方法について- 今回のサンプルだとSwiftUIで実装しているスライダーの値をUnityに渡すところでこれを行っている
- 文字列以外(但しBlittable型)が渡せる上にポインタ芸が可能
- → 例えば大きいデータのポインタを渡してmemcpyする方法など
-
SwiftUIのCanvas Previewに関する注意点
UnityFramework.xcframework
を導入した状態でSwiftUIのCanvas Preview
を表示しようとするとコンパイルエラーが発生するので、回避するためのワークアラウンドが必要な話- → こちらのソースにてコメントで補足してる内容
-
例えば「プラグインを実装する前ならエラーは出なかったけど、プラグインを実装したらエラーが出るようになった」的な現象も無きにしもあらずなので... ↩
-
最近のUnityだと(?)最初から「Swift 5.0」が指定されているっぽいので実際には無くても良いかもしれないが...一応明示的に指定するようにはしている。 ↩
-
UaaL公式サンプルにある手順はその
UnityFramework.framework
を「UaaLとして公開するための設定(NativeCallProxy.h
の公開やData
を含めるための設定)」及び「それをiOSネイティブアプリのプロジェクトに組み込むステップ」の一例について解説している ↩ -
自分の経験上だとほぼ使うことが無かった(実機オンリー) ↩
-
この性質からカメラと言った実機依存の機能を使う場合には利用が難しい説はある... ↩
-
ファイルを差し替えた上で「Build Phase」の設定を差し替えてやれば可能 ↩