如何使用Expo React Native创建iOS锁屏小组件?
使用Expo React Native实现iOS锁屏小组件
明确说明:Expo官方目前没有直接支持锁屏小组件的API,必须通过原生代码集成SwiftUI Widget,再和你的Expo应用打通数据交互。以下是具体步骤和代码示例:
1. 配置EAS构建环境
确保你的Expo项目已启用EAS(用于自定义原生代码):
eas build:configure
2. 添加SwiftUI Widget目标
在Xcode中打开项目的iOS工程(路径为ios/YourProjectName.xcworkspace):
- 点击菜单栏
File > New > Target - 选择
Widget Extension,命名为LockScreenWidget,可选勾选"Include Configuration Intent"(用于动态配置) - 确认激活目标,Xcode会自动生成Widget基础代码
3. 编写锁屏适配的SwiftUI代码
替换自动生成的LockScreenWidget.swift内容,适配锁屏的systemSmall尺寸:
import WidgetKit import SwiftUI import Intents struct Provider: IntentTimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date(), configuration: ConfigurationIntent(), widgetData: "默认数据") } func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date(), configuration: configuration, widgetData: "快照数据") completion(entry) } func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] // 从共享组UserDefaults读取React Native传递的数据 let sharedData = UserDefaults(suiteName: "group.com.yourapp.bundleid")?.string(forKey: "widgetData") ?? "无数据" // 生成每15分钟刷新一次的时间线 let currentDate = Date() for minuteOffset in 0 ..< 4 { let entryDate = Calendar.current.date(byAdding: .minute, value: minuteOffset * 15, to: currentDate)! let entry = SimpleEntry(date: entryDate, configuration: configuration, widgetData: sharedData) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } } struct SimpleEntry: TimelineEntry { let date: Date let configuration: ConfigurationIntent let widgetData: String } struct LockScreenWidgetEntryView : View { var entry: Provider.Entry var body: some View { VStack { Text("锁屏组件") .font(.headline) Text(entry.widgetData) .font(.caption) } .padding() .background(Color.gray.opacity(0.1)) } } struct LockScreenWidget: Widget { let kind: String = "LockScreenWidget" var body: some WidgetConfiguration { IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in LockScreenWidgetEntryView(entry: entry) } .configurationDisplayName("我的锁屏组件") .description("显示来自App的动态数据") .supportedFamilies([.systemSmall]) // 指定仅支持锁屏小尺寸 } } struct LockScreenWidget_Previews: PreviewProvider { static var previews: some View { LockScreenWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent(), widgetData: "预览数据")) .previewContext(WidgetPreviewContext(family: .systemSmall)) } }
4. 配置App Groups实现数据共享
为了让React Native应用和Widget互通数据,需要配置App Groups:
- 选择主App目标,进入
Signing & Capabilities,点击+ Capability添加App Groups - 创建组ID,比如
group.com.yourapp.bundleid - 给Widget目标添加相同的App Groups配置
5. 在React Native中传递数据到Widget
使用expo-modules-core编写自定义模块,实现数据写入和Widget刷新:
先安装依赖:
npx expo install expo-modules-core
React Native侧代码:
import { Platform } from 'react-native'; import { NativeModules } from 'react-native'; // 向Widget写入数据并触发刷新 export const updateWidgetData = async (data) => { if (Platform.OS !== 'ios') return; try { await NativeModules.ExpoWidgetData.setSharedData('widgetData', data, 'group.com.yourapp.bundleid'); } catch (err) { console.error('更新Widget数据失败:', err); } };
对应的Swift原生模块(创建ExpoWidgetData.swift):
import ExpoModulesCore public class ExpoWidgetData: Module { public func definition() -> ModuleDefinition { Name("ExpoWidgetData") AsyncFunction("setSharedData") { (key: String, value: String, groupId: String) in let userDefaults = UserDefaults(suiteName: groupId) userDefaults?.set(value, forKey: key) // 通知Widget刷新时间线 WidgetCenter.shared.reloadAllTimelines() } } }
6. 构建测试
使用EAS构建iOS包并安装到测试设备:
eas build --platform ios
安装完成后,进入锁屏编辑模式,添加你创建的小组件即可测试效果。
内容的提问来源于stack exchange,提问作者Dna
相关产品推荐
相关产品推荐

