SwiftUI:如何用动态过滤器对 CoreData 求和? [英] SwiftUI: How to sum CoreData with dynamic filters?

查看:44
本文介绍了SwiftUI:如何用动态过滤器对 CoreData 求和?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在另一个问题中,我发布了一个带有过滤器的 CoreData 摘要的解决方案.问题是:我无法使这些过滤器动态化.

In another question I have posted a solution for summary of CoreData with filters. The problem is: I cannot make those filters dynamic.

我有一个 CoreData 实体:NPTransaction,其属性为:日期(日期)值(整数 64).

I have a CoreData entity: NPTransaction with attributes: date (Date) and value (Integer 64).

使用静态过滤器求和的工作代码:

Working code for sum with static filter:

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 变量:

Now if I try to add selectedDate variable like this:

// (...)

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' 可用之前运行

Cannot use instance member 'selectedDate' within property initializer; property initializers run before 'self' is available

所以我尝试在代码中稍后将 FetchRequest 移动到 init ,如下所示:

So I have tried to move FetchRequest to init later in the code like this:

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:

But now I am getting 2 errors. One on the line with reduce:

'FetchRequest' 类型的值没有成员 'reduce'

Value of type 'FetchRequest' has no member 'reduce'

还有一个带有 Text("(sum)") :

And one on the line with Text("(sum)") :

使用未解析的标识符sum"

Use of unresolved identifier 'sum'

这个问题怎么解决?如何将 CoreData FetchRequest 与用户可以更改的动态过滤器相加?

解决方案根据 Asperi 和 Aspid 的回答,以下是使动态过滤器处理 CoreData 值总和的最终代码:

SOLUTION Based on answers from Asperi and Aspid, here is the final code that made dynamic filters work with sum of CoreData values:

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)
    }
}

推荐答案

如果要在 Init 中执行 fetch,首先需要获取上下文.@Environment(\.managedObjectContext) var managedObjectContext 对你没有帮助,因为它还没有准备好.@FetchRequest 是一个包装器,允许您在需要结果时进行提取,但在您定义它时不会执行提取.这段代码对我有用,但我不确定以这种方式获取上下文是否真的正确并且在所有情况下都有效:

If you want to perform fetch in Init, you need to get context first. @Environment(\.managedObjectContext) var managedObjectContext does not help you, becouse its not ready yet. @FetchRequest is a wrapper that allows you to do fatches when you need result, but it doesnt perferm fetch when you define it. This code works for me, but im not realy sure whether getting context this way is realy correct and works good in all the cases:

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 视图,并通过 init 传递 date 变量,其中使用谓词.当您更改 @State 日期时,subView 将再次初始化并且 fetch 将使用新日期执行.那看起来好多了.

But there is an alternative. You can extract Sum view in subView, and pass date variable through init, where you use predicate. When you change @State date, subView will initialize again and fetch will perform with new date. That looks much better.

这篇关于SwiftUI:如何用动态过滤器对 CoreData 求和?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆