SwiftUI:在获取请求中访问核心数据实体选择的最佳实践

问题描述 投票:0回答:2

总结

嗨!我正在寻找在我的 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
中的管理器类。不过,我需要它来过滤所选 HomeMetersEvents 以及其他过滤器参数!

我了解到

@EnvironmentObjects
不能在自定义
init()
中使用。我当前处理获取请求谓词的方式也不是很好,因为它总是创建
DataHandler()
的新实例。 👎

我尝试将获取请求放入计算变量中,该变量返回我需要的实体数组。那么问题是我的 UI 在添加/删除/编辑数据时无法正确更新。

我还考虑过在每个其他实体中使用 衍生 Home.id 属性。这样我只能将选定的 Home ID 存储在

@Published var
中。我想这在性能方面会更好吗?但这仍然引出了一个问题:如何在
@FetchRequests
中访问这个变量?

所以我的问题是:

  1. 如何在我的
    init()
    的动态获取请求中访问应用程序范围的变量?
  2. 是否有更好的设计来处理这样的用户选择 而不是将 整个实体 加载到
    @Published
    类的
    @EnvironmentObject
    变量中?
  3. 使用计算变量存储获取请求结果是否比在视图中执行获取请求更糟糕(性能方面)
    init()

PS:我对 SwiftUI 相当陌生,所以如果有人能给我指明方向或指出更好的解决方案,我将非常感激。 :)

swift swiftui core-data nsfetchrequest
2个回答
1
投票

如果我有一个想要在复杂应用程序中的任何位置访问的对象,我会采用在 NSApplication 上创建扩展的方法。然后我将所需的对象设置为 NSApplication 上的属性。

这意味着在我的代码中的任何位置,我都可以通过 NSApp 来访问所需的对象,这当然是一个全局可用的 NSApplication 对象。

在我看来,这是最干净、最简单的方法。
戴夫


0
投票

尝试一下:

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
让我知道如果是这种情况,有一些方法可以做到这一点。

© www.soinside.com 2019 - 2024. All rights reserved.