简体   繁体   中英

How do I use a NSFetchRequest with an NSExpression and .dictionaryResultType as a SwiftUI @FetchRequest?

I have a model with two entities: Category and Transaction (simplified)

Category
--------
title: String
transactions: Transaction[]

Transaction
-----------
title: String
amount: Double
date: Date
category: Category

They are one-to-many so one category can hold multiple transactions. I am trying to sum up the total value of all Transactions within a category and within a given date frame. For example, I would like to know all transactions from march 2022 in the category "Food". I have come up with this (currently only fetching from the past 30 days)

  let sumExpression = NSExpressionDescription()
  let keypathAmount = NSExpression(forKeyPath: "amount")
  sumExpression.expression = NSExpression(forFunction: "sum:", arguments: [keypathAmount])
  sumExpression.name = "sum"
  sumExpression.expressionResultType = .doubleAttributeType
  
  let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Transaction")
  let adjustedDate = Calendar.current.date(byAdding: .day, value: -30, to: Date()) ?? Date()
  let datePredicate = NSPredicate(format: "date > %@", adjustedDate as CVarArg)
  request.predicate = NSCompoundPredicate(
    type: .and,
    subpredicates: [datePredicate, NSPredicate(format: "category == %@", category)]
  )
  request.propertiesToFetch = [sumExpression]
  request.includesPendingChanges = false 
  request.returnsObjectsAsFaults = false
  request.resultType = .dictionaryResultType
  request.returnsDistinctResults = true

  do {
    let results = try moc.fetch(request)
    let resultMap = results[0] as! [String:Double]
    return resultMap["sum"] ?? 0.0
  } catch let error as NSError {
    NSLog("Error when summing amounts: \(error.localizedDescription)")
  }

Since I use the @Environment(\.managedObjectContext) var moc I can't use the fetch request in the init() since the environment is not available yet. So right now I'm doing all this in the onAppear() of the view body. In said view, I have an @ObservedObject of the currently selected Category . This of course does not update when the transactions change (for example when a new one gets added). I can not use a @FetchRequest and initialize it with the above NSFetchRequest because Xcode throws

'NSFetchedResultsController does not support both change tracking and fetch request's with NSDictionaryResultType'

How do I circumvent this? What is the best way to fetch the sum of a filtered relationships attribute in SwiftUI? How do I leverage the existence of the @ObservedObject category to retry the fetch request when a change is occurring?

I solved this with a Singleton that gets initialized in the app root and injected into each view that needs it as a @StateObject. The Singleton gets access to the NSManagedObjectContext via a one-time setup method. It uses the NotificationCenter via NSManagedObjectContext.didSaveObjectsNotification to listen to model changes and recalculate. While a Singleton isn't really popular to use in SwiftUI (often for good reason,) this one does not affect the underlying data. it just enriches and presents it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM