SwiftUI应用程序初始化与状态管理

·

1 min read

如果在应用启动时决定去完成一些操作,或是应用程序的状态发生一些改变,比如切入后台、重新激活时想要完成一些操作,在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状态进行你自己的管理了。