I have a current code base which I want to refactor.
The controller (or viewcontroller for people familiar with iOS) is around 2000 lines and doing lot of stuff.
Controller is responsible for :
1) Communicating between view and model
1.1) Handling callbacks / actions from view
1.2) Handling gestures.
1.3) Passing data from model to view.
2) Making web request if model is empty.
2.1) preparing URL and sending request to connection handler.
2.2) Parsing logic
3) Customizing UI
3.1) Minor UI modifications.
4) Business logic.
4.1) A little bit of code deals with business logic of calculation some reports.
I was thinking to delegate some responsibilities to Model and View.
Any thoughts on how to go about it?
Also, more specifically I want to know who is responsible for making web request model or controller ?
There are a couple of concepts that have been useful for me in writing easily maintained code:
In addition to the concrete model objects (e.g. if requesting weather information from a service, you might have a Weather class that represents a particular weather report, e.g., a city, a temperature, and narrative properties), I'll also employ a data controller object that coordinates the requesting of objects from either a cache, persistent storage, or from the web, as appropriate (often in that order). You want to be wary of relying solely upon simplistic requests of a web service, as caches and the like can be quite important when designing responsive UI.
The key point for me is that I rarely want either views or view controllers messing around with this level of detail (though, I confess, in trivial situations, I've done the requests directly from the controller). Ideally, the view controller will initiate an asynchronous request of my data controller, passing it a completion block with parameters of (a) the model object being retrieved; and (b) a NSError.
I'll include an example of what this might look like below.
As you might infer from above, I often also abstract the particulars of the initiation of the web requests and parsing of the responses into their own objects, themselves (because the data controller may be complicated enough, managing caches, persistent storage, etc.). I generally use a NSOperation subclass for request/parsing tasks (which lends itself to asynchronous requests with completion blocks; the ability to cancel pending requests if the UI moves on to something else, etc.). Thus, my data controller will, if it concludes that cached and/or persistent storage cannot satisfy the request, will initiate the asynchronous request/parse operation.
You list a couple of other responsibilities that you've assigned to your view controller, but might best be abstracted out, too:
1.2) Handling gestures - If a gesture gets a little complicated (e.g. horizontal only, swipe from edge, etc.), I'll actually subclass the gesture handler, greatly simplifying the view controller's own interaction with the gesture handler.
3) Customizing UI - If the UI requires much customization, I'll often subclass the appropriate view, too. Particularly with UITableViewCell and UICollectionViewCell, this can greatly simplify one's view controller code. And any views that require any material customization can often be done more gracefully in a subclass of the view itself.
Sample data controller abstraction
So, I might have a completion block defined like so:
typedef void(^WeatherRequestCompletion)(WeatherReport *weatherReport, NSError *error);
And then I might have a method in my data controller with an interface like so:
- (WeatherRequest *)requestWeatherWithIdentifier:(CityIdentifier)cityIdentifier completion:(WeatherRequestCompletion)completion;
And my view controller would use it like so:
typeof(self) __weak weakSelf = self;
self.weatherRequest = [[WeatherModel sharedController] requestWeatherWithIdentifier:self.cityIdentifier completion:^(WeatherReport *weatherReport, NSError *error) {
if (error) {
// handle error
}
if (weatherReport) {
weakSelf.cityLabel.text = weatherReport.city;
weakSelf.tempLabel.text = [weatherReport.temperature stringValue];
weakSelf.narrativeLabel.text = weatherReport.narrative;
}
}];
The view controller shouldn't be worried about how the request is formatted nor how the response is parsed (that's the job of my network request NSOperation subclass). Nor should the view controller even be too involved in the caching and/or persistent storage logic (that's the job of my data controller). The view controller should, thereby be distilled to something very logical and easy to follow.
Note, you'll notice that my data controller is returning a request object (which is, for me, frequently just a typedef to a NSOperation). I'll have my view controller maintain weak reference to this, so that I can easily cancel the request if and when needed, e.g. in the dealloc of the view controller):
- (void)dealloc
{
[_weatherRequest cancel];
}
Most of that seems to be typical ViewController behavior. Apple does have a nice document describing the MVC architecture in detail: http://developer.apple.com/library/ios/documentation/general/conceptual/devpedia-cocoacore/MVC.html
"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."
This indicates that it is appropriate for you model to handle network activity and updating. The only other thing you could change would be to put as much of the UI work as possible in the view.
Related
I've being working for a bit now with MVVM along with Swift. The principle is very simple:
ViewController is responsible for the View;
ViewModel is responsible for the Model;
ViewController owns the View;
ViewController owns the ViewModel;
ViewModel own the Model;
period.
That's clear to me. But there are some questions I still have about it (they might sound stupid, but it's important to be as clear to newbies as the rules above):
Should ViewModel be responsible for the business logic? I mean, since ViewController don't own models, it feels right to place the biz logic in ViewModel;
Should ViewModel be a struct? This is a tricky one. I'm guessing ViewModel should also give an API to ViewController interact with data. Therefore, sometimes we need to use some escaping closures in order to fetch asynchronous data (e.g. HTTP calls). Structs can't hold it.
Should I hide non-outlets variables from ViewController? Sometimes we need a strong reference to some property in order to present views (e.g. keep the current page of an UIPageControl). Should ViewController hold this reference?
How can I fire ViewController updates from ViewModel? I do read about reactive programming, but my question is wider: Sometimes ViewModel needs ViewController to respond to some actions, which is the best way to do it? Should ViewModel keep specific callbacks to specific reactions ViewController can handle? I came across Srdan Rasic's post about "binding" properties and I got confused (perhaps its not related to this question at all).
I'll be glad if you guys can share your solutions. Thanks!
Your question is quite wide. Someone will certainly explain it more widely than me, or might have different opinions. But these are my observations:
1. Yes ViewModel should be the only component which handles business logic
2. I guess that you answered it yourself, it should be a class. I do not have any reason for why should it be struct. But if it fits your needs who am I to say.
3. Depends on the use. If you use it for purpose of a view, then it is okay to have it here. There also exists a concept of having separate ViewModels for smaller parts, unfortunately, do not know the name. I mean you have a main ViewModel for a whole view and subViewModels for subview. So you will not end up having a big bunch of a code in ViewModel. Also sometimes it is acceptable technical debt (hope that no on will kill me for saying that)
4. It goes like this. You will press the button, you will call the method in your ViewModel called buttonPressed(or triggers an event). Then your ViewModel will do its job, probably obtain a new data and sets the view for that. From what I've seen you might do it with having a weak reference to your View in your ViewModel, or bind reactive properties in your View. The second option will leave you without the need of having View in your ViewModel, but you will have to to register observer for every view setting property from ViewModel.
I'm a bit sceptic wether this belongs to stackoverflow or not, please let me know if I should post to codereview or programmers instead.
I want to write a reusable component for iOS that does something like this:
Accepts a number as an input, let's call it inputNumber
Records audio for 10 seconds or until the user stops recording
During recording shows live metering
After the recording, checks the recording using the provided inputNumber (we don't really care about this part at the moment)
Allows the user to play the recorded audio
Returns a path to the recorded file
Returns YES or NO based on the check of step 4
The GUI is pretty simple, just two buttons (play/pause and record/stop) together with a view for showing the audio metering during recording.
Something like this:
Now, I have implemented all the logic I need in a singleton "manager" class and what I want to do is also provide the GUI. That means that the consumers will not care about enabling/disabling buttons, showing/hiding the meter etc.
I would like for others to be able to keep using this "manager" (and probably it will be refactored to stop being a singleton), but at the same time I would like to give the option to use it as a "drop in" view.
So, this is a question of how can I setup the whole architecture.
Right now my manager has only one method:
typedef void(^CompletionBlock)(BOOL isValid, NSString* filePath, NSError *error);
-(void)evaluateRecordingForInputNumber:(NSNumber *)inputNumber completion:(CompletionBlock)completionBlock;
and this works as I want.
But if I introduce a view, how should my API look? It doesn't seem correct to me to write a custom UIView -init that will take the role of the aforementioned method, since ideally I would like to reuse this view for different model classes (providing the inputNumber) that need to be evaluated.
How can I choose between subclassing NSObject, UIControl or UIView in my case? Is there a nice way to use both my "engine" as a standalone component and also optionally provide a backing view layer?
I think this coupling is largely dependent on how you envision the custom view/manager being used.
Will it be a lot of logic that people can use without the view, and the view is just an optional feature? If so it likely makes sense to subclass NSObject and prove the view as a property of the manager itself. That way people can use your manager class as a standalone, and in UIView's where it's needed they can do something like the following:
[self.view addSubview:myCustomManager.audioView];
On the other hand if the manager class has no value to your user without the UIView itself then I think it makes a lot of sense to subclass UIView, and hide your manager class behind that UIView. One example of a widget that uses this style of implementation is stripe: Stripe iOS Widget. Everything is based off of their 'STPView', like retrieving a charge token:
[self.stripeView createToken:^(STPToken *token, NSError *error) {
if (error) {
// Handle error
// [self handleError:error];
} else {
// Send off token to your server
// [self handleToken:token];
}
}];}
One can imagine a private 'manager' class behind the scenes of the stripeView doing all the work (this method is in addition to their delegate callback).
In your case this might be a fairly good pattern. You can add a property to the custom view for the number to be evaluated, add the view to the hierarchy, and then create a custom delegate that calls back automatically after it processes things; which would take the place of your manager class callback.
The final answer is that it depends a lot on how much of split between business logic and UIView treats this will provide. As long as the code is readable, maintainable, and you have some reasonable pattern to follow I don't think anyone is going to nail you to a cross in iOS land.
I'm starting out with ReactiveCocoa. The simple things make sense, but I can't yet do complexity. ;)
This is what I'm trying to model: I have a view controller that displays some data requested from an HTTP endpoint. The HTTP endpoint uses a browser-like basic auth with cookies.
So, I want to make that HTTP request. If it succeeds, great, display the data. If it fails with a 401, I need to tell the view to pop up a modal dialog asking for the username/password, and then retry the HTTP request.
In my ViewModel, then, do I have two signals? One that returns the content, and another which tells the view layer that I need the credentials? How do I get the credentials back down to where the HTTP request happens?
Your ViewModel adapts your application's model to its view. In other words, it maintains whatever state that the view needs to display (in the form of bindable properties) and exposes API to update that state and do the "work" of your application (in the form of plain old methods). So, judging from what you wrote:
"I have a view controller that displays some data requested from an HTTP endpoint."
It sounds like to start with, your ViewModel should have some way to represent this data as state. This can almost always be done with Objective-C properties:
#interface MyViewModel : NSObject
#property (nonatomic, strong) NSArray *tableData;
// (Or whatever makes sense for your view.)
#property (nonatomic) BOOL needCredentials;
#end
Your view controller should then bind to these properties so that whenever a property changes (e.g., whenever new data is retrieved, or a 401 error is received), the corresponding UIViews are updated. Notice how your ViewModel's API doesn't even have any ReactiveCocoa code. That's because if your view controller has a reference to the ViewModel object, the view controller can use ReactiveCocoa to bind to the ViewModel in whatever way makes sense. For example, in simpler circumstances you can just use RAC(self, infoView.name) = RACObserve(self, myViewModel.infoViewName);. In more complex cases, such as implementing a UITableViewDelegate, you would need to implement the UITableViewDataSource methods, but it's the same idea. To display a modal dialog asking for the username and password, you might even use something like
- (void)viewDidLoad
{
self.myViewModel = [[ViewModel alloc] init];
#weakify(self);
[[RACObserve(self, myViewModel.needCredentials) ignore:#NO] subscribeNext:^(id _) {
#strongify(self);
[self displayModalDialog];
}];
}
"So, I want to make that HTTP request. If it succeeds, great, display the data. If it fails with a 401, I need to tell the view to pop up a modal dialog asking for the username/password, and then retry the HTTP request."
Your ViewModel could have a method such as - (void)sendRequest:(NSDictionary *)parameters. Calling this method from your view controller might look like this:
- (IBAction)handleButtonTap:(id)sender
{
NSDictionary *parameters = [self makeParametersFromTextFields];
[self.myViewModel sendRequest:parameters];
}
Notice again: no ReactiveCocoa code necessary in your ViewModel's API. That's not to say you shouldn't use RAC, only that the API of a ViewModel isn't necessarily dependent on signals or any ReactiveCocoa concepts – it's just a model object that is specifically intended to service a specific view in your application. Within the ViewModel's method implementations, you might be using signals all over the place, or maybe you're using some more imperative API like NSOperationQueues or something. It doesn't really matter, as long as your ViewModel exposes data to the view via KVO-observable properties (so that your view controller can bind to those properties, which would typically be done using ReactiveCocoa).
So what does your -sendRequest: method do with this dictionary of parameters? I have no idea. That part is up to you. If it gets a valid response, you should update some property on the ViewModel (for example, the tableData property from the code snippet above). If it gets a 401, it should update some other property on the ViewModel (for example, set the needCredentials property to YES). The view controller, having already bound itself to these properties, will react in whatever way you've configured.
"In my ViewModel, then, do I have two signals? One that returns the content, and another which tells the view layer that I need the credentials? How do I get the credentials back down to where the HTTP request happens?"
I hope by this point I've answered the question. The ViewModel doesn't need any signals. It just needs KVO-able properties. And as demonstrated above in the -handleButtonTap: method example, you don't need to do anything special to get the credentials down to where the HTTP request happens – just call a method on the ViewModel, and pass in whatever data makes sense. (Of course, the ViewModel would have to know which object to give the credentials to in order to kick off the HTTP request, and handle the response, but that part should be pretty academic.)
I am making an object that goes to download stuff for all of my view controllers. The object is singleton instance and has a callback method with received data once the download is completed. It also has a delegate property so that it knows which object to call back to after the download is done.
There are multiple controllers that use this shared instance, and my question is how to call back to the correct view controller that requested the download.
My approach is to use delegation, but the problem is that since other view controllers are also its delegate, the download object could call back to every object and this will be hard to track.
I've worked on projects where people have attempted to use multiple delegates and it's basically a bad idea. The delegate pattern is about a 1 to 1 relationship between a class and it's delegate. Whilst it is possible to achieve some level of multiple delegation through switching the delegates in and out, it's more likely to lead to unpredictable behaviour and bugs.
My recommendation would be to change how you are thinking about this. You have two options as I see it:
Switch to an Observer pattern where you can register multiple observers which your main class can interact with. This is useful where your observers all implement the same protocol and where your main class wants to be aware of the observers and interaction with them.
Broadcast NSNotifications to indicate state changes and events. Here is a more decoupled approach because the main class does not need to know who is listening and does not directly interact with them. Other can start and stop being notified at their leisure. It also has the advantage that you do not need to create or implement a separate protocol. Instead you register the classes that need to know about changes with the NSNotificationCenter which in turns handles all the routing of notifications for you.
It actually sounds like the delegate pattern might not be the best approach here.
I would look into NSNotificationCenter instead.
The basic idea is that your singleton doing the net connection posts a notification (with something like postNotificationName:object:userInfo:) , saying that new data is available. Within this notification, you can pass a dictionary object (userInfo) that holds the data you've fetched, or info on what parts of your Model contain updated data.
Then, your other view controllers can register themselves to 'observe' these notifications by calling addObserver:selector:name:object:. Generally speaking, when a vc becomes visible I call addObserver, and removeObserver when it's being hidden or transitioned out.
Good luck!
Delegation doesn't seem like the right solution to this problem. How about requiring the requesting view controller to provide an object (its self) and a selector for you to call as a completion notification? Of course, you'll need a place to store that object and selector until the download completes. Hopefully you have (or could create) an object for this.
i recommend to use one of these ways
observer:
when use data that you want to inform other object are near to primitive ones.for example when you are using 'NSMutableArray' you can not inform the change in one of object by the standard implemented pattern at least you need to implement one for your self that is not reusable that much
Notification
when your interaction with destination object (those need to be inform) is in one-way.it means you don't need any acknowledge or other data back from them.
delegate
when there is one object to inform at each time step.
note:block use for success and fail is not a pattern to broadcast data its about to queue task when you don't know when they are finishing or failing like network operations
EDIT:
how to create notification | multi delegate issues and implementation
While I agree with most of the answers here, if you did actually want to achieve multiple delegates you could potentially declare an array of delegates and send messages to all delegates within that array. If your protocol has optional delegate methods you safely check using responds(to aSelector: Selector!) -> Bool before invoking (being mindful of memory management, as those delegates would be strongly referenced in the array). Again I do agree that multiple delegates is likely a bad architectural idea and using blocks or notification center would suit your needs better.
One approach, which works for me if you only have one other object to forward messages to is to create a forwardingDelegate This does not end up with issues of hard to debug ordering of delegates and it does not unnecessarily create a dependency on the other object. Keep in mind, if you have many objects then this might not be the best approach, it is mainly for one additional object but this could be extended to support an array of objects so long as there is one that receives the SDK and forwards it to the other objects [1]. Note that every method that is needed for the forwarded object needs to pass it along, even if it is not used by the forwarding object.
For example, if I need to forward the messages coming from the mapView delegate:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
// handle this object here.
if ([self.forwardingDelegate respondsToSelector:#selector(mapView:regionDidChangeAnimated:)])
{
[self.forwardingDelegate mapView:mapView regionDidChangeAnimated:animated];
}
// or handle this object here.
}
[self.forwardingDelegate mapView:mapView regionDidChangeAnimated:animated];
The forwarding property would be declared like this.
#property (nonatomic) id<MKMapViewDelegate> forwardingDelegate;
And the other object would adopt the protocol as if it were receiving the original message.
[1] The array approach for multiple delegates may get tricky because then you don't have as much control over what order the delegates get called, as was mentioned in other posts.
Overview
I have a iOS project that uses core data
The core data is used by view controllers as well as for notifications
Implementation
Created a singleton class for database activities called DatabaseEngine
In the appDelegate didFinishLaunchingWithOptions, DatabaseEngine is instantiated
DatabaseEngine contains properties (delegate) for the view controller and for notifications
In the viewDidLoad of the view controller I am setting the DatabaseEngine delegate to the view controller instance
Once the database is opened, the completion handler (through the delegate properties) calls the methods to setup the view controller and notifications
Concern (Timing issue)
I am concerned there might be scenario (a timing issue), where the DatabaseEngine is created first and at that moment the view controller's viewDidLoad would not be executed, and therefore the DatabaseEngine delegate would not initialized, therefore the database would execute the completionHandler but since the delegate is nil, no tasks would be done
What I have done to address the concern
Inside the view controller's viewDidLoad, I am checking if the Database is up and if the view controller is not loaded, if yes then i execute the tasks (setting up the views of the view controller) again.
Note- I am NOT using threads explicitly but based on my understanding completionHandler is executed asynchronously.
Question
I have tried it several times, and the view controller data is loaded correctly and there seems to be no timing issue. I even tried looping through a large value(to create a delay) and still there is no timing issue. I wonder why ?
Is my implementation a good design or is there a better way to do this ?
Is that the correct way to address my concern ?
Your design is a bit convoluted, but seems solid. (I prefer to have core data managed by the app delegate, but your approach is just as fine if you prefer it.)
I would, however, use the usual pattern of lazy initialization of your DatabaseEngine class. In this way, when it is needed and really does not exist, it will create itself and do the necessary initialization routines while the view controller will wait until the call to the engine returns something.
// in view controller viewDidLoad, e.g.
self.managedObjectContext = [databaseEngine managedObjectContext];
If the context is not initialized, it will happen here.
I think the best approach too is to have your app delegate manage the data. Seems like the best approach, and it is what a default CD application template does.
I would look into using MagicalRecord, which is pretty amazing if you ask me. With MagicalRecord you just call [NSManagedObjectContext MR_defaultContext]; and you get the default context just like that. MR also has amazing class methods for free like
NSArray *array = [SomeObject findAll]
which returns an array with all your CD objects. You can even set predicates, etc. and it's quite fast.