使用 SwiftUI 在后台同步数据 - Swift SDK
在此页面上
Overview
您可以使用SwiftUI BackgroundTask 以在您的应用处于背景时更新同步域 。此示例演示了如何在iOS应用中配置和执行背景同步。
您可以使用 SwiftUI Device Sync 模板应用程序按照此页面上的示例进行操作。 要获取您自己的SwiftUIDevice Sync Device SyncSwiftUIGoPrerequisitesStart with the Template模板应用程序副本,请查看 Device Sync 通读 和 部分。
为应用启用后台模式
要为您的应用启用后台任务,则执行以下操作:
安排后台任务
为您的应用启用后台进程后,您可以开始向应用添加代码,以安排和执行后台任务。首先,在您要编写此代码的文件中导入 BackgroundTasks
:
import SwiftUI import RealmSwift import BackgroundTasks
现在,您可以添加已计划后台任务。如果您正在通过模板应用跟进,可以更新您的 @main
视图:
@main struct realmSwiftUIApp: SwiftUI.App { private var phase (\.scenePhase) var body: some Scene { WindowGroup { ContentView(app: realmApp) } .onChange(of: phase) { newPhase in switch newPhase { case .background: scheduleAppRefresh() default: break } } }
您可以添加环境变量来存储对 scenePhase
: @Environment(\.scenePhase) private var phase
的更改。
然后,您可以添加.onChange(of: phase)
区块,以便在应用程序进入背景时调用scheduleAppRefresh()
功能。
创建 scheduleAppRefresh()
函数:
func scheduleAppRefresh() { let backgroundTask = BGAppRefreshTaskRequest(identifier: "refreshTodoRealm") backgroundTask.earliestBeginDate = .now.addingTimeInterval(10) try? BGTaskScheduler.shared.submit(backgroundTask) }
这会安排执行后台任务的工作,该任务的标识符在启用后台模式时添加到上述 Info.plist 中。在此示例中,标识符 refreshTodoRealm
系指该任务。
创建后台任务
既然已经安排了后台任务,就需要创建运行以更新同步 Realm 的后台任务。
如果您是按模板应用进行操作,则可在 .onChange(of: phase)
之后将此 backgroundTask
添加到 @main
视图:
.onChange(of: phase) { newPhase in switch newPhase { case .background: scheduleAppRefresh() default: break } } .backgroundTask(.appRefresh("refreshTodoRealm")) { guard let user = realmApp.currentUser else { return } let config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in if let foundSubscription = subs.first(named: "user_tasks") { foundSubscription.updateQuery(toType: Item.self, where: { $0.owner_id == user.id }) } else { subs.append(QuerySubscription<Item>(name: "user_tasks") { $0.owner_id == user.id }) } }, rerunOnOpen: true) await refreshSyncedRealm(config: config) }
此后台任务首先会检查您的应用是否有已登录用户。 如果是这样,它会设置.FlexibleSyncConfiguration 具有订阅,应用程序可以使用它来同步 Realm。
这与模板应用的ContentView
中使用的配置相同。 但是,要在这里使用它,您需要在视图层次结构的更高层访问它。 您可以将其重构为一个函数,您可以从任一视图调用该函数,该函数将User作为参数并返回Realm.configuration。
最后,此任务等待实际同步 realm 的函数的结果。添加此函数:
func refreshSyncedRealm(config: Realm.Configuration) async { do { try await Realm(configuration: config, downloadBeforeOpen: .always) } catch { print("Error opening the Synced realm: \(error.localizedDescription)") } }
通过打开此同步 realm 并使用 downloadBeforeOpen
参数指定您要下载更新,您可以在后台将新数据加载到该 realm 中。然后,当您的应用再次打开时,它已经在设备上拥有更新的数据。
重要
不要尝试在此后台任务中直接写入 realm。由于 Realm 的线程限制架构,您可能会遇到与线程相关的问题。
测试您的后台任务
安排后台任务时,您需要设置系统可以执行任务的最早时间。但是,操作系统会考虑许多其他因素,这些因素可能会在已计划的 earliestBeginDate
之后很长时间内延迟后台任务的执行。您可以设置断点并使用 LLDB 调用任务,而不是等待设备运行后台任务来验证其是否执行了您想要的操作。
配置设备以运行您的应用
要测试您的后台任务是否正在后台更新同步 Realm,您需要一台至少运行 iOS 16的物理设备。 您的设备必须配置为在 开发者模式下 运行 。如果收到Untrusted Developer
通知,请Go Settings、General和VPN & Device Management。 在这里,您可以验证是否要运行正在开发的应用程序。
可在设备上成功运行该应用后,便可测试后台任务。
设置断点
首先在 scheduleAppRefresh()
函数中设置断点。在将任务提交给 BGTaskScheduler
之后设置断点。在本例中,您可以添加 print
行,并在打印行处设置断点:
func scheduleAppRefresh() { let backgroundTask = BGAppRefreshTaskRequest(identifier: "refreshTodoRealm") backgroundTask.earliestBeginDate = .now.addingTimeInterval(10) try? BGTaskScheduler.shared.submit(backgroundTask) print("Successfully scheduled a background task") // Set a breakpoint here }
在 Atlas 中添加或更改数据
当应用处于背景但仍在 Xcode 中运行时,在相关 Atlas 集合中插入一个应同步到设备的新文档。或者,更改从设备创建的现有文档的值。成功运行背景任务后,应会看到这些数据从背景进程同步到该设备。
如果您使用的是 SwiftUI 模板应用,则可以在 Atlas 集群的Item
集合中找到相关文档。 有关如何在 Atlas 中添加或更改文档的更多信息,请参阅: MongoDB Atlas:创建、查看、更新和删除文档。
在 LLDB 中调用后台任务
使用此命令在 LLDB 中手动执行后台任务:
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"refreshTodoRealm"]
如果您为后台任务使用不同的标识符,则将 refreshTodoRealm
替换为您的任务的标识符。这会导致任务立即开始执行。
如果成功,则应看到如下所示内容:
2022-11-11 15:09:10.403242-0500 App[1268:196548] Simulating launch for task with identifier refreshTodoRealm 2022-11-11 15:09:16.530201-0500 App[1268:196811] Starting simulated task
启动该任务后,使用 Xcode 调试面板中的 Continue program execution(继续执行程序)按钮以继续运行该应用。