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.
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'll like to know why Apple allow to create more than one instance of HMHomeManager and what is the purpose of it?
I would expect the instance of HMHomeManager to be a singleton.
I can't speak for Apple but I see no cases where HMHomeManager would benefit from being a singleton and several clear disadvantages.
HMHomeManager has a delegate property. The delegate pattern works well when you want a delegator to send messages to a single delegate or when many delegators might share a delegate. It is however not useful when one delegator might produce messages of interest to many delegates which would be the case if HMHomeManager were a singleton.
Singletons are not easily deallocated. Any app using a HMHomeManager as a singleton would keep that object in memory monitoring for changes to the home database even if it was no longer needed.
The assumption that there should only ever be one HMHomeManager may not hold true forever. While one HMHomeManager can contain many homes they all share a common user. Designing this class as a singleton would preclude an app from acting on behalf of multiple users at once. Even if there's never a need for such behavior developers might be wise to avoid selecting an interface which cannot support it early in the design process.
Don't create multiple instances of HMHomeManager.May be below code will be helpful. If you want to made any changes to the existing home try to execute the code after made the changes like add room,zone...
for(HMHome *home in appDelegate.homeManager.homes)
{
if([home.name isEqualToString:appDel.selectedHome.name])
{
appDel.selectedHome = home;
}
}
I would expect internally there is a singleton client that communicates with the home daemon, the same way CLLocationManager has a singleton CLLocationInternalClient.sharedServiceClient which allows us to scatter location managers throughout our app without having to worry about forwarding notifications or multicasting delegate methods around ourselves. I haven't examined HMHomeManager as closely and it does seem quite a rushed API so maybe not as well designed. It's concerning the documentation mentions "the manager for your app" as if we are only supposed to have one. Furthermore when you receive the home changed delegate call it tells us to "invalidate your own app's objects that cache the home's data". One thing I have noticed is when you init a HMHomeManager it inits all the rooms, services and accessory objects which in my case is about 400KB (calculated by initing 10 home managers and comparing the RAM difference) for my 301 characteristics, 68 services, 19 accessories home (I used Allocations in Instruments searching for 'HM' to learn this). So we probably should stick to one manager per app but there is a serious design problem with the API that when we toggle a characteristic the delegate method isn't called so we need to reach out to our app's object and invalidate it rather than it happening automatically for us like any other API would.
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.
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.
I have a model which can download data from a server and thus an activity indicator needs to be displayed (both in the status bar and on screen).
But I also have a UIWebView which is displaying content, the content has links, some are local and some are remote, if a remote link needs to be downloaded then an activity indicator needs to be displayed again.
What are design options for accomplishing this?
The app delegate could have methods to start and stop the activity indicators as directed by the models and controllers. But somehow this doesn't seem quite clean to me - its starting to use the app delegate just as a bucket for dumping miscellaneous functionality.
I'm thinking the model should deal with its own activity indication and the controllers should deal with their own, i.e. separate them. However if I did that wouldn't we then have the situation where a model is doing some UI related stuff (even if minimal)?
Is there a clean recommended solution?
IMO, Activity indicator is a View component independent of a specific view therefore it should be managed by your application delegate.
The way I tackled it is I created an ActivityManager class and I create and instance in my application delegate. It takes an instance of UIWindow and from that, I can determine how to center and display the indicator appropriately. All other areas of my application interact with this class through the observer pattern. They post notifications when the network state has changed and my activity manager evaluates the overall state of the application to see if it needs to either show or hide the indicator. Here is a sample of what you might see in my app:
ActivityManager *activityManager = [[ActivityManager alloc] initWithWindow:self.window];
self.activityManager = activityManager;
[activityManager release];
[self.activityManager addNotification:kNetworkStatusDidChangeNotification];
[self.activityManager addAsyncActivity:[HttpManager defaultManager]];
[self.activityManager startObserving];
I've added the ability to add new notifications so that way, when the manager observes these notification is can evaluate what needs to happen. AsyncActivity is just a protocol for singletons I can check to see if they are processing data. Then I just tell my manager to start observing. When I close my app I simply call:
[self.activityManager stopObserving];
to remove all observers and free up any memory. That should get you started in the right direction, I'm sure there are other ways to do it but that seems like the least invasive way to handle it to me. It's also very portable.
It sounds like there could be multiple network events occurring simultaneously that all need to report activity accurately. Perhaps a separate class that is only responsible for tracking network operations and communicating with your instance of UIApplication is in order.
Imagine an object that uses a simple counter to keep track of the number of network operations. Adding an event increments the counter and removing one decrements the counter. While the counter is > 0, the spinner is visible. This may be a reasonable approach to encapsulating communication between the application and the model layer.