[unity2021]C# から直接 Swift コードを呼び出す

昔は 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 に提出する場合

一点問題があります。それについては別の記事にしましたので、必要に応じてご覧ください。


返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA