How to sum filtered Core Data in SwiftUI?

点点圈 提交于 2020-06-16 18:35:29

问题


How to sum filtered Core Data values (using predicate) in SwiftUI app?

I have 1 entity named NPTransaction. It has 5 attributes and I would like to sum values from the attribute named value (Integer 64). Basically, it would sum up values of income in selected month (right now it's set to current Date() in the example code, but later on it will be set to whole current month).

I have found similar question regarding Swift, but I cannot make this code to work in my app. Link to related question: How to sum the numbers(Int16) of stored core data - Swift 3

Based on code from the link, I have following code right now:

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...

I am just learning Swift and SwiftUI, so maybe there is a better and completely different way of solving this problem?


回答1:


I have found similar problem in this question: Sum of (double) fetch request field I have modified the code a little and came up with this solution:

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:


This should fix your current code, you're trying to reduce non existent variable called result which I believe you got from the code you linked. You're also making two fetches which shouldn't be necessary. Hopefully this helps.

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

Also, you can sum values from CoreData via an NSFetchRequest and NSExpression. Here's an example I found in this article: 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)")
}

Edit: I amended the sample code with your entity and column name.



来源:https://stackoverflow.com/questions/59681508/how-to-sum-filtered-core-data-in-swiftui

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!