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?

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

 //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.

@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

    //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: "")
                }
            }            
        }

The WWDC 2021 session 10002 has nothing to do with your problem, using the right tags is important to get the right answers sooner. And when you show codes of View types, please show the whole code including the declaration header like struct ??????View: View {..., you are forcing readers to guess which view is used as what view... But, anyway, your problem in this case is very clear: you cannot use @StateObject somewhere else than View (or App or Scene). You may need to completely refactor your code again.

Hey - thanks for the reply!

Really sorry about the struct header and the WWDC issue. I'll work to correct those when I need to write again.

In the mean time I'll try to correct my code here.

Cheers!

As said by the error and @OOPer you can't have StateObject in a non-view.

It looks like RefactorAccounts is a StateObject for the second unnamed code block, the first view code block, what is it named? With that, please provide class/struct names in the future it's hard to understand what is happening without that, also hard to reference it in discussion.

Make RefactorAccounts more like this:


class RefactorAccounts: ObservableObject {
    
    ///Takes object from @StateObject, presents as var
    @Published var accountBackEnd = AccountBackEnd()

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

Also, add 3 slashes to the line(s) directly above a var/func/struct/class/enum/etc. declaration to add to the quick documentation that appears when you hover over code(also in the right hand side of xcode in the doc pane view)

PublishedClassAccount, and AccountBackEnd will need to become ObservaleObjects themselves and use the @Published annotation on their own properties.

@tylerlw82, the tag thing is not a big one, please take care if you have next chance. But, in addition to @StateObject, @Environment, @FetchRequest (nor potentially @State, @EnvironmentObject) would not work in non-View context. (The runtime of SwiftUI manages them when they are in any of the views.) Please share your corrected code when done.

Pardon me. I'll add those below:

PublishedClassAccount:

import SwiftUI

class PublishedClassAccount: ObservableObject {

    @Published var accountNameForCoreData = ""

}

AccountBackEnd (Is my entity for Core data)

import Foundation
import CoreData

@objc(AccountBackEnd)
public class AccountBackEnd: NSManagedObject {

}

.... am I doing any of this right? I feel so deflated.

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?
 
 
Q