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

如果在应用启动时决定去完成一些操作，或是应用程序的状态发生一些改变，比如切入后台、重新激活时想要完成一些操作，在SwiftUI里将如何进行处理呢？我在英语小助手里就需要在启动时同步服务器数据到本地的数据库，另外在程序后台重新激活时再次尝试同步，所以在这里记录一张小纸条。

### 相关文档

从iOS14开始，SwiftUI在启动时可以使用App来进行应用初始化和管理。[Apple官方给出了App](https://developer.apple.com/documentation/swiftui/app)的文档。在 [App Structure and Behavior文档中](https://developer.apple.com/documentation/swiftui/app-structure-and-behavior) 中就提到你可以使用 [Scenes](https://developer.apple.com/documentation/swiftui/scenes) 来说明用户界面（通常就是我们写的View）了，同时也通过Scene来触发由系统管理的生命周期。


### 在App中管理状态处理

我的数据同步需要在App启动和从后台唤醒时进行，而且它的执行使用了async的异步调用，所以我特别 [准备了一个ViewModel](https://github.com/HDCodePractice/EnglishHelperApp/blob/main/LearnEnglishHelperApp/LearnEnglishHelperApp/LearnEnglishHelperViewModel.swift) 来帮助我完成这个工作：

```
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了（https://github.com/HDCodePractice/EnglishHelperApp/blob/main/LearnEnglishHelperApp/LearnEnglishHelperApp/LearnEnglishHelperAppApp.swift）：


```
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状态进行你自己的管理了。


