I'm having trouble refactoring a function call

Hi Everyone,

I'm trying to refactor a function to make less future errors in my code. I think I'm doing it the right way, but I keep coming across this error, "No ObservableObject of type PublishedClassAccount found. A View.environmentObject(_:) for PublishedClassAccount may be missing as an ancestor of this view."

I will put my code below, but please let me know if I've missed anything. I keep reading online that it has something to do with needing to add an EnvironmentObject, but I can't make sense of it past that. I've been trying to refactor this for weeks and still not getting anywhere. Once I've learned this I'll apply this refactor to other views that will need it.

Any help here is greatly appreciated! Thank you!

This is the class that I'm trying to refactor to. I've added all of the objects to it that needed to be. Error happens on the line that reads, "newAccount.accountCompanyName = publishedClassAccount.accountCompanyNamePublished":

import SwiftUI



class funcTest: ObservableObject {

    

    //Handles the in-out with the CoreData persistence store

    @Environment(\.managedObjectContext) var viewContext

    @FetchRequest(

        sortDescriptors: [NSSortDescriptor(keyPath: \AccountBackEnd.accountID, ascending: true)],

        animation: .default)

    var accounts: FetchedResults<AccountBackEnd>

    

    //Access Published Objects

    @EnvironmentObject var publishedClassAccount: PublishedClassAccount

    

    //Used for button/nav call out

    @Environment(\.presentationMode) var presentationMode

    

    func addAccount() {

        withAnimation {

            //Adds to persistence store

            let newAccount = AccountBackEnd(context: viewContext)

            newAccount.accountCompanyName = publishedClassAccount.accountCompanyNamePublished

            newAccount.accountAddress = publishedClassAccount.accountAddressPublished

            

            PersistenceController.shared.save

            {

                error in

                if let error = error {

                    print(error.localizedDescription)

                    return

                }

                print("Successfully saved account.")

            }

        

            //Exits  view

            self.presentationMode.wrappedValue.dismiss()

            publishedClassAccount.accountCompanyNamePublished = ""

        }

    }

}

This is the view that first allows the user to add an account:

import SwiftUI

import CoreData





//View for user to add a new account with

struct AddAccountView: View {

    

    //Handles the in-out with the CoreData persistence store

    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(

        sortDescriptors: [NSSortDescriptor(keyPath: \AccountBackEnd.accountID, ascending: true)],

        animation: .default)

    private var accounts: FetchedResults<AccountBackEnd>



    //Access Published Objects

    @EnvironmentObject var publishedClasses: PublishedClassAccount

    

    //Used for button/nav call out

    @Environment(\.presentationMode) var presentationMode

        
     //Calls func for refactoring
    @StateObject var vm = funcTest()
    

    var body: some View {

        

        ScrollView {

            

            VStack {

                Group {

                    MainAddHeaderView(mainTextField: "Account", iconField: "building")

                }

                

                Group{

                    AddAccountCompanyName()

                    ViewSpacer()

                    

                    AddAccountAddress()

                    ViewSpacer()

                    

                    AddAccountDetails()

                    ViewSpacer()

                    

                    

                    Button(action: vm.addAccount) {

                        LargeSaveButtonBlue(saveButtonLabel: "Save Changes")

                    }

                    .environmentObject(publishedClasses)

                    

                }

                

                ViewSpacer()

                

                LargeDeleteButton(deleteButtonLabel: "Cancel Changes")

                ViewSpacer()

                

            }

        }

        .navigationBarHidden(true)

    }

    

}

And this is the view that is meant to display the Account after it's been entered:

import SwiftUI

import CoreData





struct AccountDetailMain: View {



    //Takes object from @StateObject, presents as var

    @ObservedObject var accountBackEnd: AccountBackEnd

    

    var body: some View {

        

        ScrollView {

            

            VStack {

                MainHeaderViewTop(mainTextField: accountBackEnd.accountCompanyName ?? "", iconField: "building")

            }

            

            Spacer()

                .frame(height: 5)

            

            HStack {

                

                NavigationLink {

                    EditAccountView()

                } label: {

                    MainHeaderViewBottomLeft()

                }



                Spacer()

                

                NavigationLink {

                    AddAccountView()

                } label: {

                    SubHeaderViewIcon(subheaderIcon: "plus.square", subheaderIconColor: Color("EditButtonBlue"))

                }

            }

            

            ViewSpacer()

            

            Group {

                SalesRecords()

                ViewSpacer()

                

                LatestThreeQuotes()

                ViewSpacer()

                

                LatestNote()

                ViewSpacer()

            }

            

            Group {

                AccountsContactsView()

                ViewSpacer()

                

                BranchContactInfoView()

                ViewSpacer()

                

                AccountAddressView()

                ViewSpacer()

                

                RepDetailsView()

                ViewSpacer()

            }

            

            

            Group {

                

                NavigationLink {

                    EditAccountView()

                } label: {

                    LargeEditButtonGreen(editButtonLabel: "Edit Account")

                }



                ViewSpacer()

                

            }

        }

        .navigationBarHidden(true)

        //.environmentObject(publishedClasses)

    }

}

I cannot find any code instantiating PublishedClassAccount. Where do you instantiate it?

By the way, as I already wrote in another thread of yours, using @Environment, @FetchRequest or @EnvironmentObject in a non-View context would not make sense and will never work as you expect. Why are you still using them?

Hi @OOPer, and thank you for writing back.

  • I have a class file for instantiating PublishedClassAccount. As far as I can tell, it's doing the trick, but I might be wrong here. At any rate, I'll put it below for review.

  • I do remember you saying that they wouldn't work but I'm lost as to how I would create an object in the @Environment call anyway. I'll put that code below first:

    @Environment(\.presentationMode) var presentationMode

As well, here's my code for PublishedClassAccount: import SwiftUI

class PublishedClassAccount: ObservableObject {



    //Account ID & Name

    @Published var accountID = UUID()

    @Published var accountCompanyNamePublished = ""

    

    //Details

    @Published var accountCustomerTypePublished = ""

    @Published var accountLastDateVisitedPublished = ""

    @Published var accountManagerPublished = ""

    @Published var accountMarketTypePublished = ""



    //Address

    @Published var accountAddressPublished = ""

    @Published var accountCityPublished = ""

    @Published var accountPostalCodePublished = ""

    @Published var accountProvincePublished = ""

    

    //Contact Type

    @Published var accountWebsitePublished = ""

    @Published var accountEmailAddressPublished = ""

    @Published var accountPhoneOnePublished = ""

    @Published var accountPhoneTwoPublished = ""





//    @Published var toContacts: NSSet?

//    @Published var toQuotes: NSSet?

//    @Published var toNotes: NSSet?

    

}

I have a class file for instantiating PublishedClassAccount. 

Please show the code instantiating PublishedClassAccount.

here's my code for PublishedClassAccount

Thanks for showing the definition of PublishedClassAccount, but I want to see the code where it instantiates PublishedClassAccount.


I'm lost as to how I would create an object in the @Environment call anyway.

I do not understand what you want to achieve, but there is one thing clear:

You cannot use @Environment in a non-View context.

  • If you want to define a class, you cannot use @Environment in the class.
  • If you want to use @Environment, you cannot use class.

Hey cool. Thanks for taking the time to write back.

I get it. I'm a frustrating person to help out. I'm new to all of this, and trying really hard to ask for help, which is hard because sometimes the verbage is lost on me. For example instantiate vs initialize. As another example, last year I didn't understand the concept of scope, but knew there was something going on with my code, where some parts of it weren't being picked up by other parts of my code. Trying to describe in writing what I was working through, and struggling with, to an internet forum is not easy when you don't even know the words to use. Added to this, trying to make sense of Apple's documentation is often times a struggle. And I can't tell you how many countless hours I've spent googling for some form of help while I keep getting nowhere. So that's why I'm here. And in spite what I've just said, I do appreciate your help, and the countless others who have guided me over this last year and a half. It has not been easy.

So say for example, when you say instantiate, you clearly don't mean where the object is first established. I think that's initialization. So I'm left thinking you're talking about when it's called into a view. If that's the case, it's here: "    @EnvironmentObject var publishedClassContact: PublishedClassContact. That's in my AddAccountView. If that's not correct, let me know.

That's called in my AddAccountView file, because I think I need that in order to run my function to save account entries. Yes it's a class. If I try and refactor I clearly cannot use @EnvironmentObject, so my only option is to use @Published, which then calls for that line of code to be initialized. I've asked for help here before about if the way I'm initializing is proper, and no one seems to be able to tell me. Again, I've struggled with finding out online how to make sense of this.

Here's what I'm trying to do, and maybe this will help shed some light on this. If you've made it this far, thank you very kindly for taking your time here:

I'm trying to use SwiftUI, Core Data, and property wrappers to create an account list. That list has several attributes per entity. And some of those entities need to connect to other entities. I have not had to wrestle with that yet. I will in time though. I'm sure.

My thought is to refactor the logic away from my views, and into classes. The 'PublishedClass' calls are for the entries of data into the account list. For example, some of them are accountAddress, accountPhoneNumber, accountCustomerType etc. That list has those already set as @Published. So again Im trying to have the publishedClass files be separate from the refactor files. Please let me know if this needs clarification. When I use property wrappers, I seem to run into issues, and I think those issues come from my lack of understanding around initializing.

Please let me know what parts of this need clarification.

Thank you again.

If that's not correct, let me know.

Not correct. Instantiating means creating a new instance of some specific type. The code var publishedClassContact: PublishedClassContact declares that the type of publishedClassContact is PublishedClassContact, but it does not create an instance of PublishedClassContact.

For example, in this line @StateObject var vm = funcTest(), you create an instance of funcTest with using the initializer init().

(By the way, in Swift, type names should start with Capital letters, you should better follow the basic coding rule of Swift, even if you are writing a simplified example.)


Please let me know if this needs clarification.

You repeat refactor but not clear about to achieve what.

Ok that helps! Thank you!

I ended up re-writing my refactor file to use functions in place of property wrappers. After reading your reply and reading more about Observable Objects, I finally got passed this error:

Thread 1: Fatal error: No ObservableObject of type RefactoringAccount found.  A View.environmentObject(_:) for RefactoringAccount may be missing as an ancestor of this view.

Turns out I neglected to add ".environmentObject(refactorAccount)" in the first file that loads. I can't remember what it's called, but it's got @main near its beginning. Also, and you'll be happy to know this, my refactor file now only has @Published calls in it. ;) Woot!

And now I'm dealing with getting data to store! LOL! The pain never ends.

Thanks again!

I'm having trouble refactoring a function call
 
 
Q