UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to share data between views with CoreData

Forums > SwiftUI

When the main view code gets large, it's convenient to create a new view file to work with. But I can't figure out how to transfer data from the main view to another child file (view)

ContentView View

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Nest.nestName, ascending: true),],
        animation: .default)
    var nests: FetchedResults<Nest>

    @FetchRequest(entity: Bird.entity(), sortDescriptors: []) var birds: FetchedResults<Bird>

    var body: some View {
        NavigationView {
            List {
                //  MARK: The code below is replaced with the NestSection() view
                Section {
                    ForEach(nests, id: \.self) { nest in
                        NavigationLink {
                            //  MARK: The code below is replaced with the NestBirdsList() view
                            List {
                                ForEach(nest.birdArray,id: \.self) { bird in
                                    Text(bird.wrappedBirdName)
                                }
                            }
                            .navigationTitle(nest.wrappedNestName)
                            .navigationBarTitleDisplayMode(.inline)
                            .toolbar {
                                ToolbarItem(placement: .primaryAction) {
                                    Button {
                                        self.presentingNewBirdSheet.toggle()
                                    } label: {
                                        Label("Add new bird", systemImage: "plus")
                                    }
                                }
                            }
                        } label: {
                            Text(nest.wrappedNestName)
                        }
                    }
                }
            }
        }
    }
}

NestsSection View

import SwiftUI

struct NestsSection: View {
    var body: some View {
        Section {
            ForEach(nests, id: \.self) { nest in
                NavigationLink {
                    NestBirdsList()
                } label: {
                    Text(nest.wrappedNestName)
                }
            }
        } header: {
            Text("Nests")
        } footer: {
            Text("\(birds.count) birds in \(nests.count) nests")
        }
    }
}

struct NestsSection_Previews: PreviewProvider {
    static var previews: some View {
        NestsSection()
    }
}

Errors in NestsSection View: Cannot find 'nests' in scope Cannot find 'birds' in scope

Is it possible to get this data from ContentView View ?

2      

@Bnerd  

Move your FetchRequests in the NestSection View?

3      

Add a @StateObject property to the child view, NestsSection, for the data you want to pass to the child view. @StateObject is how you pass data to views with Core Data.

In your navigation link, set the destination to the child view, and pass the value from the parent view to the child view.

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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

Thank you guys. Had to use both answers and they worked for me.

I dubbed @FetchRequest and FetchedResults in NestsSection: View and NestBirdsList: View from ContentView: View And add @StateObject var nest = Nest() in in these views

That is, it means that always need to duplicate @FetchRequest from main View?

For some reason, it seems to me that this is not quite the right solution (albeit a working one). I think I should use @EnvironmentObject and the environmentObject() modifier somewhere.

And in general, it's probably better to use the ViewModel in such projects?

2      

Just a small note ForEach(nests, id: \.self). You don't need to use id here as CoreData makes nest identifiable by default, so you can easily delete it from here. As for creating of @FetchRequest I do not think it is a big deal to call it whenever you need to:

“A fetch request loads all the objects available in the Persistent Store. This is not a problem when the number of objects is not significant. But a Persistent Store can manage thousands of objects, which can consume resources that the app and the system need to run. Therefore, instead of the fetch request, the @FetchRequest property wrapper produces a value of type FetchedResults. This is a structure that takes care of loading into the context only the objects that are required by the view at any given moment”

Excerpt From SwiftUI for Masterminds 3rd Edition 2022 J.D. Gauchat

So @FetchRequest is not expensive operation as it will request only those items needed by view at given moment.

3      

Thanks @ygeras for the advice and explanation.

And by the way, today I discovered that my method to share the view, although it turned out to be working, shows an error in the console, even though the app works.:

CoreData: error: Failed to call designated initializer on NSManagedObject class 'Nest'

If to use only ContentView that this error is not present.

2      

@Bnerd  

Your error is possibly because of your stateObject

@StateObject var nest = Nest()

"That is, it means that always need to duplicate @FetchRequest from main View?" No, you can keep the fetchrequest in the contentView and pass the data to the child with @StateObject like below

@StateObject var nest: Nest  // in your previous solution you were initializing constantly a new instance, thus the error.

Then in your contentView you will pass the info like this:

NestSection(nest: nests) // nests is the results that will be passed to your child view.

This is I guess what @SwiftDevJournal meant.

2      

Yes, it looks like the error was due to

@StateObject var nest = Nest()

I replaced it with

@EnvironmentObject var nest: Nest

An and the error is no longer shown in the console. At the same time, this was required only in NestBirdsList: View, in NestsSection: View I did not specify anything at all, only @FetchRequest and everything seems to work.

About @FetchRequest only in ContentView: View:

I added @StateObject var nest: Nest in NestsSection: View and NestsSection(nest: nests) in ContentView: View

Error:

NestsSection(nest: nests) - Cannot convert value of type 'FetchedResults<Nest>' to expected argument type 'Nest'

2      

@Bnerd  

Try like this:

@ObservedObject var nest: FetchedResults<Nest>.Element

2      

@ObservedObject var nest: FetchedResults<Nest>.Element

It's in ContentView instead of

var nests: FetchedResults<Nest>

?

This causes even more errors. Perhaps I added something superfluous somewhere or not there in the code of the entire application.

But at least I was able to make the transition from ContentView to child views. Although @FetchRequest is required in every view (and this is probably wrong).

2      

@Bnerd  

@ObservedObject var nest: FetchedResults<Nest>.Element

Only in the child view, in order to solve your previous error in the ContentView

2      

This also does not fix the error: Cannot convert value of type 'FetchedResults<Nest>' to expected argument type 'FetchedResults<Nest>.Element' (aka 'Nest')

In general, everything works so far if just use NestsSection() in ContentView but need @FetchRequest in NestsSection View

2      

@Bnerd  

Share your code to have a look how you placed the variables.

2      

Here, for example, Paul gets the data about the book without resorting to @FetchRequest, but uses let book: Book, but let nest : Nest does not work for my structure

2      

@Bnerd  

Hmm..try this in the NestSection View

@State var nest: FetchedResults<Nest>

Paul is calling one book, you call the list of "books".

2      

If used @State var nest: FetchedResults<Nest> error Cannot find 'nests' in scope. If replace nest >>> nests and used NestsSection(nests: nests) it work, but crashed my delete func :)

And there was an idea not to use at all @FetchRequest and FetchedResults in child View, and "pull" data about the request and the result from the parent view. Or is it not possible...

2      

It seems to work like this without @FetchRequest:

NestsSection(nests: nests) - in ContentView

//  @FetchRequest(sortDescriptors: [SortDescriptor(\.nestName, order: .forward)])
    var nests: FetchedResults<Nest> /// in NestsSection

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.