Friday, July 17, 2015 – 285 views
— by jazzychad
I just read today's Mike Ash post "When to Use Swift Structs and Classes" which was highly relevant as I am working on a new Swift project.
One point he made hit home as it related to a decision I've been making about using structs or classes for Model objects in the app.
This paragraph was especially interesting:
"What about model types? You might have a User type representing a user on your system, or a Crime type representing an action taken by a User. These are pretty copyable, so they should probably be value types. However, you probably want updates to a User's Crime made in one place in your program to be visible to other parts of the program. This suggests that your Users should be managed by some sort of user controller which would be a reference type."
Because mac/iOS apps are UI driven and necessarily stateful, I like to keep my UI in sync with the data in my model instances.
By way of example, consider the Twitter app. You have multiple tabs within the app which can all display arbitrary feeds/tweets/users/etc.... let's say for example that you have the same tweet open in multiple tabs. If you hit the fav button on one tweet, you want the updated fav count to be reflected in every tweet-view currently living in the app.
In Objective-C I am used to accomplishing this in the following way: Each view that represents some model data has a @property of that type, and when that property is set, the view registers as an observer on NSNotificationCenter for "ModelDidUpdateNotification" on that specific object. When something on the model changes, it posts the "ModelDidUpdate" notification, and the view will update its UI state to reflect the current model's data.
So the whole flow would look like this:
Tap fav button on tweet -> view (or data controller) manipulates model data -> model posts update notification -> view(s) receive notification -> view(s) update UI
This works really well, especially if you have a data store/controller that doles out the same canonical model instance to each view that represents it.
Now consider using Swift structs for models:
You can no longer have "one canonical model instance" as values are copied around. You can no longer use NSNotificationCenter to watch for notifications from a specific model (as you can't use a struct as the target of observation, nor do you have a single canonical copy of the model anyway). You could cheat by using NSNotificationCenter to pass some sort of identifier about the model (e.g. userId) in the userInfo dictionary and box an updated model struct in a generic wrapper class to also read from the userInfo dictionary, but then the observers have to register to listen for updates from 'nil' and filter out any notifications that don't match the identifier they care about (but that seems overly expensive).
For now I have decided to make my models reference types and use the Observer pattern (have a set of observers in the model objects that conform to some Observable/Observer protocol with a method that gets called when something updates) instead of using NSNotificationCenter, and it works just as elegantly as before, but I'm curious how "your User structs should be managed by some sort of user controller which would be a reference type" idea would work to solve this problem to keep everything that cares about a particular model in sync?
I'm curious to hear what other Swift developers think about this specific topic.