如果在应用启动时决定去完成一些操作,或是应用程序的状态发生一些改变,比如切入后台、重新激活时想要完成一些操作,在SwiftUI里将如何进行处理呢?我在英语小助手里就需要在启动时同步服务器数据到本地的数据库,另外在程序后台重新激活时再次尝试同步,所以在这里记录一张小纸条。
相关文档
从iOS14开始,SwiftUI在启动时可以使用App来进行应用初始化和管理。Apple官方给出了App的文档。在 App Structure and Behavior文档中 中就提到你可以使用 Scenes 来说明用户界面(通常就是我们写的View)了,同时也通过Scene来触发由系统管理的生命周期。
在App中管理状态处理
我的数据同步需要在App启动和从后台唤醒时进行,而且它的执行使用了async的异步调用,所以我特别 准备了一个ViewModel 来帮助我完成这个工作:
class LearnEnglishHelperViewModel:ObservableObject{
private var realmController : RealmController
@Published var isLoading = true
init(){
realmController = RealmController.shared
Task{
await fetchData()
}
}
func fetchData() async{
isLoading = true
await realmController.fetchData()
isLoading = false
}
}
它的async的fetchData函数就是一个与服务器进行同步数据的异步函数了。它在ViewModel init时就会被执行一次,是因为在App启动时就会初始化这个ViewModel了(github.com/HDCodePractice/EnglishHelperApp/..):
import SwiftUI
@main
struct LearnEnglishHelperAppApp: App {
@StateObject var vm = LearnEnglishHelperViewModel()
@Environment(\.scenePhase) var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(vm)
}
.onChange(of: scenePhase) { phase in
switch phase{
case .active:
print("app active")
Task{
if vm.isLoading == false{
await vm.fetchData()
}
}
case .inactive:
print("app inactive")
case .background:
print("app background")
default:
print("app unknow status")
}
}
}
}
所以,只要App启动时,会init @stateObject var vm,所以也就触发了第一次的数据同步。之后通过scenePhash环境变量,只需要在Scene的.onChange里识别应用状态的变化即可。这里我只对active状态做了处理,也就是App在active状态时看看上次同步数据有没有完成,如果完成了,就再做一次数据同步。
在SwiftUI里对应用状态进行处理
其实我们在一个View里也可以使用相同的方法对应用状态发生变化进行处理。这里有一个示例:
import SwiftUI
struct ContentView: View {
@Environment(\.scenePhase) private var scenePhase
var body: some View {
Text("Hello, world!")
.padding()
.onChange(of: scenePhase, perform: { value in
if value == .active {
print("app active")
}
})
}
}
接下来你可以对App状态进行你自己的管理了。