UnityでSwiftの自作「Static Library」を使う

# 環境
- Unity 2018.4.23f1
- Xcode 11.5
- Swift 5
- iOS 13.4.1（iPhone 11）


# 手順の概要

**「Unity」**から直接**「Swift」**のコードを呼び出すことはできないようです。。。orz
その為、下記のような形で、**「Objective-C++」**を経由する事で、**「Swift」**の関数を呼び出します。

<img height="300px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/1ee9cc3c-aa67-9f3c-1c4c-db30460510ea.png">


# 設定手順
## 「Swift」の「Static Library」を作成
### 「Xcode」で、プロジェクトを作成

- 「iOS」→「Framework & Library」→「Static Library」を選択して、「Next」を押下
<img height="500px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/cf48523f-e055-f197-baf4-f7b031d4e1d8.png">


- 下記の項目を設定して、「Next」を押下<br>※記載以外はデフォルト値

| 項目 | 値 |
| --- | --- |
| Product Name | 任意 |
| Team | 任意 |
| Organization Name | 任意 |
| Organaization Identifier | 任意 |
| Language | Objective-C |

<img height="500px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/774556c4-3ff7-e43e-69b0-69dd3b8bf5d8.png">



- 任意のディレクトリにプロジェクトを作成

<img height="500px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/ae09d876-a046-a609-1f32-cfcf7d4c4ca3.png">


### 呼び出される「Swift」ファイルの作成

- 「プロジェクト」→「コンテキストメニュー」→「New File」を押下

<img height="700px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/37fc08b7-b2c5-7a16-82c8-e8490ccb069e.png">


- 「iOS」→「Source」→「Swift File」を選択して、「Next」を押下

<img height="600px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/e5523ab7-9a03-2f6f-c80b-55d148bc3b9b.png">


- 任意の名前を設定して、「Create」を押下

<img height="600px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/e0a3a4c7-9ae7-190e-f3a0-52f68d621393.png">


- 「Create Briding Header」を押下

<img height="300px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/d5f40272-6f20-90ff-70b9-00aafde57130.png">


- 作成した「Swift」ファイルを選択して、呼び出す関数を作成

<img height="350px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/05b09fd0-c02b-0155-dee2-bbb232befecc.png">

```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: 引数１
    @objc static func swiftCallTestArgument(val1: String) {
        NSLog("swiftCallTestArgument OK! Argument:" + val1)
    }

    /// 引数、戻り値あり
    /// - Parameters:
    ///     - val1: 引数１
    /// - Returns:戻り値
    @objc static func swiftCallTestArgumentReturn(val1: String) -> String {
        NSLog("swiftCallTestArgumentReturn OK!")
        return "Return swiftCallTestArgumentReturn Argument:" + val1;
    }
}
```



### 外部公開用の「Objective-C++」を作成する

- 「Command + B」で、Buildする<br>
※Swiftのヘッダーファイルを生成させる
- 初期で作成されている、「Objective-C」のファイルの拡張子を、「m」を、「mm」にする<br>
※「Objective-C++」に変更
- 変更したファイルに、Swiftを呼び出す関数を作成

<img height="350px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/907f213a-5817-039b-dbd6-9c82f2d3bcfc.png">

```Objective-C++
// 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」ファイルが作成される<br>
ファイルを選択した状態で、右のメニューの「Indentity andType」→「Full Path」の矢印を押下すると、格納ディレクトリを開きます

<img height="350px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/0d0a119b-e64f-bd73-7276-859608e29cfd.png">



## Unityで「Sttaic Library」を使用する

### ライブラリの配置

- 格納用ディレクトリを、下記の構成で作成

```
Assets
　├─ Efitor
　├─ Plugins
　|　└─ iOS
　└─ Scripts
```

<img height="500px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/d52e7e2b-5847-d9c9-2a52-481a2ff7e33f.png">


- 「Assets/Plugins/iOS」に生成された、「a」ファイルを格納

<img height="500px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/a848b915-7997-53b0-0c7d-a16f883e004f.png">




- 格納した「a」ファイル選択して、「Inspector」の下記の項目を設定して、「Apply」を押下

| 項目　| 値 |
| --- | --- |
| Select platforms for plugin -> Include Platfprms | 「iOS」のみにチェック |
| Platform settings | デフォルト |

<img height="800px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/aa7be49d-22a6-c728-1f4c-a43ed2cd9364.png">





### 呼び出しクラスの作成

- 呼び出し確認用のクラスを、「Assets/Scripts」に作成

<img height="400px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/8ce159f2-e280-fe40-ad16-cfd05b3baac1.png">


```C#
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("１.Unityからの呼び出しだ！");

        string result = CallTestArgumentReturn("２.Unityからの呼び出しだ！");
        Debug.Log("============================ Unity Result：" + result);
    }

    // Update is called once per frame
    void Update()
    {

    }
}
```
<br>


- 「Hierarchy」に、任意のGameObjectを作成して、作成したスクリプトをアタッチ

<img height="300px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/3660b02c-6a67-b141-cdc1-cffe5d27a393.png">






### ビルド設定

- 「Player setting」で、任意の設定をする<br>
※今回は下記の赤い部分のみを変更しました

<img height="700px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/5aeb1d47-d97f-07bc-821d-e12207747dad.png">



- 「Assets/Editor」配下に、拡張エディタを作成<br>
そのままだと、ビルドしたXcodeプロジェクトに、毎回設定をしないといけないので、拡張エディタを作成

<img height="300px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/694f618e-b945-0934-e5cb-f26072ec5f26.png">



``` C# 
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);
        }
    }
}
```



## ビルドして実機で実行
- ログに実行結果が出力されると思います

<img height="200px"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148696/a4980ea2-3de7-e053-3837-d50e16eff4e8.png">


# あとがき
今回、Swiftで作成されたライブラリを、Unityで呼び出す必要があり、
「Objective-C」、「Swift」を触った事ない状態から、色々試した結果です。

ビルドのプロパティについては、「Xcode」でネイティブアプリとして、
「Objective-C」と、「Objective-C + Swift」の二つを作成、解析して、必要な設定を割り出した物になります。

問題や追加の説明が欲しいなどが、あったらコメントなどで、教えていただけたら嬉しいです。