SwiftUI: Как суммировать CoreData с динамическими c фильтрами? - PullRequest
0 голосов
/ 13 января 2020

В другом вопросе я разместил решение для сводки CoreData с фильтрами. Проблема в том, что я не могу сделать эти фильтры динамическими c.

У меня есть сущность CoreData: NPTransaction с атрибутами: date (Date) и значение (целое число 64) .

Рабочий код для суммы со стати c фильтр:

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 в свойстве initializer; инициализаторы свойств запускаются до того, как станет доступным '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 ошибки , Одно в строке с уменьшением:

Значение типа 'FetchRequest' не имеет члена 'уменьшить'

И одно в строке с текстом ("(сумма)" ):

Использование неразрешенного идентификатора 'сумма'

Как решить эту проблему? Как суммировать CoreData FetchRequest с динамическими c фильтрами, которые могут изменить пользователи?


РЕШЕНИЕ Основываясь на ответах Аспери и Аспида, вот окончательный код, который сделал фильтры Dynami c работают с суммой значений 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)
    }
}

Ответы [ 2 ]

1 голос
/ 13 января 2020

Я предполагаю, что это должно было сделать sum как вычисляемое DashboardView свойство (из init), как

var sum: Int64 {
    fetchRequest.wrappedValue.reduce(0) { $0 + $1.value }
}

...
init() {
   ...
}
1 голос
/ 13 января 2020

Если вы хотите выполнить выборку в Init, вам сначала нужно получить контекст. @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
    }
}

Но есть альтернатива. Вы можете извлечь представление Sum в subView и передать переменную date через init, где вы используете предикат. Когда вы изменяете @State дату, subView снова инициализируется и выборка будет выполняться с новой датой. Это выглядит намного лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...