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

I can't get a button to work, instead I get this error: "Accessing StateObject's object without being installed on a View. This will create a new instance each time." Help?

Forums > SwiftUI

Hi Everyone,

I'm trying to get my SwiftUI project to work with Core Data, and to be refactored. I'm using property wrappers for its fetch request.

I keep getting this error in the log: "Accessing StateObject's object without being installed on a View. This will create a new instance each time."

As far as I know, it's something to do with having multiple @StateObject requests. Is this correct?

What am I doing wrong here?

I'll try and add my relevant code below. Any help here is greatly appreciated. Alternatively I can send you a link to my GH .

my refactoring\modelview file

import SwiftUI

class RefactorAccounts: ObservableObject {
    //Fetch request for pulling from persistent store.
    @Environment(\.managedObjectContext) var viewContext
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \AccountBackEnd.accountCompanyName, ascending: true)],
        animation: .default)
    
var accounts: FetchedResults<AccountBackEnd>

    //Takes object from @StateObject, presents as var
    @StateObject var accountBackEnd = AccountBackEnd()

    //Calls to Class for published objects
    @StateObject var publishedClasses = 
PublishedClassAccount()

    //Used for button/nav calls out
    @Environment(\.presentationMode) var presentationMode

    //Add account Function
    func addAccount() {

        withAnimation {
            //Adds to persistence store
            let newAccount = AccountBackEnd(context: viewContext)
            newAccount.accountCompanyName = publishedClasses.accountNameForCoreData
            PersistenceController.shared.save
            {
                error in
                if let error = error {
                    print(error.localizedDescription)
                    return
                }
                print("Successfully saved account.")
            }
           self.presentationMode.wrappedValue.dismiss()
            publishedClasses.accountNameForCoreData = ""
        }
    }
}

The file that displays clickable data

 
import SwiftUI

class RefactorAccounts: ObservableObject {

 //Refactoring
    @StateObject var refAccouunts = RefactorAccounts()
    //Used for button/nav calls out
    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        VStack {
            HStack {
                Spacer()
                NavigationLink {
                    AddAccountView()
                } label: {
                    SubHeaderViewIcon(subheaderIcon: "plus.square", subheaderIconColor: Color("EditButtonBlue"))
                }
                .padding()
            }

            List {
                ForEach(refAccouunts.accounts, id: \.id) {account in

                    NavigationLink {
                        AccountDetailMain()
                    } label: {
                        Text(account.accountCompanyName ?? "")
                    }
                    .foregroundColor(.red)
                }
                .onDelete(perform: deleteAccounts)
            }
            .listStyle(PlainListStyle())
        }
        .navigationBarHidden(true)
    }

View that is used to add data, from sub views.

import SwiftUI
import CoreData

struct DatabaseViewMain: View {

@ObservedObject var refAccouunts = RefactorAccounts()
    //Used for button/nav call out
    @Environment(\.presentationMode) var presentationMode
    var body: some View {

        ScrollView {
            VStack {
                Group {
                    MainAddHeaderView(mainTextField: "Account", iconField: "building")
                }
                Group{
                    AddAccountCompanyName()
                    ViewSpacer()
                    AddAccountAddress()
                    ViewSpacer()
                    AddAccountDetails()
                    ViewSpacer()
                    Button(action: refAccouunts.addAccount) {
                        LargeSaveButtonBlue(saveButtonLabel: "Save Changes")
                    }               
 }
                ViewSpacer()
              LargeDeleteButton(deleteButtonLabel: "Cancel Changes")
                ViewSpacer()
            }
        }
        .environmentObject(refAccouunts.publishedClasses)
        .navigationBarHidden(true)
    }

Specific sub-view


struct AccountDetailMain: View {

    //Refactoring
    @ObservedObject var refAccouunts = RefactorAccounts()

    //Used for button/nav call out
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {        
        VStack {
            HStack {
                SubHeaderViewText(subheaderTitle: "Account")            
                NavigationLink {
                    AddContactView()
                } label: {
                    SubHeaderViewIcon(subheaderIcon: "plus.square", subheaderIconColor: Color("EditButtonBlue"))
                }                
      }
            Spacer()
                .frame(height: 20)
            Group {
                VStack (alignment: .leading) {
                    Text("Account Name")
                        .frame(alignment: .leading)
                        .modifier(AddRangerHeaderStyle())
                        .font(.custom("SF Pro Display Light", fixedSize: 20))
                        .foregroundColor(Color("DarkerGrey"))
                    TextField("Account Name", text: refAccouunts.$publishedClasses.accountNameForCoreData)
                        .textFieldStyle(AddRangerTextFieldStyle())
                    HStack {

                        RangerTextField(labelText: "First Name", placeHolder: "")

                        RangerTextField(labelText: "Last Name", placeHolder: "")

                    }
                    RangerTextField(labelText: "Position", placeHolder: "")
                }
            }            
        }

2      

You can only use @StateObject, @FetchRequest, and @Environment within a View. You are trying to use them inside your RefactorAccounts class and that won't work.

Also, you create a @StateObject here:

@StateObject var refAccouunts = RefactorAccounts()

but then you keep creating the same object as an @ObservableObject in your subviews:

@ObservedObject var refAccouunts = RefactorAccounts()

If RefactorAccounts should be created as an @StateObject in a parent View and referenced in child Views, then you should use:

@ObservedObject var refAccouunts: RefactorAccounts

and pass it in when you create the child View.

2      

Hi @Roosterboy,

Thank you for your reply. Just to make sure I understand this, I'd need to use @Published within the refatcoring class. So I'd write it like this:

@Published var accountBackEnd: AccountBackEnd

My worry here is that AcountBackEnd is the file created by coredata. So now I'd need to initilalize that in my refaftoring file, which to be honest is a question mark for me.

I still don't know how to properly initialize that, and it's embarassing to write.

After this, my views should be using @ObservedObject when referencing to the refactor file. Is this right?

I've been looking for days now to find a solution to this, but I keep coming up flat, and with more errors.

2      

Ok - So I think I've got it to a good place. To initialize the calls in my refatcoring file, I just wrote them out like this:

@Published var accountBackEnd: AccountBackEnd = AccountBackEnd()
@Published var publishedClasses: PublishedClassAccount = PublishedClassAccount()

And as far as I know, that's an ok way to initliaze those properties. Again, AccountBackEnd is a the Entity file created by cxore data.

Where I'm at now is that whenever I click to add/save a new account, nothing happens. It doesn't crash, it just doesn't connect.

2      

I don't really understand why you are trying to fetch a list of your AccountBackEnd entities (since you are—mistakenly—using a FetchRequest) but you also have a Published property that is one AccountBackEnd that you are initializing in your ObservableObject. What is that single AccountBackEnd for? I don't see it used anywhere in the rest of your posted code?

Nor do I really understand what PublishedClasses is doing.

At any rate, this:

@Published var accountBackEnd: AccountBackEnd = AccountBackEnd()
@Published var publishedClasses: PublishedClassAccount = PublishedClassAccount()

isn't going to do what you presumably want it to do. You are creating those items without associating them with any particular Managed Object Context, so they won't be included in your Core Data store. And by making them Published properties, you are telling whatever View observes them that you are expecting them to change. But since they aren't part of any particular context, any changes you make won't be saved anywhere. (And that's if you can even create a valid entity instance without a context; I don't know as I've never tried.)

It's honestly kind of hard to give any good advice since all we are seeing is chopped up pieces without getting a sense of how they all fit together.

2      

Hey - I agree thast I'm probably not making the most sense, but I'm still trying to learn. For what that's worth.

I'll try to post again with the headers attached etc.

Thanks again for taking the time to reply.

EDIT: Just added the headers. :)

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!

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.