1
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?

More than 1 year has passed since last update.

CoreBluetoothForUnityAdvent Calendar 2023

Day 19

【Unity】Swift のネイティブプラグインからのコールバックで文字列を渡す

Last updated at Posted at 2023-12-18

本記事について

この記事は CoreBluetoothForUnity Advent Calendar 2023 の19日目の記事です。

ネイティブプラグインから C# のコールバックを呼ぶ際に文字列を渡す方法のうち、文字列そのもののポインタを渡す方法について説明します。

やること

C# から文字列を NSString のポインタとして受け取る場合はざっくり以下の流れになります。

callback1.png

C# 側からネイティブプラグイン側に破棄を指示しています。

この流れでは C# 側から処理が始まっていますが、コールバックの場合はネイティブプラグイン側から呼ばれることになるので、以下のようになります。

callback4.png

この場合コールバックの処理は同期のため、コールバックの処理終了後にネイティブプラグイン側に処理を入れることが可能です。

であるならば、C# 側で破棄を指示せず、ネイティブプラグイン側で文字列を破棄しても問題ないはずです。

callback5.png

さらに、破棄をネイティブプラグイン側で行うのであれば NSString のポインタじゃなくて最初から文字列渡してしまえばいいのでは?というのが以下です。

callback3.png

とてもシンプルになりました。

長くなりましたが、本記事ではこれをどうやって書くのかを説明します。

環境

  • CoreBluetoothForUnity 0.4.6

コード

例として示す delegate は以下です。

.cs
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void CB4UCentralManagerDidConnectHandler(IntPtr centralPtr, IntPtr peripheralIdPtr);
.swift
public typealias CB4UCentralManagerDidConnectHandler = @convention(c) (UnsafeRawPointer, UnsafePointer<CChar>) -> Void

peripheralIdPtr が文字列のポインタです。
ペリフェラルは CoreBluetooth の用語であり、本記事では気にする必要はありません。

Swfit

リンク

CB4UCentralManager.swift
public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    let peripheralId = peripheral.identifier.uuidString
    peripheralId.withCString { (uuidCString) in
        didConnectHandler?(selfPointer(), uuidCString)
    }
}

peripheralId は String です。
withCString を使って null 終端の C 文字列を取得し、コールバックの引数として渡しています。

この関数を抜けると Swift によって文字列は勝手に破棄されます。

C#

リンク

NativeCentralManagerCallbacks.cs
[AOT.MonoPInvokeCallback(typeof(NativeMethods.CB4UCentralManagerDidConnectHandler))]
static void DidConnect(IntPtr centralPtr, IntPtr peripheralIdPtr)
{
    GetDelegate(centralPtr)?.DidConnect(Marshal.PtrToStringUTF8(peripheralIdPtr));
}

Marshal.PtrToStringUTF8 を使って文字列を取得するのが楽です。

Marshal.PtrToStringUTF8 内で文字列はマネージドメモリにコピーされるため、元の文字列が破棄されても問題ありません。

おわりに

本記事では Swift のネイティブプラグインにおいてコールバックで文字列を渡す方法としてはおそらくこれが一番簡単だろうと思うものを紹介しました。
withCString にたどり着くために少し時間かかったため、Swift のネイティブプラグインを書く際に参考になれば幸いです!

1
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
1
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?