1

Im using the new SwiftUI. I have a UserUpdate class which is a Bindable Object and I want to modify these variables and automatically Update the UI.

I update these Values successfully but the views in my UI struct isn't updating when I change the variable in the UserUpdate class.

It only changes when I modify the @EnviromentObject variable in the UI struct itself.

That's my Bindable Object Class:

final class UserUpdate: BindableObject {
    let didChange = PassthroughSubject<Any, Never>()

    var allUsers: [User] = [] {
        didSet {
            print(allUsers)
            didChange.send(allUsers)
        }
    }

    var firstName: String = "" {
        didSet {
            didChange.send(firstName)
        }
    }

    var lastName: String = "" {
        didSet {
            didChange.send(lastName)
        }
    }
}

That's my User class:

struct User: Identifiable {
    let id: Int
    let firstName, lastName: String
}

Here's how I configure my UI:

struct ContentView : View {
    @EnvironmentObject var bindableUser: UserUpdate

    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                Text("All Users:").bold().padding(.leading, 10)
                List {
                    ForEach(bindableUser.allUsers) { user in
                        Text("\(user.firstName) \(user.lastName)")
                    }
                }
            }
        }
    }
}

Here I modify the variables in UserUpdate:

class TestBind {
    static let instance = TestBind()

    let userUpdate = UserUpdate()

    func bind() {
        let user = User(id: userUpdate.allUsers.count, firstName: "Heyy", lastName: "worked")
        userUpdate.allUsers.append(user)
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
SwiftiSwift
  • 7,528
  • 9
  • 56
  • 96
  • 1
    That's because your view variable(bindableUser) is bound to EnvirnomentObject stream. The view wouldn't know if you update some other stream. – SMP Jun 07 '19 at 19:58
  • 1
    And how can I solve that? I don't see why Bindable Object if useful then. – SwiftiSwift Jun 07 '19 at 20:04
  • 1
    Have you bound your environment to the view in your scene delegate? See https://stackoverflow.com/questions/56476007/swiftui-textfield-max-length/56477309?noredirect=1#comment99546841_56477309 – Paulw11 Jun 07 '19 at 21:14
  • 1
    @Paulw11 I've done that already – SwiftiSwift Jun 08 '19 at 04:09
  • 3
    `BindableObject` protocol is now renamed to `ObservableObject`, `ObjectBinding` is now `ObservedObject` and `didChange` is now `objectWillChange`. – Stoyan Nov 06 '19 at 12:39

2 Answers2

2

I found out that I had to call the method from my UI to get it working so its on the same stream.

For example by this:

struct ContentView : View {
@EnvironmentObject var networkManager: NetworkManager

var body: some View {
    VStack {
        Button(action: {
            self.networkManager.getAllCourses()
        }, label: {
            Text("Get All Courses")
        })

        List(networkManager.courses.identified(by: \.name)) {
            Text($0.name)
        }
    }
}
}
SwiftiSwift
  • 7,528
  • 9
  • 56
  • 96
2

If I'm not wrong, you should inject the UserUpdate instance in your ContentView, probably in the SceneDelegate, using ContentView().environmentObject(UserUpdate()).

In that case, you have 2 different instance of the UserUpdate class, the first one created in the SceneDelegate, and the second created in the TestBind class.

The problem is that you have one instance that is bond to view (and will trigger view reload on update), and the one that you actually modify (in TestBind class) which is totally unrelated to the view.

You should find a way to use the same instance in the view and in the TestBind class (for example by using ContentView().environmentObject(TestBind.instance.userUpdate)

rraphael
  • 10,041
  • 2
  • 25
  • 33