前一章介紹了 SwiftUI Clean Architecture
架構的實作,本章詳細介紹 AppState
的一些細節。
前一章我們在實作了 AppState
結構,並在內部儲存一個 members
變數:
import Foundation
import Combine
struct AppState {
var members = CurrentValueSubject<[Member]?, Never>([])
}
那麼我們該如何在 View
內使用呢?
首先我們先在 ContentView
內加入 container
和 members
:
@Environment(\.injected) private var container: DIContainer
@State private var members: [Member] = []
接下來把 members
顯示畫面刻出來:
@ViewBuilder
private func memberList() -> some View {
VStack {
if members.isEmpty {
Text("Empty Member!")
} else {
ForEach(members, id: \.id) { member in
Text("\(member.id) \(member.name)")
}
}
}
}
onReceive
修飾器提供訂閱 Publisher
的功能,我們可以利用 onReceive
來訂閱 AppState
內的資料:
.onReceive(container.appState!.members) { data in
members = data ?? []
}
也可以寫成這樣:
.onReceive(container.appState!.members) { members = $0 ?? [] }
這邊
appState!
未來會修掉,這邊只是因為方便說明所以把AppState
定義為Optional
別忘了要把 PreviewProvider
內注入 DIContainer
,不然 Canvas
會噴錯:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environment(\.injected, DIContainer(appState: AppState()))
}
}
因為目前還沒有實作 Interactor
,我們可以先實作一個按鈕來測試看看資料有沒有訂閱到:
@ViewBuilder
private func TestButton() -> some View {
Button("Test Push Members") {
let randomID = Int64.random(in: 1...50)
container.appState?.members.send([
Member(id: randomID, name: "Faker", position: .Mid),
Member(id: 50 - randomID, name: "MaRin", position: .Top)
])
}
.buttonStyle(.borderedProminent)
}
利用
send
方法可以對CurrentValueSubject
推送資料這邊用
Int64.random
方法來建立亂數的id
,方便你從id
判斷資料有沒有改變
實作完按鈕後可以在 Canvas
測試看看按下按鈕畫面的資料會不會改變?
最後的程式碼會長這樣:
import SwiftUI
struct ContentView: View {
@Environment(\.injected) private var container: DIContainer
@State private var members = [Member]()
var body: some View {
VStack {
memberList()
TestButton()
}
.onReceive(container.appState!.members) { members = $0 ?? [] }
}
@ViewBuilder
private func memberList() -> some View {
VStack {
if members.isEmpty {
Text("Empty Member!")
} else {
ForEach(members, id: \.id) { member in
Text("\(member.id) \(member.name)")
}
}
}
}
@ViewBuilder
private func TestButton() -> some View {
Button("Test Push Members") {
let randomID = Int64.random(in: 1...50)
container.appState?.members.send([
Member(id: randomID, name: "Faker", position: .Mid),
Member(id: 50 - randomID, name: "MaRin", position: .Top)
])
}
.buttonStyle(.borderedProminent)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environment(\.injected, DIContainer(appState: AppState()))
}
}
View
利用 onReceive
可以訂閱 AppState
內的 Publisher
。
當 AppState
內的資料改變時,View
就會收到推送並更新顯示的資料。