SwiftUI:如何将 CoreData 与动态过滤器相加?
Posted
技术标签:
【中文标题】SwiftUI:如何将 CoreData 与动态过滤器相加?【英文标题】:SwiftUI: How to sum CoreData with dynamic filters? 【发布时间】:2020-01-13 12:09:13 【问题描述】:在另一个问题中,我发布了一个使用过滤器汇总 CoreData 的解决方案。 问题是:我无法使这些过滤器动态化。
我有一个 CoreData 实体:NPTransaction,其属性:date (Date) 和 value (Integer 64)。
使用静态过滤器求和的工作代码:
import SwiftUI
import CoreData
struct DashboardView: View
@Environment(\.managedObjectContext) var managedObjectContext
// FetchRequest with predicate set to "after now"
@FetchRequest(entity: NPTransaction.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \NPTransaction.value, ascending: false)], predicate: NSPredicate(format: "date > %@", Date() as NSDate)) var fetchRequest: FetchedResults<NPTransaction>
// sum results using reduce
var sum: Int64
fetchRequest.reduce(0) $0 + $1.value
var body: some View
NavigationView
VStack(alignment: .leading)
HStack
VStack(alignment: .leading)
Text("sum")
Text("\(sum)")
.font(.largeTitle)
Spacer()
.padding()
Spacer()
.navigationBarTitle("Title")
struct DashboardView_Previews: PreviewProvider
static var previews: some View
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return DashboardView().environment(\.managedObjectContext, context)
现在,如果我尝试像这样添加 selectedDate 变量:
// (...)
struct DashboardView: View
@Environment(\.managedObjectContext) var managedObjectContext
@State var selectedDate = Date()
@FetchRequest(entity: NPTransaction.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \NPTransaction.value, ascending: false)], predicate: NSPredicate(format: "date > %@", selectedDate as NSDate)) var fetchRequest: FetchedResults<NPTransaction>
// (...)
我收到一个错误:
不能在属性初始化器中使用实例成员“selectedDate”; 属性初始化器在“self”可用之前运行
所以我尝试将 FetchRequest 移动到稍后在代码中进行初始化,如下所示:
import SwiftUI
import CoreData
struct DashboardView: View
@Environment(\.managedObjectContext) var managedObjectContext
@State var selectedDate = Date()
var fetchRequest: FetchRequest<NPTransaction>
var body: some View
NavigationView
VStack(alignment: .leading)
HStack
VStack(alignment: .leading)
Text("sum")
Text("\(sum)")
.font(.largeTitle)
Spacer()
.padding()
Spacer()
.navigationBarTitle("Title")
init()
fetchRequest = FetchRequest<NPTransaction>(entity: NPTransaction.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \NPTransaction.value, ascending: false)
], predicate: NSPredicate(format: "date >= %@", Date() as NSDate))
var sum: Int64
fetchRequest.reduce(0) $0 + $1.value
但现在我收到 2 个错误。一个与reduce一致:
“FetchRequest”类型的值没有成员“reduce”
还有一个与 Text("(sum)") :
使用未解析的标识符“sum”
如何解决这个问题?如何将 CoreData FetchRequest 与用户可以更改的动态过滤器相加?
解决方案 根据 Asperi 和 Aspid 的回答,这里是使动态过滤器与 CoreData 值的总和一起工作的最终代码:
DashboardView.swift
import SwiftUI
struct DashboardView: View
@State var selectedDate = Date()
var body: some View
NavigationView
VStack(alignment: .leading)
// selectedDate change is controlled in another DateView
DateView(selectedDate: $selectedDate)
// selectedDate is passed to init in DashboardViewSum
DashboardViewSum(selectedDate: selectedDate)
Spacer()
.navigationBarTitle("Title")
struct DashboardView_Previews: PreviewProvider
static var previews: some View
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return DashboardView().environment(\.managedObjectContext, context)
DashboardViewSum.swift
import SwiftUI
struct DashboardViewSum: View
@Environment(\.managedObjectContext) var managedObjectContext
var fetchRequest: FetchRequest<NPTransaction>
var sum: Int64
fetchRequest.wrappedValue.reduce(0) $0 + $1.value
var body: some View
HStack
VStack(alignment: .leading)
Text("sum")
Text("\(sum)")
.font(.largeTitle)
Spacer()
.padding()
// selectedDate is passed from DashboardView and makes predicate dynamic. Each time it is changed, DashboardViewSum is reinitialized
init(selectedDate: Date)
fetchRequest = FetchRequest<NPTransaction>(entity: NPTransaction.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \NPTransaction.value, ascending: false)
], predicate: NSPredicate(format: "date >= %@", selectedDate as NSDate))
struct DashboardViewSum_Previews: PreviewProvider
static var previews: some View
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return DashboardViewSum(selectedDate: Date()).environment(\.managedObjectContext, context)
【问题讨论】:
【参考方案1】:如果要在 Init 中执行 fetch,则需要先获取上下文。
@Environment(\.managedObjectContext) var managedObjectContext
对你没有帮助,因为它还没有准备好。
@FetchRequest
是一个包装器,它允许您在需要结果时进行填充,但在您定义它时它不会执行获取。
这段代码对我有用,但我不确定以这种方式获取上下文是否真的正确并且在所有情况下都很好用:
init()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContaner.viewContext
let fetchRequest: NSFetchRequest<SomeType> = SomeType.fetchRequest
//... some order and filter
if let result = try? context.fetch(fetchRequest)
//result is [someClass] - do what you need
但是还有一个选择。
您可以在 subView 中提取 Sum 视图,并将 date
变量传递给您使用谓词的init
。当您更改 @State
日期时,subView 将再次初始化,并且 fetch 将以新日期执行。这样看起来好多了。
【讨论】:
非常感谢您,阿斯皮德!我无法让你的代码工作,但你的替代想法帮助我解决了这个问题。杰出的!我已将您的想法与 Asperi 对我的代码的修复一起使用,现在它可以工作了:) 我已经编辑了我的问题,以根据您和 Asperi 的答案提供有效的解决方案。谢谢!【参考方案2】:我认为它的目的是将sum
设为可计算的DashboardView
属性(在init
之外),作为
var sum: Int64
fetchRequest.wrappedValue.reduce(0) $0 + $1.value
...
init()
...
【讨论】:
非常感谢您,Asperi!你的代码帮助我摆脱了错误,让我离解决方案更近了一步。 Aspid 的想法与您的修复相结合,使其发挥了作用。我已经编辑了我的问题,以根据您和 Aspid 的回答提供可行的解决方案。谢谢!以上是关于SwiftUI:如何将 CoreData 与动态过滤器相加?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 SwiftUI 中的颜色类型存储到 CoreData 中?
保存数据时出现问题,将 CoreData 与 SwiftUI 一起使用
如何在 SwiftUI 中从 FetchedResults (CoreData) 中获取 NSOrderedSet