跳至主内容

iOS - 在原生模块中使用 Swift

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

Swift 是 iOS 平台开发原生应用程序的官方默认语言。

本指南将带您探索如何使用 Swift 编写原生模块。

备注

React Native 的核心主要使用 C++ 编写,尽管 Apple 开发了互操作性层,但 Swift 与 C++ 的互操作性并不理想。

因此,由于语言兼容性问题,本指南中编写的模块并非纯 Swift 实现。您需要编写一些 Objective-C++ 胶水代码,但本指南的目标是尽量减少所需的 Objective-C++ 代码量。如果您正在将旧架构的原生模块迁移到新架构,这种方法可以让您复用大部分现有代码。

本指南基于原生模块指南的 iOS 实现展开。
在深入学习本指南前,请确保熟悉该指南内容,建议先完成其中的示例实现。

适配器模式

我们的目标是使用 Swift 模块实现所有业务逻辑,并通过 Objective-C++ 编写薄胶水层来连接应用与 Swift 实现。

您可以通过适配器设计模式实现这一目标,连接 Swift 模块与 Objective-C++ 层。

React Native 会创建 Objective-C++ 对象,该对象持有 Swift 模块的引用并管理其生命周期。所有方法调用都将通过 Objective-C++ 对象转发给 Swift。

创建 Swift 模块

第一步是将实现从 Objective-C++ 层迁移到 Swift 层。

请按以下步骤操作:

  1. 在 Xcode 项目中创建新文件,命名为 NativeLocalStorage.swift

  2. 在 Swift 模块中添加如下实现:

NativeLocalStorage.swift
import Foundation

@objcMembers public class NativeLocalStorage: NSObject {
let userDefaults = UserDefaults(suiteName: "local-storage");

public func getItem(for key: String) -> String? {
return userDefaults?.string(forKey: key)
}

public func setItem(for key: String, value: String) {
userDefaults?.set(value, forKey: key)
}

public func removeItem(for key: String) {
userDefaults?.removeObject(forKey: key)
}

public func clear() {
userDefaults?.dictionaryRepresentation().keys.forEach { removeItem(for: $0) }
}
}

请注意:所有需要通过 Objective-C 调用的方法必须声明为 public 并添加 @objc 注解。
同时确保您的类继承自 NSObject,否则无法从 Objective-C 调用。

更新 RCTNativeLocalStorage 文件

接下来需要更新 RCTNativeLocalStorage 的实现,使其能够创建 Swift 模块并调用其方法。

  1. 打开 RCTNativeLocalStorage.mm 文件

  2. 按如下方式更新:

RCTNativeLocalStorage.mm
//  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

请务必导入 "SampleApp-Swift.h" 头文件。该文件由 Xcode 自动生成,包含 Swift 文件的公开 API(以 Objective-C 兼容格式)。其中 SampleApp 是您的应用名称,所以如果应用名称不同SampleApp,则需要修改。

另请注意:不再需要 RCT_EXPORT_MODULE 宏,因为原生模块现在通过 package.json 注册,详见此文档

这种方法会导致接口出现少量代码重复,但能让您以最小成本复用现有代码库中的 Swift 代码。

实现桥接头文件

备注

如果你是库开发者,正在开发一个将作为独立库分发的原生模块,则无需执行此步骤。

连接 Swift 代码与 Objective-C++ 对应部分的最后必要步骤是创建桥接头文件。

桥接头文件是一个可以导入所有需要被 Swift 代码访问的 Objective-C 头文件的声明文件。

你的代码库中可能已有桥接头文件,如果尚未创建,请按以下步骤新建:

  1. 在 Xcode 中创建新文件,命名为 "SampleApp-Bridging-Header.h"

  2. 按以下内容更新 "SampleApp-Bridging-Header.h" 文件:

SampleApp-Bridging-Header.h
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

+ #import <React-RCTAppDelegate/RCTDefaultReactNativeFactoryDelegate.h>
  1. 在项目中关联桥接头文件:
    1. 在项目导航器中选中你的应用名称(左侧的 SampleApp
    2. 点击 Build Settings
    3. 搜索 "Bridging Header"
    4. 添加桥接头文件的相对路径(示例中为 SampleApp-Bridging-Header.h

桥接头文件

构建并运行应用

现在你可以按照原生模块指南的最后步骤操作,应用运行时应该能看到使用 Swift 编写的原生模块正常工作。