用于在Swift中编写iOS本机插件的本机插件


Unity的iOS本机插件是用Objective-C编写的,从Objective-C到Unity(C#)端的方法调用是UnitySendMessage函数,从Unity到Objective-C端的方法调用是extern "C"是通过声明的C接口来完成的。

最近,Swift本机开发人员的数量有所增加,因此,正如标题所示,我做了一个"用于在Swift中编写Unity的iOS本机插件的本地插件"。

从Swift

在Unity端调用方法

要从Swift访问C函数和Objective-C类,请在特定头文件(Bridging Header)中导入在其中声明了它们的头文件。
当在Xcode中创建新项目时,在选择Swift作为语言的项目中创建Objective-C文件时,或者在Objective-C项目中创建Swift文件时,Bridgeging Header都是自动的。创建这个。此时的默认文件名为<Product Name>-Swift-Bridge.h,但是可以在"构建设置/ Swift编译器-代码生成"的Objective-C Bridging Header(SWIFT_OBJC_BRIDGING_HEADER)设置中进行更改。

从Objective-C调用Unity端方法的UnitySendMessage函数和获取Unity场景的根视图控制器的UnityGetGLViewController函数包含在构建Unity项目所生成的Xcode项目中。它使用x7>声明,并使用桥接头将其导入。
同样,UnityInterface.h引用诸如NSStringUIView之类,因此也要导入Foundation.hUIKit.h

UnitySwift-Bridging-Header.h

1
2
3
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "UnityInterface.h"

现在您可以直接从Swift调用C函数,还可以使用UnitySendMessage函数在Unity端调用方法。

Example.swift

1
2
3
4
5
6
7
8
import Foundation

class Example : NSObject {
    static func callUnityMethod() {
        // Call a method on a specified GameObject.
        UnitySendMessage("CallbackTarget", "OnCallFromSwift", "Hello, Unity!")
    }
}

从Unity

在Swift端调用方法

从Unity访问Swift类类似于从传统Unity访问Objective-C类。

步骤1:建立Swift类别

Example.swift

1
2
3
4
5
6
7
import Foundation

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

步骤2:创建一个.mm(Objective-C)文件,并创建一个带有外部" C"声明的C接口

例子.mm

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
#import "unityswift-Swift.h"    // Required

extern "C" {
    void _ex_callSwiftMethod(const char *message) {
        // You can access Swift classes directly here.
        [Example swiftMethod:[NSString stringWithUTF8String:message]];
    }
}

这时,您可以通过导入名为<Product Module Name>-Swift.h(在上例中为unityswift-Swift.h)的头文件,从Objective-C访问Swift类。
当您在Xcode中运行构建时,此头文件会在DerivedData下自动生成。您还可以在Build Settings / Swift Compiler --Code Generation Objective-C Generated Interface Header Name(SWIFT_OBJC_INTERFACE_HEADER_NAME)设置中更改文件名。

步骤3:在C#端创建接口

通过在C#端添加[DllImport("__Internal")]属性,可以访问步骤2中在extern "C"中声明的C接口。

Example.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.Runtime.InteropServices;

public class Example {
    #if UNITY_IOS && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern void _ex_callSwiftMethod(string message);
    #endif

    public static void CallSwiftMethod(string message) {
        #if UNITY_IOS && !UNITY_EDITOR
        _ex_callSwiftMethod(message);
        #endif
    }
}

现在,您可以使用以下描述从C#类调用Swift端的方法。

1
Example.CallSwiftMethod("Hello, Swift!");

在传统的Objective-C本机插件中,步骤1中的类定义和步骤2中的接口声明被写入一个.mm文件中。
通过将类定义部分移植到Swift而不更改接口声明,可以像以前一样从C#访问它。

运行路径搜索路径设置

至此,您可以在Swift和C#之间调用方法,但是在运行时会发生以下错误。

1
dyld: Library not loaded: @rpath/libswiftCore.dylib

这是因为通过构建Unity项目生成的Xcode项目在"构建设置/链接"中未设置Runpath Search Paths(LD_RUNPATH_SEARCH_PATHS),可以通过将其设置为@executable_path/Frameworks来避免。

本机插件

桥接头和Objective-C生成的接口头名称默认情况下取决于产品名称,因此它会根据项目而每次更改,并且每次手动设置Runpath Search Paths都很麻烦,因此这是本机插件进入。

miyabi / unity-swift:本机插件,用于在Swift中为Unity编写本机代码。

该插件在构建Unity项目时自动配置以下设置。

  • Objective-C Bridging Header设置为UnitySwift-Bridging-Header.h并预先导入Foundation.hUIKit.hUnityInterface.h
  • Objective-C Generated Interface Header Name设置为unityswift-Swift.h
  • Runpath Search Paths设置为@executable_path/Frameworks

有关使用unity-swift在Swift中实际创建本机插件的示例,请参见Unity ReplayKit Bridge的swift分支。

unity-replay-kit-bridge /示例/资产/ UnityReplayKitBridge快速·miyabi / unity-replay-kit-bridge