TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Thoughts about proper way to share FetchedResult between Views + Problem

Forums > SwiftUI

Good day together,

iam pretty new at swift and also in this forum. First I say what I intend to do and then I show what I have done.

After a FetchedRequest has been made, I would like to make the data records available for all views. I thought it would be best to create an EnvironmentObject that holds the data before passing it to each subview.

Intermediate question: Is this a good approach? Or is there a better way.

As I said, I chose the approach with the EnvironmentObject.

Here is the code:

import SwiftUI
import CoreData

@main
struct TestApp: App {

    @StateObject var persistenceManager = PersistenceManager()
    @StateObject var animalManager = AnimalManager()

    var body: some Scene {
        WindowGroup {
            MainMenu().environmentObject(animalManager)
        }
    }
}
import SwiftUI
import CoreData

struct MainMenu: View {

    @State private var showAnimalDetails: Bool = false
    @Environment(\.managedObjectContext) var moc

    var body: some View {
        NavigationView {
            VStack {
                ListView()
                SlideView()
            }           
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        MainMenu()
    }
}
class AnimalManager: ObservableObject {

    @FetchRequest(entity: MyAnimal.entity(), sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)])
     var listOfAnimals: FetchedResults<MyAnimal>    
}

In the Codeblock below raise the Warning message:

Accessing StateObject's object without being installed on a View. This will create a new instance each time.

struct ListView: View {

    @EnvironmentObject var animalManager: AnimalManager

    var body: some View {
        List(animalManager.listOfAnimals){ animal in
            Text(animal.name!)
        }
    }
}

I struggle on this the whole day. iam not sure i follow the right path. Of course I googled it. I found that it might be because I have to execute the fetch result in a view. The solution approach was that I also pass it to the window group. The answer was marked as "correct", but it still doesn't work.

Does anyone have an idea or can tell me what I should do?

Many thanks in advance and a happy new year

2      

Hi! In this case you cannot use @FetchRequest in the class AnimalManager. It will not work that way.

I found that it might be because I have to execute the fetch result in a view.

That is true so you have to refactor your class similar to this. This example passes in moc because you might pass in a different NSManagedObjectContext when previewing your SwiftUI vew. If this is not important to you then you can also just initialize a moc in the initializer and not pass it in.

class AnimalManager: ObservableObject {
    let moc: NSManagedObjectContext
    @Published var listOfAnimals: [AnimalEntity] = []

    init(moc: NSManagedObjectContext) {
      self.moc = moc
    }

    func fetch() {
    // Every entity has a built-in fetchRequest function that returns a fetch request object for you
      let request = AnimalEntity.fetchRequest()

      request .sortDescriptors = [NSSortDescriptor(keyPath: \AnimalEntity.name, ascending: true)]

      if let listOfAnimals = try? moc.fetch(request) {
        self.listOfAnimals = listOfAnimals
    }
}

Then you can use it something like this.

@main
struct TestApp: App {

    let moc = PersistantManager().persistentContainer.viewContext

    var body: some Scene {
        WindowGroup {
            MainMenu(listOfAnimals: AnimalManager(moc: moc))

            // for injection 
              .environmentObject(AnimalManager(moc: moc))
        }
    }
}
struct MainMenu: View {

  @StateObject var listOfAnimals: AnimalManager

    var body: some View {
        NavigationView {
            VStack {
                ListView()
                SlideView()
            }           
        }
        .task {
          listOfAnimals.fetch()
        }
    }
}

or you can inject AnimalManager to the environment and access it from there as well.

3      

@MrSwifty lets us know he's new to Swift:

i am pretty new at swift and also in this forum

Then asks about CoreData

After a FetchedRequest has been made, I would like to make the data records available for all views.
I thought it would be best to create an EnvironmentObject that holds the data before passing it to each subview.

And asks for feedback:

Is this a good approach? Or is there a better way?

SwiftData vs CoreData

Yes, there is a better way. It's not to use CoreData. Instead, since you're new to Swift and SwiftUI, you'll probably find it much easier to learn SwiftData instead.

SwiftData allows you to "make the data records available for all views" in a more Swifty fashion. SwiftData uses @Query instead of FetchRequests. There are several other subtle improvements too. And you are in for a real treat! This excellent web site has updated its many tutorials on CoreData and now teaches how to implement SwiftData, instead.
Sweet corndogs, what a deal!

Keep Coding!

Highly recommend you try out the SwiftData tutorials on this site, then return here and share your success stories!

See -> Fun with SwiftData

3      

Thanks to you both.

And a special thanks to Obelix. I like SwiftData. :)

Everything works immediately. And most importantly, it works the way I want it to.

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.