https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html
At the above link, I see the following in regards to how the model layer should communicate with the ViewController.
"When a model object changes (for example, new data is received over a network connection), it notifies a controller object, which updates the appropriate view objects."
My question is, how does the model object notify the Controller object? What are various ways this can be done? My model layer gets the user's location and then calls a web service. How should I notify the controller object when that data is downloaded?
There are several ways to do this, depending on your specific case. Generally you would use a protocol/delegate implementation, key value observing, or notifications. There is a pretty good overview here.
Edit:
Thought I should add, as the comment to this answer mentioned: using callback blocks is also a very solid option, depending on your needs. There are a lot of possible deciding factors on what solutions are best for what cases but here is a general outline I follow.
If you are guaranteed to only need one observer at a time: I generally use blocks or delegates. My personal preference is that I use blocks if there are only one or two callbacks because this is where they shine, but I use delegate protocols if there more than a few possible methods.
If you may need multiple observers, I use notifications via NSNotificationCenter.
I use Key-Value-Observing when I only need to observe specific properties on an instance, instead of events.
Related
I have some complex networking in my app( I don't use any third party dependencies, because of project requirements). For instance, I send three network requests in parallel after first two requests provide results. All my networking is done in separate models, known as networkClients(following MVC-S pattern) and are called directly from repository, not from ViewControllers. However, I need the last request to notify my viewController after I get response from network. How should I do that? I don't think notification center would be right solution because it can cause memory leaks and I have not found correct approach to complex problem like this. Please provide some prominent solutions. It should conform to good design pattern like MVVM or MVC and should not be some workaround or hack. Maybe delegates would work? I know that rxSwift would solve my issue, because I could start observing for results after initializing viewController and after data would be updated from repository my viewController would also be notified...
The right design doesn't have VCs observing the network clients directly. Those network operations should be assembling parts of a model, which is what the VC really cares about. Have the VC observe that singular model.
It can do this observing using one of the well known patterns for loosely coupled communication between objects. The OP correctly mentions delegates. Notification center and KVO are others. There's plenty of discussion on SO about which to use and how to implement. (I'd go with NSNotificationCenter as an easy and rational start).
So the order of operation is like this:
allocate the model
launch the network requests and setup those request completions (completion blocks, probably) to update that model with their responses. (the model can launch the requests, which is a reasonable practice).
create the view controller(s) that setup model observation when they initialize (probably, in viewWillAppear or later)
What about the fact that >1 requests are in flight simultaneously? A commenter above points out correctly that GCD offers a way to group those async operations into a single one. But you can do this yourself straight-forwardly: the model decides when it's completely built. The completion code for each request will change some condition in the model to the "ready" state. Each request completion can check to see whether all of the ready conditions are met, and only then post a "ready" notification for observers to see.
Another niggling issue: what if those requests all run very, very fast? Maybe there's some cached response that's ready early, making the model "ready" before the VC has had a chance to setup observation? Handle this straight-forwardly in the VC: before observing the model, check to see if it's ready already and run the same update code that runs on the notification.
I have got several types of a logic controller class that handle the communication with a remote resource. To do so each class uses an instance of a network controller class that deals with the connection details: connects to a resource, sends data, receives data. The logic controller classes are notified of data changes from the network controller class using notifications. So in my current solution each logic controller class registers for the notifications and implements specific functions to deal with them differently.
However, if I want to do proper code engineering, I wonder whether it would be more robust to use protocols and delegation.
I wonder if I could define which methods the logical controller classes need to implement in order to receive the notification. However I am not sure if this makes sense and is correct because I do not want inheritance here.
Any suggestion?
Typically you have 1 or 2 (explained later) methods per notification. So it is a quite simple mapping. The reason for that is that you have one notification per "event" and there is little reason to chance that for delegation.
But your design is good. What is the difference between delegation and notifications? (Robustness is not.)
A delegate is a single instance that customizes the behavior of the delegating instance. Therefore beside the usual "inform" methods (will…, did…) it can have the ability to change the behavior of the delegating, i. e. returning NO as an "invalid marker" (should…) or change the data set, a operation is executed on. (Sometimes this is the 2nd method for an event.) It is obvious that in such cases you cannot have multiple instances at a time. Delegation is in competition with subclassing and often the better choice (white boxing vs. black boxing).
You can have as many notification receivers as you want to. Obviously they cannot change the behavior of the notification sender. Therefore it is the better choice, if you have several instances that only needs an information that an event happened.
If understood your use case correctly, notifications are perfectly fitting your requirements.
I originally assumed the answer to my question was a simple 'yes', but some of the reading I've done since has had me looking at the question again.
My intention is to use modular code, which lets me pick and choose from a number of code sections to add various features to an app. As an example, I might have a browser component that allows for safe-browsing, a survey component to take user surveys, and a 'core' component that is the primary purpose of the app -- be it displaying a menu or displaying a map of a location.
I'd want each independent module to have it's own CoreData stack. They don't talk to each other or interfere with each other in any way, outside of whatever is mediated by the app itself (communication to the app done using the delegate model). The browser's list of bookmarks and whitelisted sites is separate from the survey's list of survey data, which is also completely seperate from anything the 'core' of the app does. If they need to talk to each other, they can do so through delegate calls, which will consist of telling the app 'I need a browser to display X page', 'display the survey with this id', or finally "I'm done, return to the main app".
What started me down this path was realizing that there was no way to determine whether a given NSManagedObjectContextDidSaveNotification belongs to a given core data stack or not. And, presumably, giving a MOC the notification from a different core data stack in the mergeChangesFromContextDidSaveNotification: method would be a bad idea. (I'm also concerned what happens when you try to feed a MOC's own notification back into it, but that's something I can experiment with easily enough)
You can (and this is explicitly recommended in the NSManagedObjectContext documentation)
register for changes coming from a specific context:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(<#Selector name#>)
name:NSManagedObjectContextDidSaveNotification
object:<#A managed object context#>];
and when you receive such a notification, the notification object is the managed object context. It is therefore possible to create independent components where each
component uses its own Core Data stack.
As Martin R noted, you can register for notifications from a specific context. If you're listening for notifications from more than one context, it's also possible to ask the notification which context posted it and proceed based on that. With NSManagedObjectContextDidSaveNotification, look at [notification object] to find out which context posted it.
Keeping your objects in entirely different stacks, with separate persistent stores, seems excessive unless there's some kind of security-related issue. Like maybe, for some reason it's very important that one collection of data never be allowed to near some other collection. As described, the app doesn't seem to have any compelling reason for the extra complexity.
You might find it preferable to use one stack and one persistent store, but with multiple configurations. Each configuration would include specific entities from the data model. You'd have a single persistent store and one NSManagedObjectModel instance but multiple NSPersistentStoreCoordinator instances. Indicate which configuration you want when calling addPersistentStoreWithType:configuration:URL:options:error:.
As part of my syncing solution, I use a sync status for all objects of a certain class. Whenever specific (not all) attributes of that object change, I want to update the status.
I am considering four approaches:
Manually setting the status in code wherever I change
something that needs to synced. This is the most obvious, but also
the most laborious and error-prone (I'll need to remember to also add the sync status update any time I add new functionality).
Track it using a core data notification (e.g. willSave or
NSManagedObjectContextObjectsDidChangeNotification). This seems the most appropriate at first glance -
I simply sign up for the notifications in my AppDelegate and update
the status each time. But will it be possible to examine the changes
and only update the sync status when an attribute I care about is
updated? Also, won't setting
the sync_status itself also fires this notification, leading me into an endless loop? How would I address this?
Custom setters on the attributes I care about. I have had
trouble trying to get this working before, and eventually decided
to try to leave the standard core data getters/setters alone. But I
would delve back in if it is the best fit.
KVO. I've not used this pattern before, but it might be easiest to
just sign up for notifications of attribute changes for those I care
about and set the flag there. But where would I do this? I need
to monitor every object of the class, so would it be possible to start observing an
attribute's KVO notifications in that same object's awakeFromInsert?
I.e., whenever an object is created, immediately have that same
object listen for changes to attributeA and set it's own sync_status
when it fires?
Which of these approaches will serve me best? Perhaps I am missing some other ideas?
Manually setting the status code
Probably a bad idea, for exactly the reason you describe. You'll need to do this in all kinds of cases. You might not always be the developer on the app. One day you or someone else will forget it. Even if you don't, you get extra code all over the place that could be centralized.
Track it using a core data notification [...] Also, won't setting the sync_status itself also fires this notification, leading me into an endless loop?
It depends how you do it. Listening for NSManagedObjectContextDidSaveNotification could work, if you use a secondary NSManagedObjectContext. That way you can set the sync flag, save changes, and avoid looping because you're saving on a different context that you're not observing.
Using NSManagedObjectContextObjectsDidChangeNotification could also work. That will be posted when object properties are changed but a save is not actually in progress yet. Inspect the userInfo dictionary to see if anything you care about has changed, and if so, set your sync_status flag. Setting the flag would trigger a new notification, but it will be one you don't care about, so you break the loop. Using a separate context would also prevent looping here.
Custom setters on the attributes I care about.
Definitely workable, though depending on how many attributes you care about, you could end up with a lot of accessors just to update sync status. Of the four you mention, this is the one I would use.
A related but simpler approach would be to override willSave on your managed object classes. That will get called just before a save. Implement it to
Look through [self changedValues] for attributes that trigger syncing.
Set the sync flag if you find any of them.
This way you only have one custom method per entity, no matter how many attributes end up triggering the sync flag.
KVO
Should work, but is probably less intuitive to get working right than custom setters.
When I had to do something like this, I put the sync information outside of my data store. I'd listen for NSManagedObjectContextDidSaveNotification, and in the observer method I would:
Look through userInfo to see what had changed
Get the NSManagedObjectID for each object that would need to sync
Convert the object IDs to NSStrings and add those to a list saved in a separate file.
On a successful sync I'd clear out the object ID list.
The thinking was mainly that a sync flag is more metadata than actual model data, so I kept it out of the model. If you prefer to keep it in the model, I'd go with overriding willSave.
I am building a remote app which is receiving different states of its accessory. It is receiving things like: power state on/off, volume state 5, equalizer setting jazz, etc. and has nothing more to do than map theses states into the UI with selected or unselected states and send changes done back to the accessory.
About the app architecture:
The app is connected with it's accessory as illustrated in Apples EADemo project using the external accessory framework.
The UI is build within non-repeating customized UITableViewCell full of UIButtons. When starting the app a data model class will receive all current states from the examples EADSessionController and has to communicate theses states to the UI (the cells directly rather than the UITableViewController) with one of the mentioned patterns. This will be a stand alone, one-page app looking like a real remote.
Thinking of NSNotification, delegates and KVO (key-value-observing) I am trying to figure out which of these patterns I should use for this special approach?
Any answer on why choosing one of them and a brief description on how to implement would be appreciated. If your answer will be KVO please give some more insights since I never used this pattern so far.
It really depends.
The most loosely coupled one is to use NSNotification and NSNotificationCenter, as the instance which post the notification does not necessarily have knowledge about the observer, and there can be more than one observer.
The delegate pattern is a little more rigid, and there could usually be only one delegate object that receives a message. If the UITableViewController in your project is the only instance that handles a message(, or it would properly propagate the message to other components), it is still OK.
The KVO pattern requires more precisely designed observation relationship. You will have and have to look after exactly how the KVO is implemented. And KVO also allows one-to-many observation. The down side of KVO is if the observing relationship is dynamic and transient, you must take much more care about how these objects were torn down, or you could get a lot of crashes like sending updates to a dealloc'ed instance, etc.
If you are working on a library which would be delivered to a 3rd party to use, perhaps NSNotification would be the first choice.