2

I am new to swift programming. I was trying to play around with the environment object to update views by changing the value of the environment object specifically via a class function. I have two subviews in the contentview as I wish to see if both subviews get updated together when the string gets changed in UserData environment object.

However, there's an error saying 'Thread 1: Fatal error: No ObservableObject of type UserData found. A View.environmentObject(_:) for UserData may be missing ...'

In SceneDelagate.swift, I have injected the environment object:

let userData = UserData()

window.rootViewController = UIHostingController(rootView:contentView.environmentObject(userData))

UserData.swift

final class UserData: ObservableObject {

    @Published var image: UIImage? = nil
    @Published var string  = "" 



}

StringGenerator.swift

class StringGenerator {

    @EnvironmentObject var userData: UserData


    func changeURL(){
        print("Changing string now from \(userData.string)")
        if self.userData.string == "HELLO"{
            self.userData.string = "BYE"

        }
        else{
            self.userData.string = "HELLO"
        }
        print("to \(userData.string)")
    }
}

SubView1.swift

struct SubView1: View {
    @EnvironmentObject var userData: UserData



    var body: some View {
        Text("\(userData.string)")

    }
}

SubView2.swift

struct SubView2: View {
    @EnvironmentObject var userData: UserData



    var body: some View {
        Text("\(userData.string)")

    }
}

ContentView.swift

struct ContentView: View {
    @EnvironmentObject var userData: UserData

    let stringGenerator = StringGenerator()

    var body: some View {

        VStack{

            Button(action: {
                print("Changing Text Now !")
                self.stringGenerator.changeURL()
                print("\(self.userData.string)")
            }) {
                Text("Change Text ")
            }


            SubView1()
            SubView2()


        }


    }
}

In general, I wish to know how does one change the environment object via a class function.

The code crashes when I run it. What's the problem in here?

mic
  • 155
  • 2
  • 9
  • `stringGenerator` doesn't inherit the environment because it isn't part of the view hierarchy. Also, there is no class function here. You could explicitly pass the `userData` to `changeURL` – Paulw11 May 29 '20 at 10:36
  • could you post some code? – mic May 29 '20 at 13:45

1 Answers1

1

Here is a solution:

1) in ContentView

struct ContentView: View {
    @EnvironmentObject var userData: UserData

    let stringGenerator: StringGenerator   // declare-only

2) in generator

class StringGenerator {

    var userData: UserData   // just member
    init(userData: UserData) {
      self.userData = userData
    }

3) in SceneDelegate

let userData = UserData()

// inject generator via constructor with same user data
let contentView = ContentView(stringGenerator: 
                              StringGenerator(userData: userData))

window.rootViewController = UIHostingController(rootView: 
                              contentView.environmentObject(userData))
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 1
    This wont compile, error appears: "Class 'StringGenerator' has no initializers", and "'StringGenerator' cannot be constructed because it has no accessible initializers" – mic May 29 '20 at 13:43