e.blog

主にUnity/UE周りのことについてまとめていきます

Swiftでネイティブプラグインを作る

概要

以前、Objective-Cを用いたプラグイン生成については記事を書きました。

edom18.hateblo.jp

今回はSwiftを用いたプラグイン作成方法です。
Unity側で準備する内容は基本的には大きく違いませんが、Xcode側でやることあったり、SwiftとObjective-Cとの連携周りについての調整が多く、そのあたりを中心にまとめておきたいと思います。

ちなみに、大本はこちらの記事を参考にさせていただきました。

qiita.com

SwiftからObjCを呼び出すためのBridge用ファイルを生成・設定する

通常、Xcodeプロジェクトから生成したものであればブリッジが必要になったタイミングでXcodeが聞いて自動的に生成してくれるようですが、Unityからだとそれが行われないので自分で追加、設定する必要があります。

ファイル名は任意で、必要な項目にそのファイルを設定するだけです。

設定箇所は「Swift Compiler - General > Objective-C Bridging Header」です。

$(SRCROOT)とするとプロジェクトフォルダまでのパスを設定してくれるので、あとは作成したファイル名を続けて入力すると無事に設定されます。

$(SRCROOT)/Bridging-Header.h

f:id:edo_m18:20190606170511p:plain

ヘッダファイルの中身は、Swiftから呼ばれる可能性のあるものをインポートしておきます。

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "UnityInterface.h"

Objective-CからSwiftのメソッドを呼び出す

Swiftのバージョンなどによって挙動が違うようですが、Swift4から(?)明示的に書かないとObjCに対して公開されないようになったようです。
そのため、ObjCに対して公開する場合は@objcをつけて宣言する必要があるようです。

class ExampleClass : NSObject {
    @objc static func swiftMethod(_ message: String) {
        print("\(#function) is called with message: \(message)")
    }
}

ObjCから呼び出している箇所はこんな感じ↓

extern "C" {
    void _ex_callSwiftMethod(const char* message) {
        [ExampleClass swiftMethod:[NSString stringWithUTF8String:message]];
    }
}

ちなみに、この@objcが必要な理由はこちらの記事で解説されているので、興味がある人は読んでみるといいかもです。

qiita.com

Objective-CからSwiftのクラスをイニシャライザを使って生成する

例えば以下のようなSwiftクラスを定義したとします。

public class SwiftClass : NSObject {
    var name: String?
    var age: Int?
    @objc public init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

これを、Objective-Cで利用するには以下のようにします。

SwiftClass *aClass = [[SwiftClass alloc] initWithName:@"hoge" age:@20]

Tips

< Product_Module_Name>-Swift.hCommand + Left Clickすると定義に飛べるので、自動生成したヘッダファイルを見て、Swiftのメソッドがどうエクスポートされているかを確認するとエラー解消が早いかもしれません。
Product_Module_NameはBuild Settingsで確認することができます)

ちなみに、以下のようなクラス・メソッドの場合のエクスポート例です。

Swiftの定義は以下。

import Foundation

public class Callback : NSObject {
    var objectName: String?
    var methodName: String?
    
    @objc public init(objectName: String, methodName: String) {
        self.objectName = objectName
        self.methodName = methodName
    }
    
    @objc public func Picked(date: String) {
        UnitySendMessage(objectName, methodName, date)
    }
}

ヘッダには以下のように書き出される。

@interface Callback : NSObject
- (nonnull instancetype)initWithObjectName:(NSString * _Nonnull)objectName methodName:(NSString * _Nonnull)methodName OBJC_DESIGNATED_INITIALIZER;
- (void)PickedWithDate:(NSString * _Nonnull)date;
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_DEPRECATED_MSG("-init is unavailable");
@end

Objective-Cからはこんな感じで呼び出します。

Callback *callback = [[Callback alloc] initWithObjectName:objStr methodName:methodStr];
[callback PickedWithDate:@"2019/06/07"];

その他気になったところ

Swift Versionの設定

上記設定をしてビルドをしようとすると、Swiftのバージョンについての問題を指摘されてエラーとなりビルドが失敗します。

設定項目があってそこを設定するだけなので、該当エラーが出たら以下の箇所を適切に設定してください。

f:id:edo_m18:20190606172936p:plain

stackoverflow.com