Search code examples
xcodeswiftuicore-datawatchosnsfetchrequest

My SwiftUI FetchRequest display no items although there is 10 items


This is what I have in code:

struct ServicesView: View {
    @FetchRequest(entity: Category.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Category.name, ascending: false)], animation: .easeIn) var results: FetchedResults<Category>

    var body: some View {
        List(results) { category in
            VStack(alignment: .leading, spacing: 3, content: {
                Text(category.name)
                    .font(.system(size: 12))
                    .foregroundColor(.white)
                Text("x\(category.services.count)")
            })
        }
        .overlay(
            Text(results.isEmpty ? "Nothing here" : "")
        )
        .onAppear {
            print("😋")
            print(Category.numberOfEntities())
        }
    }
}

On console is displayed:

😋

10

Why there is nothing on the list?

enter image description here

How number is calculated?

extension NSManagedObjectContext {
    static func defaultContext() -> NSManagedObjectContext {
        CoreDataManager.shared.defaultContext!
    }
}


protocol Fetchable {
    associatedtype FetchableType: NSManagedObject = Self
    static func numberOfEntities() -> Int
}

extension Fetchable where Self: NSManagedObject {
    static var entityName: String {
        entity().name ?? ""
    }
    static func numberOfEntities() -> Int {
        let request = NSFetchRequest<FetchableType>(entityName: entityName)
        let context = NSManagedObjectContext.defaultContext()
        do {
            return try context.count(for: request)
        } catch let error as NSError {
            print("❌ Core Data Count Error \(error), \(error.userInfo)")
            return 0
        }
    }
}

I suppose the issue is here, because I don't assign @Environment’s managedObjectContext anywhere. How should I do it?

@main
struct Watch_AppApp: App {
    var body: some Scene {
        WindowGroup {
                TabView {
                    TimerView()
                    ContentView()
                    ServicesView()  // how do I assign here managed object context?
                    Text("4th page")
                }
                .tabViewStyle(PageTabViewStyle())
                .onAppear(perform: loadData) //load data, fetch categories from cloudkit and save to core data, is is fetched and saved
            }
    }
}

Solution

  • First of all you have to add the ManagedObjectContext to your view from which you are trying to fetch the data from.

    You can add it like this to your view:

    struct ServicesView: View {
        // The context for your view
        @Environment(\.managedObjectContext) var viewContext
    
       @FetchRequest(entity: Category.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Category.name, ascending: false)], animation: .easeIn) var results: FetchedResults<Category>
    
        var body: some View {
            List(results) { category in
                VStack(alignment: .leading, spacing: 3, content: {
                    Text(category.name)
                        .font(.system(size: 12))
                        .foregroundColor(.white)
                    Text("x\(category.services.count)")
                })
            }
            .overlay(
                Text(results.isEmpty ? "Nothing here" : "")
            )
            .onAppear {
               print("😋")
               print(Category.numberOfEntities())
            }
        }
    }
    

    To pass the context from your entry view you have to get the context from your PersistenceController, like so:

    @main
    struct Watch_AppApp: App {
        var body: some Scene {
            WindowGroup {
                TabView {
                    TimerView()
                    ContentView()
                    ServicesView()  
                        // Assign the context
                        .environment(\.managedObjectContext, PersistenceController.shared.container.context)
                    Text("4th page")
                }
                .tabViewStyle(PageTabViewStyle())
                .onAppear(perform: loadData) //load data, fetch categories from cloudkit and save to core data, is is fetched and saved
            }
        }
    }
    

    You can find the documentation of Core Data here: https://developer.apple.com/documentation/coredata