はじめに
iOS の BLE 接続プラグインを作成して下記のようなものを作ってみました。
iPad1台、iPhone2台を使うアプリができた🙌
この方向で格ゲー作れないかな🙄 pic.twitter.com/0mKCNLFGv5— am10 (@am103141592) June 13, 2021
Unity から iOS のメソッド呼び出しはサクッとできたのですが iOS 側からのコールバックに少しつまづいたのでそのあたりについて書きます。
プラグインの構成
せっかくなので Swift で書きたいので下記の構成になります。
プラグインに必要なのは下記2つです。
- .mm ファイル(C#から呼び出す用)
- .swift ファイル(プラグインの実装ファイル)
ボタンを押して Swift の print
を実行して 10 を返すメソッドを呼び出す場合、下記のような実装になります。
Swift ファイル。
1 2 3 4 5 6 7 8 9 10 |
import Foundation public class Hoge: NSObject { // @objcが必要 @objc public static func printHello() -> Int32 { print("Hello") return 10 } } |
Objective-C++ ファイル。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#import <Foundation/Foundation.h> #import <UnityFramework/UnityFramework-Swift.h> // Swift使うのに必要? #ifdef __cplusplus extern "C" { #endif int printHello() { return [Hoge printHello]; } #ifdef __cplusplus } #endif |
C# ファイル。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.UI; class Hoge : MonoBehaviour { [SerializeField] Button _button; void Start() { _button.onClick.AddListener(() => { int hoge = PrintHell(); Debug.Log(hoge); }); } // extern "C"内で宣言した関数をここで呼び出す // EntryPointを設定することによって.mmファイルの実装と別名をつけれる [DllImport("__Internal", EntryPoint = "printHello")] static extern int PrintHello(); } |
少し気をつけないといけないのが C# に int
を返したい場合、Swift 側では Int
ではなく Int32
を使う必要があります。Int
を使うと思わぬ動作をしたり、クラッシュしたりするので気をつけましょう。
対応する型は下記参考。
Objective-CからSwiftへの書き換え作業、型、ポインタ、配列Array、辞書Dictionary
コールバック
いよいよコールバックを実装します。
Swift ファイル。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import Foundation public typealias HogeCallback = @convention(c) (Int32, UnsafePointer<Int8>) -> Void public class Hoge: NSObject { // コールバック関数を保持するためにシングルトンにしました public static let shared = Hoge() private override init() { super.init() } private var callback: HogeCallback? @objc static public func register(callback: @escaping HogeCallback) { Hoge.shared.callback = callback } @objc public static func printHello() { print("Hello") Hoge.shared.callback?(10, NSString("Hoge").utf8String!) } } |
Objective-C++ ファイル。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#import <Foundation/Foundation.h> #import <UnityFramework/UnityFramework-Swift.h> typedef void (* hogeCallback)(int, const signed char*); #ifdef __cplusplus extern "C" { #endif void registerCallback(hogeCallback callback) { [Hoge registerWithCallback:callback]; } void printHello() { return [Hoge printHello]; } #ifdef __cplusplus } #endif |
C# ファイル。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.UI; class Hoge : MonoBehaviour { [SerializeField] Button _buttonHelloWorld; void Start() { // コールバックの登録 RegisterCallback(HogeCallback); _button.onClick.AddListener(() => { PrintHell(); }); } // .mmで定義したコールバックと同じ型のデリゲートを定義 delegate void HogeCallbackDelegate(int num, string text); // iOSから呼び出されるやつ [AOT.MonoPInvokeCallbackAttribute(typeof(HogeCallbackDelegate))] static void HogeCallback(int num, string text) { Debug.Log($"num: {num} text: {text}"); } [DllImport("__Internal", EntryPoint = "registerCallback")] static extern void RegisterCallback(HogeCallbackDelegate hogeCallback); [DllImport("__Internal", EntryPoint = "printHello")] static extern void PrintHello(); } |
これでボタンを押したときに static void HogeCallback(int num, string text)
が呼び出されます。
string
を扱いたい場合に const signed char*
でいいのかはちょっとわかりませんがとりあえず動きました。
これを応用してコールバックをいっぱい登録すれば「はじめに」にあるような BLE 接続ができます。
おわりに
無事 Swift からコールバックできました!
今回のやつだとメモリリークする気がするのでちゃんとやるならいいタイミングで登録したコールバック関数を開放してやるために clear
メソッド(ここで nil
つっこむ)とか作って呼び出す必要があるかと思います。
コメント