環境
- Unity 2018.4.23f1
- Xcode 11.5
- Swift 5
- iOS 13.4.1(iPhone 11)
手順の概要
**「Unity」から直接「Swift」のコードを呼び出すことはできないようです。。。orz
その為、下記のような形で、「Objective-C++」を経由する事で、「Swift」**の関数を呼び出します。
設定手順
「Swift」の「Static Library」を作成
「Xcode」で、プロジェクトを作成
- 「iOS」→「Framework & Library」→「Static Library」を選択して、「Next」を押下
- 下記の項目を設定して、「Next」を押下
※記載以外はデフォルト値
項目 | 値 |
---|---|
Product Name | 任意 |
Team | 任意 |
Organization Name | 任意 |
Organaization Identifier | 任意 |
Language | Objective-C |
- 任意のディレクトリにプロジェクトを作成
呼び出される「Swift」ファイルの作成
- 「プロジェクト」→「コンテキストメニュー」→「New File」を押下
- 「iOS」→「Source」→「Swift File」を選択して、「Next」を押下
- 任意の名前を設定して、「Create」を押下
- 「Create Briding Header」を押下
- 作成した「Swift」ファイルを選択して、呼び出す関数を作成
//
// SwiftTest.swift
// SwiftStaticLibrary
//
// Created by Kumatta_ss on 2020/05/31.
// Copyright © 2020 Kumatta_ss. All rights reserved.
//
import Foundation
class SwiftTest: NSObject {
/// 呼び出しのみ
@objc static func swiftCallTest() {
NSLog("swiftCallTest OK!")
}
/// 引数のみ
/// - Parameters:
/// - val1: 引数1
@objc static func swiftCallTestArgument(val1: String) {
NSLog("swiftCallTestArgument OK! Argument:" + val1)
}
/// 引数、戻り値あり
/// - Parameters:
/// - val1: 引数1
/// - Returns:戻り値
@objc static func swiftCallTestArgumentReturn(val1: String) -> String {
NSLog("swiftCallTestArgumentReturn OK!")
return "Return swiftCallTestArgumentReturn Argument:" + val1;
}
}
外部公開用の「Objective-C++」を作成する
- 「Command + B」で、Buildする
※Swiftのヘッダーファイルを生成させる - 初期で作成されている、「Objective-C」のファイルの拡張子を、「m」を、「mm」にする
※「Objective-C++」に変更 - 変更したファイルに、Swiftを呼び出す関数を作成
// Swiftの関数宣言ヘッダー(名称は、「{プロジェクト名}-Swift.h」形式)
#import <SwiftStaticLibrary-Swift.h>
// 外部に公開用の関数宣言
extern "C" {
void CallTest();
void CallTestArgument(const char *val1);
const char* CallTestArgumentReturn(const char *val1);
}
// 呼び出しのみ
void CallTest() {
NSLog(@"CallTest");
[SwiftTest swiftCallTest];
}
// 引数のみ
void CallTestArgument(const char *val1) {
NSLog(@"CallTestArgument");
[SwiftTest swiftCallTestArgumentWithVal1:@(val1)];
}
// 引数、戻り値あり
const char* CallTestArgumentReturn(const char *val1) {
NSLog(@"CallTestArgumentReturn");
NSString *result = [SwiftTest swiftCallTestArgumentReturnWithVal1:@(val1)];
const char *resultEncodeVal = [result cStringUsingEncoding:NSUTF8StringEncoding];
char *returnVal = (char*)malloc(strlen(resultEncodeVal) + 1);
strcpy(returnVal, resultEncodeVal);
return returnVal;
}
ライブラリの生成
- ビルドすると「Products」ディレクトリ配下に、「a」ファイルが作成される
ファイルを選択した状態で、右のメニューの「Indentity andType」→「Full Path」の矢印を押下すると、格納ディレクトリを開きます
Unityで「Sttaic Library」を使用する
ライブラリの配置
- 格納用ディレクトリを、下記の構成で作成
Assets
├─ Efitor
├─ Plugins
| └─ iOS
└─ Scripts
- 「Assets/Plugins/iOS」に生成された、「a」ファイルを格納
- 格納した「a」ファイル選択して、「Inspector」の下記の項目を設定して、「Apply」を押下
項目 | 値 |
---|---|
Select platforms for plugin -> Include Platfprms | 「iOS」のみにチェック |
Platform settings | デフォルト |
呼び出しクラスの作成
- 呼び出し確認用のクラスを、「Assets/Scripts」に作成
using System.Runtime.InteropServices;
using UnityEngine;
public class UnityCallTest : MonoBehaviour
{
[DllImport("__Internal")]
private static extern void CallTest();
[DllImport("__Internal")]
private static extern void CallTestArgument(string val1);
[DllImport("__Internal")]
private static extern string CallTestArgumentReturn(string val1);
// Start is called before the first frame update
void Start()
{
CallTest();
CallTestArgument("1.Unityからの呼び出しだ!");
string result = CallTestArgumentReturn("2.Unityからの呼び出しだ!");
Debug.Log("============================ Unity Result:" + result);
}
// Update is called once per frame
void Update()
{
}
}
- 「Hierarchy」に、任意のGameObjectを作成して、作成したスクリプトをアタッチ
ビルド設定
- 「Player setting」で、任意の設定をする
※今回は下記の赤い部分のみを変更しました
- 「Assets/Editor」配下に、拡張エディタを作成
そのままだと、ビルドしたXcodeプロジェクトに、毎回設定をしないといけないので、拡張エディタを作成
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
public class TestPostProcessBuild
{
[PostProcessBuild]
public static void BuildTest(BuildTarget buildTarget, string projectPath)
{
// iOSの場合のみ
if (BuildTarget.iOS == buildTarget)
{
string projectFilePath = PBXProject.GetPBXProjectPath(projectPath);
var proj = new PBXProject();
proj.ReadFromFile(projectFilePath);
string target = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
// BridgingHeaderを作成・設定
string projSwiftBridgingFile = "Classes/Unity-iPhone-Bridging-Header.h";
string swiftBridgingFile = Path.Combine(projectPath, projSwiftBridgingFile);
var fs = File.Create(swiftBridgingFile);
fs.Close();
string swiftBridgingFileGuid = proj.AddFile(swiftBridgingFile, projSwiftBridgingFile, PBXSourceTree.Source);
proj.AddFileToBuild(target, swiftBridgingFileGuid);
proj.SetBuildProperty(target, "SWIFT_OBJC_BRIDGING_HEADER", projSwiftBridgingFile);
// Xcodeのビルド設定
proj.SetBuildProperty(target, "CLANG_ENABLE_MODULES", "YES");
proj.AddBuildProperty(target, "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks");
proj.SetBuildProperty(target, "SWIFT_VERSION", "5.0");
proj.SetBuildProperty(target, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
proj.SetBuildProperty(target, "SWIFT_INSTALL_OBJC_HEADER", "YES");
proj.SetBuildProperty(target, "SWIFT_PRECOMPILE_BRIDGING_HEADER", "YES");
proj.SetBuildProperty(target, "SWIFT_OBJC_INTERFACE_HEADER_NAME", "$(PRODUCT_NAME)-Swift.h");
proj.AddBuildProperty(target, "LIBRARY_SEARCH_PATHS", "/usr/lib/swift/");
proj.AddFileToBuild(target, proj.AddFile("usr/lib/swift/libswiftFoundation.tbd", "Frameworks/libswiftFoundation.tbd", PBXSourceTree.Sdk));
proj.WriteToFile(projectFilePath);
}
}
}
ビルドして実機で実行
- ログに実行結果が出力されると思います
あとがき
今回、Swiftで作成されたライブラリを、Unityで呼び出す必要があり、
「Objective-C」、「Swift」を触った事ない状態から、色々試した結果です。
ビルドのプロパティについては、「Xcode」でネイティブアプリとして、
「Objective-C」と、「Objective-C + Swift」の二つを作成、解析して、必要な設定を割り出した物になります。
問題や追加の説明が欲しいなどが、あったらコメントなどで、教えていただけたら嬉しいです。