SwiftUI UserDefaults 干扰 Firebase Firestore 数据
SwiftUI UserDefaults interfering with Firebase Firestore data
我正在将 Google Firebase 的 Cloud Firestore 服务与 SwiftUI 项目一起使用,该项目详细介绍了电子游戏中每张地图的策略。我正在使用 View、ViewModel、Model 架构。可能还值得注意的是,我正在使用 Swift 包管理器而不是 CocoaPods 来导入 API。
我有一个 ScrollView,其中包含从 Firestore 集合中获取的一些地图。点击地图将向用户显示另一个滚动视图,其中列出了从 Firestore 中的单独集合中获取的策略。我遇到的问题是,每当更改 UserDefault(使用 UserDefaults.standard.setValue(...)
或 @AppStorage("settingName") var...
)时,ScrollView 的内容就会消失。 Firestore 数据和 UserDefaults 之间没有关系(没有任何内容从一个保存到另一个)。
尽管代码从一个 ViewModel swift 文件复制并粘贴到另一个文件,但此 仅 发生在详细视图中,而不是主视图中。
这是 StratsViewModel.swift 代码:
class StratsViewModel: ObservableObject {
@Published var strats = [Strat]()
private var db = Firestore.firestore()
func fetchData(forMap: String) {
if strats.isEmpty {
db.collection("maps/\(forMap)/strats").getDocuments { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
return
}
self.strats = documents.map { (queryDocumentSnapshot) -> Strat in
let data = queryDocumentSnapshot.data()
let id = queryDocumentSnapshot.documentID
let name = data["name"] as? String ?? ""
let map = data["map"] as? String ?? ""
let type = data["type"] as? String ?? ""
let side = data["side"] as? String ?? ""
let strat = Strat(id: id, name: name, map: map, type: type, side: side)
return strat
}
}
}
}
}
在MapsDetailView.swift(由MapsView.swift呈现)ScrollView中,策略显示如下:
struct MapsDetailView: View {
var map: Map
@ObservedObject private var viewModel = StratsViewModel()
var body: some View {
ScrollView {
ForEach(viewModel.strats, id: \.self) { strat in
NavigationLink(destination: StratView()) {
StratCell(strat: strat)
}
}
}
.onAppear() {
self.viewModel.fetchData(forMap: map.id)
}
}
}
如果我要向视图添加一个按钮,例如,将任何值设置为 UserDefaults 中的任何键,ScrollView 中的几十个项目就会消失。
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
UserDefaults.standard.setValue("test", forKey: "testKey")
} label: {
Text("Don't press me!")
}
}
}
使用 TabBar 导航到另一个视图,然后返回到 MapDetailView 将触发 .onAppear
方法,但是,在上面显示的 ForEach
行添加一个断点,表明此循环不会't 运行 直到视图被完全关闭并重新打开。
在 TabBar 中切换选项卡时也会出现此问题,因为在 onAppear 时,每个视图都会设置一个 tabSelection 键,以记住应用程序被终止时用户最后选择的选项卡。从一个选项卡切换回 MapsDetailView 选项卡将删除 ScrollView 中的所有单元格。
有什么想法吗?如有必要,很乐意分享更多源代码。
当您初始化 viewModel
时,您应该使用 @StateObject
而不是 @ObservedObject
。这两个 属性 包装器几乎相同,除了 @StateObject
将确保对象在更新/重新呈现视图时保持“活动”状态,而不是重新初始化。以下是有关此主题的一些重要资源:
我正在将 Google Firebase 的 Cloud Firestore 服务与 SwiftUI 项目一起使用,该项目详细介绍了电子游戏中每张地图的策略。我正在使用 View、ViewModel、Model 架构。可能还值得注意的是,我正在使用 Swift 包管理器而不是 CocoaPods 来导入 API。
我有一个 ScrollView,其中包含从 Firestore 集合中获取的一些地图。点击地图将向用户显示另一个滚动视图,其中列出了从 Firestore 中的单独集合中获取的策略。我遇到的问题是,每当更改 UserDefault(使用 UserDefaults.standard.setValue(...)
或 @AppStorage("settingName") var...
)时,ScrollView 的内容就会消失。 Firestore 数据和 UserDefaults 之间没有关系(没有任何内容从一个保存到另一个)。
尽管代码从一个 ViewModel swift 文件复制并粘贴到另一个文件,但此 仅 发生在详细视图中,而不是主视图中。
这是 StratsViewModel.swift 代码:
class StratsViewModel: ObservableObject {
@Published var strats = [Strat]()
private var db = Firestore.firestore()
func fetchData(forMap: String) {
if strats.isEmpty {
db.collection("maps/\(forMap)/strats").getDocuments { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
return
}
self.strats = documents.map { (queryDocumentSnapshot) -> Strat in
let data = queryDocumentSnapshot.data()
let id = queryDocumentSnapshot.documentID
let name = data["name"] as? String ?? ""
let map = data["map"] as? String ?? ""
let type = data["type"] as? String ?? ""
let side = data["side"] as? String ?? ""
let strat = Strat(id: id, name: name, map: map, type: type, side: side)
return strat
}
}
}
}
}
在MapsDetailView.swift(由MapsView.swift呈现)ScrollView中,策略显示如下:
struct MapsDetailView: View {
var map: Map
@ObservedObject private var viewModel = StratsViewModel()
var body: some View {
ScrollView {
ForEach(viewModel.strats, id: \.self) { strat in
NavigationLink(destination: StratView()) {
StratCell(strat: strat)
}
}
}
.onAppear() {
self.viewModel.fetchData(forMap: map.id)
}
}
}
如果我要向视图添加一个按钮,例如,将任何值设置为 UserDefaults 中的任何键,ScrollView 中的几十个项目就会消失。
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
UserDefaults.standard.setValue("test", forKey: "testKey")
} label: {
Text("Don't press me!")
}
}
}
使用 TabBar 导航到另一个视图,然后返回到 MapDetailView 将触发 .onAppear
方法,但是,在上面显示的 ForEach
行添加一个断点,表明此循环不会't 运行 直到视图被完全关闭并重新打开。
在 TabBar 中切换选项卡时也会出现此问题,因为在 onAppear 时,每个视图都会设置一个 tabSelection 键,以记住应用程序被终止时用户最后选择的选项卡。从一个选项卡切换回 MapsDetailView 选项卡将删除 ScrollView 中的所有单元格。
有什么想法吗?如有必要,很乐意分享更多源代码。
当您初始化 viewModel
时,您应该使用 @StateObject
而不是 @ObservedObject
。这两个 属性 包装器几乎相同,除了 @StateObject
将确保对象在更新/重新呈现视图时保持“活动”状态,而不是重新初始化。以下是有关此主题的一些重要资源: