如何在 SwiftUI 中汇总过滤的核心数据?
Posted
技术标签:
【中文标题】如何在 SwiftUI 中汇总过滤的核心数据?【英文标题】:How to sum filtered Core Data in SwiftUI? 【发布时间】:2020-01-10 12:16:25 【问题描述】:如何在 SwiftUI 应用中对过滤后的 Core Data 值求和(使用谓词)?
我有 1 个名为 NPTransaction 的实体。它有 5 个属性,我想对名为 value (Integer 64) 的属性的值求和。基本上,它将汇总所选月份的收入值(现在它在示例代码中设置为 current Date(),但稍后将设置为整个当前月份)。
我发现了关于 Swift 的类似问题,但我无法让这段代码在我的应用程序中运行。 相关问题链接:How to sum the numbers(Int16) of stored core data - Swift 3
根据链接中的代码,我现在有以下代码:
import SwiftUI
struct DashboardView: View
@Environment(\.managedObjectContext) var managedObjectContext
@State var selectedDate = Date()
// This code in private var below produces many errors like:
// Value of type 'AppDelegate' has no member 'managedObjectContext'
// Use of undeclared type 'NSEntityDescription'
// Use of undeclared type 'NSFetchRequest'
// - so I assume that this linked question may be outdated or I should take different approach using SwiftUI
private var income: Int64
let context = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
let entityDesc: NSEntityDescription = NSEntityDescription.entity(forEntityName: "NPTransaction", in: context)!
let request: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest()
request.entity = entityDesc
request.predicate = NSPredicate(format: "date == %@", selectedDate)
let records = try! context.fetch(request)
try! context.fetch(request) as! [NSManagedObject]
let monthlyIncome = result.reduce(0) $0 + ($1.value(forKey: "value") as? Int64 ?? 0)
return monthlyIncome
var body: some View
NavigationView
VStack
Text("Monthly income:")
Text("\(income) USD")
// more code...
我只是在学习 Swift 和 SwiftUI,所以也许有更好的完全不同的方法来解决这个问题?
【问题讨论】:
NSExpressionDescription 很有用。例如***.com/questions/49099914/… 请记住 Date() 包含时间,因此您的谓词不太可能匹配任何对象。我将从删除谓词开始,以获得工作代码来计算所有值的总和。然后稍后再添加谓词 - 要获得“今天的总数”,您将需要相当于“日期 > 昨晚午夜和日期 谢谢,pbasdf,好点子。不过,我已经解决了这个日期谓词。我也欢迎仅针对 date > Date() 的解决方案。我仍然无法解决的问题是如何对数据求和。 Gagan_ios,谢谢,但您的链接中的问题没有得到解答。我不知道如何正确使用 NSExpressionDescription,也找不到有效的 SwiftUI 示例 【参考方案1】:我在这个问题中发现了类似的问题: Sum of (double) fetch request field 我稍微修改了代码并提出了这个解决方案:
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)
【讨论】:
【参考方案2】:这应该修复您当前的代码,您正在尝试减少名为 result
的不存在变量,我相信您是从链接的代码中获得的。您还进行了两次不必要的提取。希望这会有所帮助。
request.predicate = NSPredicate(format: "date == %@", selectedDate)
let records = try! context.fetch(request) as! [NSManagedObject]
let monthlyIncome = records.reduce(0) $0 + ($1.value(forKey: "value") as? Int64 ?? 0)
return monthlyIncome
此外,您可以通过 NSFetchRequest 和 NSExpression 对 CoreData 中的值求和。这是我在本文中找到的一个示例:Group by, Count and Sum in CoreData。
let keypathExp1 = NSExpression(forKeyPath: "value")
let expression = NSExpression(forFunction: "sum:", arguments: [keypathExp1])
let sumDesc = NSExpressionDescription()
sumDesc.expression = expression
sumDesc.name = "sum"
sumDesc.expressionResultType = .integer64AttributeType
let request = NSFetchRequest(entityName: "NPTransaction")
request.returnsObjectsAsFaults = false
request.propertiesToFetch = [sumDesc]
request.resultType = .dictionaryResultType
let monthlyIncome = try! context.fetch(request) as? Int64
if let monthlyIncome = monthlyIncome
print("monthly income total: \(monthlyIncome)")
编辑:我用您的实体和列名修改了示例代码。
【讨论】:
谢谢,clawesome,但我仍然遇到很多错误。使用您的第一个代码,我得到Use of unresolved identifier 'request'
。或Value of type 'AppDelegate' has no member 'managedObjectContext'
或Use of undeclared type 'NSFetchRequest'
。使用第二个代码时,我得到了Use of unresolved identifier 'NSExpressionDescription'
和Use of unresolved identifier 'NSFetchRequest'
之类的东西。我想我在这里混合了太多代码。您的链接中的这个示例是否可以与 SwiftUI 一起使用?也许我将此代码放在文件的错误部分?
例如在线let sumDesc = NSExpressionDescription()
我收到一个错误:Use of unresolved identifier 'NSExpressionDescription'
。我不知道该怎么办:/
好的,我已经修复了一些错误,只需在文件开头添加import CoreData
。但仍然在线let monthlyIncome = try! context.fetch(request) as? Int64
我收到Use of unresolved identifier 'context'
。如果我尝试添加代码let context = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
,我会收到错误:Value of type 'AppDelegate' has no member 'managedObjectContext'
。即使我在视图结构的开头有一行@Environment(\.managedObjectContext) var managedObjectContext
(这适用于我使用核心数据的其他代码)。
抱歉,我假设您拥有的代码引用(例如let context
)已经在工作。如果您收到Value of type 'AppDelegate' has no member 'managedObjectContext'
,那么您的应用程序委托很可能被重命名为AppDelegate
以外的其他名称,或者它没有managedObjectContext
变量,这可能是因为使用了SwiftUI。看起来你已经解决了这个问题。
谢谢,棒棒哒。 AppDelegate 具有此名称。我不知道是什么问题。我用另一种方式解决了这个问题。我不知道是否正确,但它有效。非常感谢你试图帮助我。万事如意!以上是关于如何在 SwiftUI 中汇总过滤的核心数据?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:如何将 CoreData 与动态过滤器相加?
如何从 SwiftUI 的详细信息视图(编辑项目视图)中删除核心数据条目?