In chapter 9 subsection Making an Object Observable you say:
The protocol defines one objectWillChange property only, which synthesizes automatically. That means you aren’t required to implement it — the compiler will do it for you.
Actually, this seems to be inaccurate to me. Take a simple app that lists and edits items from an array. The array is observable but changes to the array elements do not trigger view updates because the array itself (a reference) is unchanged.
This drove me quite demented for hours. If I made the edits on a separate view (e.g.an alert) the list updated on my return to it, but if I implemented a button in the list to toggle a Bool (the edit occurred in the array element but did not cause a view update.
finally I discovered that adding this line to the the button action, the view updates:
viewModel.objectWillChange.send()
// viewModel is the observed object that the view
// is watching that contains the array
So, unless I’ve missed something, an explicit call to objectWillChange.send() is sometimes required.
I’m still puzzled by something arising from the above. My simple app to test my above claim contained the following view which produces a single line in a list. It consists of buttons on the left and right and some text between them.
The left-hand button changes the order of the list and since it deletes an element and inserts it elsewhere, it causes the view to update.
THe right-hand button toggles a member of one array element and requires the objectWillChange.send() method to force a view update.
Here’s the puzzle:
Each works as desired when only one is present, but the code below, with both present works correctly on macOS but shows strange behaviour on iOS. Clicking on either button causes both to fire. Indeed, so does clicking on the central text!!
Can anybody tell me why? Do we have a bug here?
var body: some View {
HStack {
// left-hand button
Button(action: { viewModel.shiftPersonDown(person) })
{
Label("", systemImage: "arrow.turn.left.down")
.foregroundColor(.blue)
}
// central text
Text(person.name)
.font(.title)
.frame(maxWidth:.infinity)
// right-hand button
Button(action: {
person.isStudent.toggle()
viewModel.objectWillChange.send(). // required to have view update
})
{
if person.isStudent {
Label("", systemImage: "checkmark")
.foregroundColor(.green)
} else {
Label("", systemImage: "multiply")
.foregroundColor(.red)
}
}
}
}
Well I’ve made a further puzzling discovery. The code above (from PersonView) was being called by this code and it seems to have caused the problem:
// this code does not work correctly
List {
ForEach(people) { person in
PersonView(person: person)
}
}
When I tried the following slight modifications (see below), I got the expected answer. It seems to me that chapter 14 which encourages the use of List could do with a health warning 
// curiously, this code does work correctly
ScrollView {
ForEach(people) { person in
PersonView(person: person)
}
}
// and so does this code work correctly
Group {
ForEach(people) { person in
PersonView(person: person)
}
}
My question becomes: what is different about List that it behaves in this unexpected way?