Reputation: 9476
So I have a SwiftUI view that I instantiate in a parents ViewControllers viewDidLoad()
method.
It looks something like this:
class ChildViewController: UIViewController {
private var historyView: HistoryView?
private var models: [HistoryModel]?
...
override func viewDidLoad() {
super.viewDidLoad()
historyView = HistoryView(models: models)
let historyViewHost = UIHostingController(rootView: historyView)
view.addSubview(historyViewHost.view)
historyViewHost.didMove(toParent: self)
historyViewHost.view.frame = view.frame
}
...
}
At some point i need to update the models in the historyView and I do so like this:
func updateHistory() {
let updatedModels = requests.map({ HistoryModel.fromRequest(request: $0) })
historyView!.historyCellModels = updatedModels
}
The problem is the models in the view do not actually get updated. And I don't mean that the view doesn't display the new models, but it actually doesn't even get the new list of models.
My HistoryView also updates everytime I hit a button, when I hit that button I have a breakpoint set in the construction of the view, and from the debugger I can see that the models do not get updated. I also have a breakpoint in my updateHistory
method, where i can see that the models in the parent ARE updated, its just not getting passed down to the child.
I had the idea that maybe 2 instances of the view were being created and I was just updating the wrong one. So I viewed the memory of the historyView
when I had a breakpoint inside it, and I wrote down where it was in memory. Then at the breakpoint I have in the parent view, i went to look at the memory of historyView
and it pointed to 0x00! But whats even stranger is that historyView
is not nil! I even did a force cast of it to a non-optional and the program had no issues.
So I figured the debugger must be lying to me and just not giving the right info. So I went to some old trusty print statements. When I added print statements like this:
func updateHistory() {
let updatedModels = requests.map({ HistoryCellModel.fromRequest(request: $0) })
print(updatedModels.count)
historyView!.historyCellModels = updatedModels
print(historyView?.historyCellModels.count)
}
And then I create a new model, and call the update function it will output:
11
Optional(10)
How is that possible!
My HistoryView looks something like this:
struct HistoryView: View {
@State var historyCellModels: [HistoryModel]
var body: some View {
List(historyCellModels) { ... }
}
}
I clearly set the two variables to be equal, the view is non-null, there is only one instance of it... I'm really not sure what the next step to hunting this bug is. Is there something obvious I could be missing?
Upvotes: 2
Views: 106
Reputation: 258345
@State
is designed to be used only inside SwiftUI view itself (and recommended always to be declared as private).
So here is a way...
Use instead
struct HistoryView: View {
@ObservedObject var historyCellModels: HistoryViewModel
init(models: HistoryViewModel) {
historyCellModels = models
}
...
where
import Combine
class HistoryViewModel: ObservableObject {
@Published var historyCellModels: [HistoryModel]
}
and now it can be
class ChildViewController: UIViewController {
private var models: HistoryViewModel = HistoryViewModel()
...
and
func updateHistory() {
let updatedModels = requests.map({ HistoryModel.fromRequest(request: $0) })
models.historyCellModels = updatedModels
}
and all should work.
Upvotes: 1