iOS - 在原生模块中使用 Swift
¥iOS - Using Swift in Your Native Modules
Swift 是开发 iOS 原生应用的官方默认语言。
¥Swift is the official and default language for developing native application on iOS.
在本指南中,你将探索如何使用 Swift 编写原生模块。
¥In this guide, you will explore how you can write your Native Modules using Swift.
React Native 的核心主要用 C++ 编写,尽管 互操作层 是由 Apple 开发的,但 Swift 和 C++ 之间的互操作性并不好。
¥The core of React Native is mainly written in C++ and the interoperability between Swift and C++ is not great, despite the interoperability layer developed by Apple.
因此,由于语言之间的不兼容性,你将在本指南中编写的模块不会是纯 Swift 实现。你需要编写一些 Objective-C++ 粘合代码,但本指南的目标是最大限度地减少所需的 Objective-C++ 代码量。如果你要将现有的原生模块从旧架构迁移到新架构,这种方法应该允许你重用大部分代码。
¥Therefore, the module you are going to write in this guide won't be a pure Swift implementation due to the incompatibilities between the languages. You'll have to write some Objective-C++ glue code but the goal of the guide is to minimize the amount of Objective-C++ code that is needed. If you are migrating an existing Native Modules from the legacy architecture to the New Architecture, this approach should allow you to reuse most of the code.
本指南基于 原生模块 指南的 iOS 实现。在深入研究本指南之前,请务必熟悉该指南,并可能实现指南中的示例。
¥This guide starts from the iOS implementation of the Native Module guide. Make sure to be familiar with that guide before diving into this one, potentially implementing the example in the guide.
适配器模式
¥The Adapter pattern
目标是使用 Swift 模块实现所有业务逻辑,并在 Objective-C++ 中创建一个薄粘合层,以便将应用与 Swift 实现连接起来。
¥The goal is to implement all our business logic using a Swift module and have a thin glue layer in Objective-C++ that is able to connect the app with the Swift implementation.
你可以利用 适配器 设计模式将 Swift 模块与 Objective-C++ 层连接起来。
¥You can achieve this by leveraging the Adapter design pattern, to connect the Swift Module with the Objective-C++ layer.
Objective-C++ 对象由 React Native 创建,并保留对 Swift 模块的引用,用于处理其生命周期。Objective-C++ 对象将所有方法调用转发给 Swift。
¥The Objective-C++ object is created by React Native and it keeps a reference to the Swift module, handling its lifecycle. The Objective-C++ object forwards to the all the methods invocation to Swift.
创建 Swift 模块
¥Creating the Swift Module
第一步是将实现从 Objective-C++ 层移到 Swift 层。
¥The first step is to move the implementation from the Objective-C++ layer to the Swift Layer.
为此,请按照以下步骤操作:
¥To achieve that, please follow these steps:
-
在 Xcode 项目中创建一个新的空文件,并将其命名为
NativeLocalStorage.swift
¥Create a new empty file in the Xcode project, and call it
NativeLocalStorage.swift
-
在你的 Swift 模块中添加实现,如下所示:
¥Add the implementation in your Swift module like it follows:
import Foundation
@objc public class NativeLocalStorage: NSObject {
let userDefaults = UserDefaults(suiteName: "local-storage");
@objc public func getItem(for key: String) -> String? {
return userDefaults?.string(forKey: key)
}
@objc public func setItem(for key: String, value: String) {
userDefaults?.set(value, forKey: key)
}
@objc public func removeItem(for key: String) {
userDefaults?.removeObject(forKey: key)
}
@objc public func clear() {
userDefaults?.dictionaryRepresentation().keys.forEach { removeItem(for: $0) }
}
}
请注意,你必须将所有需要从 Objective-C 调用的方法声明为 public
,并使用 @objc
注解。请记住,还要让你的类继承自 NSObject
,否则将无法在 Objective-C 中使用它。
¥Notice that you have to declare all the methods that you need to call from Objective-C as public
and with the @objc
annotation.
Remember also to make your class inherit from NSObject
, otherwise it would not be possible to use it from Objective-C.
更新 RCTNativeLocalStorage
文件
¥Update the RCTNativeLocalStorage
file
然后,你需要更新 RCTNativeLocalStorage
的实现,以便能够创建 Swift 模块并调用其方法。
¥Then, you need to update the implementation of the RCTNativeLocalStorage
to be able to create the Swift module and to call its methods.
-
打开
RCTNativeLocalStorage.mm
文件¥Open the
RCTNativeLocalStorage.mm
file -
按如下方式更新它:
¥Update it as it follows:
// RCTNativeLocalStorage.m
// TurboModuleExample
#import "RCTNativeLocalStorage.h"
+#import "SampleApp-Swift.h"
- static NSString *const RCTNativeLocalStorageKey = @"local-storage";
-@interface RCTNativeLocalStorage()
-@property (strong, nonatomic) NSUserDefaults *localStorage;
-@end
-@implementation RCTNativeLocalStorage
+@implementation RCTNativeLocalStorage {
+ NativeLocalStorage *storage;
+}
-RCT_EXPORT_MODULE(NativeLocalStorage)
- (id) init {
if (self = [super init]) {
- _localStorage = [[NSUserDefaults alloc] initWithSuiteName:RCTNativeLocalStorageKey];
+ storage = [NativeLocalStorage new];
}
return self;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
return std::make_shared<facebook::react::NativeLocalStorageSpecJSI>(params);
}
- (NSString * _Nullable)getItem:(NSString *)key {
- return [self.localStorage stringForKey:key];
+ return [storage getItemFor:key];
}
- (void)setItem:(NSString *)value key:(NSString *)key {
- [self.localStorage setObject:value forKey:key];
+ [storage setItemFor:key value:value];
}
- (void)removeItem:(NSString *)key {
- [self.localStorage removeObjectForKey:key];
+ [storage removeItemFor:key];
}
- (void)clear {
- NSDictionary *keys = [self.localStorage dictionaryRepresentation];
- for (NSString *key in keys) {
- [self removeItem:key];
- }
+ [storage clear];
}
++ (NSString *)moduleName
+{
+ return @"NativeLocalStorage";
+}
@end
代码实际上没有改变。你无需直接创建对 NSUserDefaults
的引用,而是使用 Swift 实现创建一个新的 NativeLocalStorage
。每当调用原生模块函数时,调用都会转发到 Swift 实现的 NativeLocalStorage
。
¥The code is not really changed. Instead of creating a reference to the NSUserDefaults
directly, you create a new NativeLocalStorage
using the swift implementation and, whenever a native module function is invoked, the invocation is forwarded to the NativeLocalStorage
implemented in Swift.
请记住导入 "SampleApp-Swift.h"
标头。这是一个由 Xcode 自动生成的头文件,其中包含 Swift 文件的公共 API,其格式可供 Objective-C 使用。标头中的 SampleApp
部分实际上是你的应用名称,因此,如果你创建的应用名称与 SampleApp
不同,则必须更改它。
¥Remember to import the "SampleApp-Swift.h"
header. This is a header automatically generated by Xcode which contains the public API of your Swift files, in a format that is consumable by Objective-C. The SampleApp
part of the header is actually your App name, so if you created the app with a name that is different from SampleApp
, you'll have to change it.
另请注意,RCT_EXPORT_MODULE
宏不再需要,因为原生模块是使用 package.json
注册的,如 此处 中所述。
¥Note also that the RCT_EXPORT_MODULE
macro is not required anymore, because native modules are registered using the package.json
as described here.
这种方法会在接口中引入一些代码重复,但它允许你重用代码库中可能已有的 Swift 代码,而无需额外付出任何努力。
¥This approach introduces a bit of code duplication in the interfaces, but it allows you to reuse the Swift code you may already have in your codebase, with little extra effort.
实现桥接头文件
¥Implementing the Bridging Header
如果你是一位库作者,正在开发一个原生模块,并将其作为单独的库分发,则无需执行此步骤。
¥If you are a library author, developing a native module that is going to be distributed as a separate library, this step is not required.
将 Swift 代码与 Objective-C++ 对应部分连接起来的最后一步是桥接头。
¥The last required step to connect the Swift code with the Objective-C++ counterpart is a bridging header.
桥接头文件是一个头文件,你可以在其中导入所有需要在你的 Swift 代码中可见的 Objective-C 头文件。
¥A bridging header is an header where you can import all the Objective-C header files that needs to be visible by your swift code.
你的代码库中可能已经有桥接头文件,但如果没有,你可以按照以下步骤创建一个新的:
¥You might already have a bridging header in your codebase, but in case you haven't, you can create a new one by following these steps:
-
在 Xcode 中,创建一个新文件并将其命名为
"SampleApp-Bridging-Header.h"
¥In Xcode, create a new file and call it
"SampleApp-Bridging-Header.h"
-
按如下方式更新
"SampleApp-Bridging-Header.h"
的内容:¥Update the content of the
"SampleApp-Bridging-Header.h"
like this:
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
+ #import <React-RCTAppDelegate/RCTDefaultReactNativeFactoryDelegate.h>
-
将桥接头文件链接到你的项目中:
¥Link the Bridging header in your project:
-
在项目导航器中,选择你的应用名称(左侧的
SampleApp
)。¥In the project navigator, select your app name (
SampleApp
, on the left) -
单击
Build Settings
¥Click on
Build Settings
-
"Bridging Header"
过滤器¥Filter for
"Bridging Header"
-
添加 "桥接头文件" 的相对路径,在本例中为
SampleApp-Bridging-Header.h
¥Add the relative path to the "Bridging Header", in the example it is
SampleApp-Bridging-Header.h
-
构建并运行你的应用
¥Build and Run Your App
现在你可以按照 原生模块指南 的最后一步操作,你应该会看到你的应用正在使用用 Swift 编写的原生模块运行。
¥Now you can follow the last step of the Native Module's guide and you should see your app running with a Native Module written in Swift.