昔は C# > mm > swift と迂回しないと呼び出せなかった Swift コードですが、いつのまにか直接呼び出せるようになっていたので共有します。多分 2020 あたりから……?
ネイティブコード呼び出し未経験の人でもわかりやすくするため、これ以上短くするのが無理なくらい最低限のコードにしました。
更に上を目指すのであればこちらの記事を参考にしましょう!
タメになりました
Swift から返ってきた値を表示
OnPressButton に、適当に作成した UI Button を割り当ててください。
C# コード (CallSwiftPlugin.cs)
using UnityEngine; using System.Runtime.InteropServices; public class CallSwiftPlugin : MonoBehaviour { [DllImport("__Internal", EntryPoint = "callSwift")] static extern long CallSwift(); public void OnPressButton() { long value = CallSwift(); Debug.Log($"Hello world. {value}"); } }
swift コード (SwiftPlugin.swift)
import Foundation public class SwiftPlugin { public static func callSwift() -> Int32 { print("called swift") return 123 } } @_cdecl("callSwift") public func callSwift() -> Int32 { return SwiftPlugin.callSwift() }
Assets/Plugin/iOS に置こう
実行
Xcode で実行すると、Hello world. 123 というログが表示されます。
123 は swift から取得した値です。
iOS ネイティブなので、Unity Editor では実行できません。
アプリに使う場合は #if UNITY_IOS ~ #endif でコードを囲みましょう。
Swift からのコールバック
RegisterCallback で登録したメソッドが、ネイティブからコールされるサンプルです。
iOS でなにかの処理終了を待って、unity に実行を移す……という場合に使えます。
(サンプルはボタン起動なので、SwiftPlugin とあまり結果は変わりませんが)
C# コード (CallSwiftCallback.cs)
using UnityEngine; using System.Runtime.InteropServices; using System; public class CallSwiftCallback : MonoBehaviour { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate void SwiftCallbackDelegate([MarshalAs(UnmanagedType.I4)] Int32 num); [DllImport("__Internal", EntryPoint = "registerSwiftCallback")] static extern void RegisterCallback([MarshalAs(UnmanagedType.FunctionPtr)] SwiftCallbackDelegate callback); [DllImport("__Internal", EntryPoint = "callSwiftCallback")] static extern void CallCallback(); public void OnPressButton() { // register 'SampleCallback' to swift RegisterCallback(SampleCallback); // callSwiftCallback -> SwiftPlugin.callCallback -> SampleCallback CallCallback(); } [AOT.MonoPInvokeCallbackAttribute(typeof(SwiftCallbackDelegate))] static void SampleCallback(Int32 num) { Debug.Log($"call native code: {num}"); } }
swift コード (SwiftCallback.swift)
import Foundation public typealias SwiftCallbackDelegate = @convention(c) (Int32) -> Void public class SwiftCallback { static private var callbackDelegate: SwiftCallbackDelegate? = nil; public static func registerCallback(_ delegate: @escaping SwiftCallbackDelegate) { callbackDelegate = delegate } public static func callCallback() { callbackDelegate?(123) } } @_cdecl("registerSwiftCallback") public func registerSwiftCallback(_ delegate: @escaping SwiftCallbackDelegate) { return SwiftCallback.registerCallback(delegate) } @_cdecl("callSwiftCallback") public func callSwiftCallback() -> Int32 { return SwiftCallback.callCallback() }
実行
call native code: 123 と表示されれば成功です。
とても軽めの解説
unity(C#) は swift コードの @_cdecl で宣言されたメソッドを呼び出すことができます。
[DllImport("__Internal", EntryPoint = "callSwift")] static extern long CallSwift(); ↓ @_cdecl("callSwift") public func callSwift() -> Int32 { return SwiftPlugin.callSwift() }
単純に言うとこれだけです。
今回の例はシングルトンのため分かりやすく簡単です。インスタンスを作成する場合はそこそこ複雑で、メモリ管理を意識したプログラミングとなります。
メモリ管理なんて GC が勝手にやるんでしょ? という方にはあまりオススメできませんが、どうしても必要であれば最初に紹介した記事で詳しく解説されているので、参考にしてください。
swift を呼び出せると、iOS についてはかなり自由に色々な事が出来ると思うので、是非チャレンジしてみましょう!
Android は知らン
AppStore に提出する場合
一点問題があります。それについては別の記事にしましたので、必要に応じてご覧ください。