[Unity] iOS NativePluginの実装の仕方

# はじめに
Androidに引き続き、iOS側でもNativePluginの作成方法をまとめました。
ここでは、UnityでiOSのNativeアラートを表示させるまでの忘却録です。

Unity向けのiOS Pluginは作り方は人によって違うと思いますが、
今回は『UnityのiOSビルド結果であるXcodeプロジェクト上からコードを編集しました。』
もっと効率の良いやり方があればご教授下さい><;

Android版↓

https://qiita.com/atsutama/items/28744b1a29e79184c3fc

## 環境
macOS Catalina: 10.15.7
Unity 2018.4.19f
Xcode12.4
Swift4.0

## UnityとiOS Pluginの構成
Unityから書き出されるiOS関連のファイルはObjective-CベースなのでSwiftを呼び出す設定が必要です。
SwiftからObjective-Cのクラスに呼ぶには、宣言されてるヘッダーファイルを特定のヘッダーファイルでimportして使用します。
今回は`Objective-C++`のコードを経由させます。
呼び出しの簡易図は下記。

```
UnityのC#がインターフェース(.csファイル)
↑↓
↓↑
iOSネイティブ Objective-C++ Cインターフェース(.mmファイル)
↓↑
↓↑
iOSネイティブ Swiftコード(.swiftファイル)
```

## Unity側の対応(呼び出し方法)サンプル
ボタンを押下してアラート表示は、Androidの時の対応と同様な構成にしています。
![ 2021-11-29 19.11.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/c56d52ce-7f51-2364-3bf4-59a62557d649.png)


ここでのポイントは下記です。
①、`[DllImport("__Internal")]`属性を付けること。
[ネイティブプラグイン参照](https://docs.unity3d.com/ja/2018.4/Manual/NativePlugins.html)

```c#:ButtonView.cs
using System.Runtime.InteropServices;
using UnityEngine;

public class ButtonView : MonoBehaviour
{
    #if UNITY_IOS && !UNITY_EDITOR
	[DllImport("__Internal")]
	private static extern void _showAlert();
    #endif

    public void ShowNativeDialog() {
        #if UNITY_IOS && !UNITY_EDITOR
		_showAlert();
		#endif
    }
}
```

https://github.com/atsutama2/NativePluginTest/blob/main/Assets/Scripts/ButtonView.cs

## Unity上でSwiftファイル、Objective-C++のファイルを準備
`Assets`→`Plugin`→`iOS`ディレクトリが無ければ作成します。
ここにNativePluginのファイルを置きます。
![ 2021-09-13 19.03.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/b16efd9b-c196-5bdd-66d5-23073362b714.png)

`AlertPlugin.swift`、`AlertPlugin.mm`ファイルを作成。中身は空です。
![ 2021-09-13 19.40.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/6c9606b3-af1e-b666-6d25-0939b456629f.png)

## ビルド周りのEditor拡張が必要
やることは下記です。
①、Swiftのバージョン指定する(今回はSwift4.0)
②、`Objective-C Bridging Header`の設定
③、`Objective-C Generated Interface Header Name`の設定
上記を設定していきますが、便利なPluginが既に用意されてるのでこちらを参考にさせて頂きました。

https://github.com/miyabi/unity-swift

`Assets`→`Editer`→`PostProcessor.cs`こちらにファイルを作ります。
![ 2021-11-29 19.17.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/e7d576d9-3d33-b991-18ea-fb36122b8c87.png)

```c#:PostProcessor.cs
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using System.IO;

namespace UnitySwift {
    public static class PostProcessor {
        [PostProcessBuild]
        public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath) {
            if(buildTarget == BuildTarget.iOS) {
                var projPath = buildPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
                var proj = new PBXProject();
                proj.ReadFromFile(projPath);

                string xcodeTarget = proj.TargetGuidByName("Unity-iPhone");

                proj.AddBuildProperty(xcodeTarget, "SWIFT_VERSION", "4.0");
                proj.SetBuildProperty(xcodeTarget, "ENABLE_BITCODE", "NO");
                proj.SetBuildProperty(xcodeTarget, "SWIFT_OBJC_BRIDGING_HEADER", "Libraries/Plugins/iOS/UnitySwift-Bridging-Header.h");
                proj.SetBuildProperty(xcodeTarget, "SWIFT_OBJC_INTERFACE_HEADER_NAME", "Unity-iPhone-Swift.h");
                proj.AddBuildProperty(xcodeTarget, "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks");

                proj.WriteToFile(projPath);
            }
        }
    }
}

```

### UnitySwift-Bridging-Header.h
Swiftから呼び出すObjファイルをimportします。

```obj-c:UnitySwift-Bridging-Header.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "UnityInterface.h"
```

## ビルドしてiOSプロジェクトを書き出す
`File`→`Build Settings`→`Build`で書き出されるディレクトリ名を決めて作成。
![ 2021-09-28 16.20.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/b0b25be9-94e5-c901-39c8-4306d80a736a.png)

`Unity-iPhone.xcodeproj`をXcodeで展開します。
![ 2021-09-28 16.25.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/130ad024-4719-27ef-d8d3-f2b90616d2d3.png)


## Xcode上での作業
Xcodeを開くと下記のようなディレクトリ構造になっています。
Alertのコードは`Unity-iPhone`→`Libraries`→`Plugins`→`iOS`に内包されてます。
Unityで追加したファイルは`Libraries`に入るみたいです。
![ 2021-10-05 15.40.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/20fb5ed8-dd72-e024-7a7d-da41255b2c2d.png)

先程、ビルド周りのEditer拡張で指定した下記はこちらに設定されていますので、確認します。
①、Swiftのバージョン指定する(今回はSwift4.0)
②、`Objective-C Bridging Header`の設定
③、`Objective-C Generated Interface Header Name`の設定
![ 2021-11-29 20.52.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/75cc594b-6b5e-2d9d-dbdd-762fe7930b99.png)

Swiftコードは下記です。ここで必要なことは下記です。
①、クラスは [NSObject](https://developer.apple.com/documentation/objectivec/nsobject) を継承する
②、クラスのスコープを [public](https://developer.apple.com/documentation/os/oslogprivacy/3578098-public/) にする
※ `public` にするのは、`Objective-C++`から直接呼び出したいクラスおよびメソッドだけでも良い。
③、メソッドに[@objc](https://qiita.com/masakihori/items/4a715f28fc7f18d1a625)を付ける

```swift:AlertPlugin.swift
public class AlertPlugin : NSObject {
    
    @objc static func showAlert() {
        let alertView = UIAlertController(title: "Title", message: "AlertMessage", preferredStyle: UIAlertController.Style.alert)

        let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler:nil)
        let cancelButton = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler:nil)

        alertView.addAction(okAction)
        alertView.addAction(cancelButton)

        UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
    }
}

```



Objective-C++コードです。ここでのポイントは、下記です。
①、`extern "C"`宣言でCインターフェイスを作成。
②、`Objective-C Generated Interface Header Name`で設定している`Unity-iPhone-Swift.h`をimportすること。
これをimportすると`Objective-C++`のクラスのように呼び出すことができる。

```obj-c++:AlertPlugin.mm
#import "Unity-iPhone-Swift.h"

extern "C"
{
    void _showAlert()
    {
        [AlertPlugin showAlert];
    }
}

```

## 実機確認
![IMG_1747.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/2cd4f1da-2d05-7d77-80ac-964e415869a9.png)

## 成果物
https://github.com/atsutama2/NativePluginTest

## 参考
https://qiita.com/mao_/items/2305822054dadb2c5da8