Search code examples
macoscore-dataswiftuiswift5

SwiftUI connect CoreDate to settings window (macOS)


I recently added a settings pane to my app, and tried to connect it to core data to let the users mange their api and url settings.
But unfortunately when I launch my app and open the settings pane I alway get a bunch of errors telling me that core data is not connected correctly or something like that. - error messages below
-----------------------------------------

This is my App.swift file where I added the settings pane.

@main
struct RandomAppName: App {
    
    let persistenceController = PersistenceController.shared
    @State private var filter = 1
    
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
         
        }
       .commands { // some command features }
                
        
        
        #if os(macOS)
        
                Settings {
                    SettingsPane()
                        .environment(\.managedObjectContext, persistenceController.container.viewContext)
                     }
        
                #endif
        
        
        }
        }

The settings pane itself contains a tabview with serval subviews.
Here some code snippets.

import SwiftUI

struct SettingsPane: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    @State private var id : Int = 3

 var body: some View {

// some content stuff //

  if id == 1 || id == 0 {  ApiView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)  }

// some content stuff //

}
}

And some of the actual Api settings view

import SwiftUI

struct ApiView: View {

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \APIMaster.timestamp, ascending: true)],
        animation: .default)
    private var content: FetchedResults<APIMaster>

   var body: some View {

    // some content stuff //

        VStack{

          ScrollView{
          ForEach(content,  id: \.self) { content in

            HStack{
             Image(systemName: "xserve")
             Text(content.hostName)
             }

            }}

    // some content stuff //
 

} }


struct ApiView_Previews: PreviewProvider {
    static var previews: some View {
        ApiView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}


everything had been working fine until I implemented the core data part.
I already tested the ForEach loop with the core data connection in the app view and there it works perfectly fine.

The error message is:

2021-01-09 21:28:11.348566+0100 RandomAppName[71835:2714021] [error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'APIMaster' so +entity is unable to disambiguate. CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'APIMaster' so +entity is unable to disambiguate.
2021-01-09 21:28:11.348698+0100 RandomAppName[71835:2714021] [error] warning: 'APIMaster' (0x600003128000) from NSManagedObjectModel (0x600002530230) claims 'APIMaster'. CoreData: warning: 'APIMaster' (0x600003128000) from NSManagedObjectModel (0x600002530230) claims 'APIMaster'.

I hope somebody can help me with this specific problem.

Here is what the settings pane looks like. (I also tried the "main" entity)

enter image description here


Solution

  • I finally found a very easy way to solve my problem.
    The following code works perfect for me in Xcode 12.3.
    To access the PersistenceController.shared we need to connect our view to the "main" PersistenceController.shared, which is called in the App file.

    @main
    struct RandomAppName: App {
    
    let persistenceController = PersistenceController.shared
    @State private var filter = 1
    
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
         
        }
       .commands { // some command features }
                
        
        
        #if os(macOS)
        
                Settings {
                    SettingsPane(viewContext: persistenceController.container.viewContext)
                        .environment(\.managedObjectContext, persistenceController.container.viewContext)
                     }
        
                #endif
        
        
        }
        }
    

    In the settings window we ask for the Persistence like this:

    import SwiftUI
    
    struct SettingsPane: View {
        
       let viewContext : NSManagedObjectContext // ask for the persistence
    
        @State private var id : Int = 3
    
     var body: some View {
    
    // some content stuff //
    
      if id == 1 || id == 0 {  ApiView(viewContext: viewContext)  } // pass the persistence to the subview
    
    // some content stuff //
    
    }
    }
    

    And Finale in the subview:

    import SwiftUI
    
    struct ApiView: View {
    
     let viewContext : NSManagedObjectContext // ask for the persistence
    
        @FetchRequest(
            sortDescriptors: [NSSortDescriptor(keyPath: \APIMaster.timestamp, ascending: true)],
            animation: .default)
        private var content: FetchedResults<APIMaster>
    
       var body: some View {
    
        // some content stuff //
    
            VStack{
    
              ScrollView{
              ForEach(content,  id: \.self) { content in
    
                HStack{
                 Image(systemName: "xserve")
                 Text(content.hostName)
                 }
    
                }}
    
        // some content stuff //
     
    
    
    
      } }
        
       // I just uncomment the PreviewProvider because it's not necessary for me 
       // struct ApiView_Previews: PreviewProvider {
       //     static var previews: some View {
       //          ApiView()
       //     }
       //  }
    

    This works fine for me (I don't know if this is the official way)
    Hope I could help someone who has the same problem.