嗨!我正在寻找在我的 SwiftUI 应用程序中随处获取由用户选择的核心数据实体的最佳方法。该应用程序具有相当复杂的视图结构,包含多个选项卡、模式和导航堆栈级别。
正如您在下面看到的,该模型的结构方式是所有实体都以某种方式与一个实体(Home)相关。不过,可以有多个 Home 实体。然后,用户可以选择他们想要查看的Home,整个应用程序将根据该选择重新加载其内容。该选择也保存在用户默认值中,以便在启动期间保留。
因此,因为我基本上在(几乎)每个视图中都需要选定的 Home,所以我不想将其作为加载的每个视图的参数传递下去。
目前我正在使用
DataHandler()
管理器类,它(创建)并将选定的 Home 保存为 @Published
变量。该变量是一个可选变量,如果用户尚未创建 Home,它允许我处理入门。
@main
struct MyApp: App {
let persistenceController = PersistenceController.shared
// My custom manager class
@StateObject var dataHandler = DataHandler()
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(dataHandler)
}
}
}
class DataHandler: ObservableObject {
// Variable that holds the user selected Home entity
@Published var selectedHome: Home?
// User Defaults
@AppStorage("homeID") private var homeID: UUID?
init() {
// Check if User Default exists
if homeID != nil {
// Search for Home with User Default ID
selectedHome = fetchHomeBy(id: homeID!)
// Check if Home with ID exists
if selectedHome == nil {
loadFallbackHome()
}
} else {
// No UserDefault
loadFallbackHome()
}
}
// more stuff …
}
要访问给定视图中选定的 Home,我将使用
@EnvironmentObject
。然后,我想在 @FetchRequest
或 @SectionedFetchRequest
中过滤此选择的其他实体类型。获取请求还可以过滤其他变量或自定义排序。
struct EventsTabView: View {
@EnvironmentObject var dataHandler: DataHandler
@SectionedFetchRequest private var events: SectionedFetchResults<String, Event>
init() {
// Fetching Events of selected Home that aren’t archived
_events = SectionedFetchRequest(entity: Event.entity(),
sectionIdentifier: \.startYear,
sortDescriptors: [NSSortDescriptor(keyPath: \Event.startDate, ascending: false)],
predicate: NSPredicate(format: "eventHome == %@, isArchived != true", DataHandler().selectedHome!))
}
var body: some View {
// …
}
}
我目前遇到的问题是我无法访问
@FetchRequest
中的管理器类。不过,我需要它来过滤所选 Home 的 Meters 和 Events 以及其他过滤器参数!
@EnvironmentObjects
不能在自定义init()
中使用。我当前处理获取请求谓词的方式也不是很好,因为它总是创建 DataHandler()
的新实例。 👎
我尝试将获取请求放入计算变量中,该变量返回我需要的实体数组。那么问题是我的 UI 在添加/删除/编辑数据时无法正确更新。
我还考虑过在每个其他实体中使用 衍生 Home.id 属性。这样我只能将选定的 Home ID 存储在
@Published var
中。我想这在性能方面会更好吗?但这仍然引出了一个问题:如何在 @FetchRequests
中访问这个变量?
init()
的动态获取请求中访问应用程序范围的变量?@Published
类的 @EnvironmentObject
变量中?init()
?PS:我对 SwiftUI 相当陌生,所以如果有人能给我指明方向或指出更好的解决方案,我将非常感激。 :)
如果我有一个想要在复杂应用程序中的任何位置访问的对象,我会采用在 NSApplication 上创建扩展的方法。然后我将所需的对象设置为 NSApplication 上的属性。
这意味着在我的代码中的任何位置,我都可以通过 NSApp 来访问所需的对象,这当然是一个全局可用的 NSApplication 对象。
在我看来,这是最干净、最简单的方法。
戴夫
尝试一下:
struct EventsTabView: View {
@SceneStorage("selectedHomeID") var selectedHomeID: Home.ID?
@SectionedFetchRequest<String, Event>(
sectionIdentifier: \.startYear,
sortDescriptors: [SortDescriptor(\.startDate, order: .reverse)],
predicate: NSPredicate(value: false)
)
private var events: SectionedFetchRequest<String, Event>
func updatePredicate() {
events.nsPredicate = NSPredicate(format: "eventHome == %@, isArchived != true", selectedHomeID))
}
var body: some View {
let _ = updatePredicate()
// …
}
}
Home.ID
(即NSManagedObjectID
)可能需要编码为URL并再次返回才能使用@SceneStorage
让我知道如果是这种情况,有一些方法可以做到这一点。