Search code examples
swiftcore-datamvvmswiftuinsfetchrequest

How do I correctly insert a sort descriptor for this SwiftUI CoreData model?


I'm trying to implement an MVVM architecture as described here:

https://medium.com/better-programming/swiftui-and-coredata-the-mvvm-way-ab9847cbff0f

But I'm getting a run-time error in this file and cannot figure out where to add the sort descriptor:

import Foundation
import CoreData
import Combine
import UIKit

class ItemsStorage: NSObject, ObservableObject {
    var items = CurrentValueSubject<[Item], Never>([])
    private let itemFetchController: NSFetchedResultsController<Item>
    
    //SINGLETON INSTANCE
    static let shared: ItemsStorage = ItemsStorage()  ///Run time error: Thread 1: "An instance of NSFetchedResultsController requires a fetch request with sort descriptors"
    
    private override init() {
        
        /// This line doesn't solve the issue
        let sort = NSSortDescriptor(key: "name_", ascending: true)
        
        itemFetchController = NSFetchedResultsController(
        
        fetchRequest: Item.fetchRequest(),
        managedObjectContext: PersistenceController.shared.container.viewContext,
        sectionNameKeyPath: nil, cacheName: nil
        )
        
        /// This line doesn't solve the issue
        itemFetchController.fetchRequest.sortDescriptors = [sort]
        
        super.init()
        
        itemFetchController.delegate = self
        
        do {
            try itemFetchController.performFetch()
            items.value = itemFetchController.fetchedObjects ?? []
        } catch {
            NSLog("Error: could not fetch objects")
        }
    }
    
    func add(){
        
    }
    
    func update(withID id: UUID) {
        
    }
    
    func delete(id: UUID) {
        
    }
}

extension ItemsStorage: NSFetchedResultsControllerDelegate  {
    public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        guard let items = controller.fetchedObjects as? [Item] else { return }
        NSLog("Context has changed, reloading items ...")
        self.items.value = items
    }
}

Solution

  • You need to set the sort descriptor for the fetchRequest, before initialising the fetched results controller:

        let sort = NSSortDescriptor(key: "name_", ascending: true)
        let itemFetchRequest = Item.fetchRequest()
        itemFetchRequest.sortDescriptors = [sort]
        
        itemFetchController = NSFetchedResultsController(
        
        fetchRequest: itemFetchRequest,
        managedObjectContext: PersistenceController.shared.container.viewContext,
        sectionNameKeyPath: nil, cacheName: nil
        )