Separating delegates from ViewController to own classes - iphone

I'm pretty new to iOs development and I've become a bit confused how should I separate my code to avoid a huge ViewController.m-file. Currently my main viewcontroller has quite a many delegates:
#interface ViewController : UIViewController <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
I would like to reduce the viewController code and I guess I should create separate classes to handle delegate tasks. The problem is that if I for example create singleton class for CLLocationManager and let it implement delegate methods, how do I then communicate with the view controller?
Let's say that my CLLocationManager receives a location update, how do I tell the viewController to make changes to the UI? Should I use NSNotificationCenter to post a notification and then add observer to the view controller? Or is the best way just to let viewController implement all delegate methods as it is now?

Move some of that functionality into your data model.
It's hard to say how you should manage this given the limited information you've provided, but one has to wonder whether a single view controller should really be managing a map view and keeping track of location and managing a HTTP connection and managing a Core Data fetch. Why not move some of that into your model, where it'll be somewhat easier to divide into modules?
Let's say that my CLLocationManager receives a location update, how do
I tell the viewController to make changes to the UI? Should I use
NSNotificationCenter to post a notification and then add observer to
the view controller?
A notification would be a good solution -- it provides a way for the part of your program that manages location (again, this probably belongs in the model) to communicate the change without having to know anything in particular about the parts of the program that care about changes to location. It also allows one-to-many communication -- if you have another view controller that also cares about location, it can observe the same notification.
Or is the best way just to let viewController implement all delegate methods as it is now?
I try to think about dividing responsibilities appropriately more than limiting the size of the code. If you have a class that does one job but needs a lot of code to do it, that's fine. If you have one class that manages many unrelated things, that's not so good. The trouble is that a lot of jobs seem to fall into the traditional "manages a screenful of content" role of a view controller. Try to separate the task of managing the presentation of the data (which is the view controller's rightful job) from managing the data itself (which is the model's job).

Implement a class responsible for delegate methods:
#interface DelegateManager : NSObject <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
-(id)initWithViewController:(ViewController*)vc;
#property (weak) ViewController *delegate;
#end
In your ViewController:
#interface ViewController : UIViewController
-(void)doSomething;
#end
In your ViewController, create an instance of DelegateManager with self as parameter. Set all your delegates' target to your DelegateManager. In the delegate methods of your DelegateManager, call [self.delegate doSomething]; to communicate back to your ViewController.

Related

best way to use CoreLocation across multiple views

I have two views in my app, one is a general view where CoreLocation works away calculating the users location while the user is doing other stuff in the view. The second view is accessed by the user when they touch a button allowing them to locate themselves more accurately using a mapview and MapKit, i would like the mapview in this view to show the location that CoreLocation has already identified in the first view AND to continue displaying this location based on updates from CoreLocation in the other view.
Is the best way here to create a singleton that encapsulates the CoreLocation stuff and have this referenced in the view with the map, or to use notifications ? or to use some other better practice for my scenario ?
Thanks
I have a couple of apps that use CoreLocation in multiple places. From what I've read, you definitely want there to be just one instance of CLLocationManager. It's worked great as a singleton for me.
Hope this helps!
If I were you, I would do it this way:
Decide which view is going to be always loaded.
I assume, you want CalculatingView is loaded all the time, and MapView will be loaded/unloaded based on the user action.
Allocate and initialize a pointer to CLLocationManager inside CalculatingView. This will provide location property and also call delegate messages. Since the CalculatingView is loaded and retained, this pointer is always working too.
Set CLLocationManager's delegate to be CalculatingView, which might also be called self, if this view has allocated and initialized CLLocationManager pointer.
Implement delegate methods of CLLocationManager, in CalculatingView
If you like to, you can have MapView to be allocated and initialized within CalculatingView. But it's ok to have it in other places, as long as you can send message to MapView. Make sure they are valid by checking if it's not nil or if it respondsToSelector.
When the CLLocationManager's delegate, which is CalculatingView receives messages, send a message to MapView.
It's like relaying messages, but the messages that MapView should respond to don't have to be the same messages sent to CalculatingView like delegate method calls from CLLocationManager
By checking if MapView is valid, meaning if it's loaded to be displayed, you can decide to send messages to MapView or not
The essence is to decide which view is loaded consitently, to use delegate methods for sending(or relaying) messages to other pointers(in this cases, MapView pointer).
The singleton is good, but unless you are going to use CLLocationManager from multiple places, like more than 3~4 places, it's not that necessary, I think
Hope I didn't confuse you. Based on what you posted, it seems like this way can be simple solution for your goal. If I didn't catch your true intention, please let me know.
I am not sure this is the best way, but I've been setting up my main controller (the one that is loaded first) as a location manager delegate. When the location updates it fires off a notification with the new location as the notification object. Any controllers listening can then use that data however they need it.
As an aside, Apple's LocateMe app instantiates the location manager three times. So, by their example, having multiple LocationManagers might not be a problem.
From what I've read, best practice for this is to add CLLocationManager to your App Delegate as you can access it from any view.
Short sample code to put in your view where you need the CLLocationManager
....imports....
#implementation YourViewController
- (void)viewDidLoad {
self.myLocationManager = [[UIApplication sharedApplication] delegate].yourLocationManagerVarName;
}
#end
Hop that helps.
Maybe you should consider a MVC oriented approach. From your description your are missing a model layer representation of your user. Defining a simple User class with a basic CLLocation property would be a first step.
#interface User {}
#property (nonatomic, retain) CLLocation *location;
#end
#implementation User
#synthesize location;
- (void)dealloc {
self.location = nil;
[super dealloc];
}
#end
The same instance of the User will be passed to your view controller. It may be created in the app delegate.
Next create location services object for your app. It will start the CLLocationManager, and give the location to your user. You may have to set the GPS accuracy, ignore frames you don't want, and implement basic LBS logic here.
At this point, you have a feature full app, without any UI. This is a good design in the way it can be reused and tested.
Now stack your UI on top of that. Give your root controller a pointer to the User instance in your app delegate. Your view controller pass this pointer to modals / navigations view controllers it creates.
This controller start observing User's location changes in their viewDidLoad and react accordingly.
- (void)viewDidLoad {
[self observeValueForKeyPath:#"location" ofObject:self.user change:0 context:NULL];
}
Your view controller would also register for notification raised by your location services objects to display an alert to the user.
Based on other answers:
there is no real penalty to create multiple CLLocationManager instances in your code. The only side effect is that the api is asynchronous, thus you have to wait to get a valid location in your view controller. You can try to get the current location from the location manager on your viewDidLoad using locationManager.location API.
don't share stuff from your app delegate. This prevent code reuse. What if you reuse your views and you app delegate don't have a location manager ?
if you need more code, please ask.

How do runtime-created sub views communicate with the view controller?

In my iPhone project I have a UIViewController in which I add an instance of a subclass of UIView. From this instance I need to communicate back an integer to my view controller. My current solution is to send a message from my instance to my App Delegate which sends a message to the view controller. It works, but it feels a bit messy.
Is there anyway I can send a message straight back to the view controller?
I would love to be able to do something like [super doSomething:123];
Any ideas?
Thanks
This is the kind of thing that NSNotificationCenter was provided for. Once you get handy with sending and receiving notifications, your message-passing gets a WHOLE lot simpler.
One of the classic things people confront is how to get a pointer to the object they want, in order to tell it about something. How do I, for instance, tell the ViewController two slots back up the UINavigationController stack that the user just changed this data field? So you dig into the stack, offset back by some magic number of elements in the stack, build public setters on the fields you want talk to... It's super cumbersome.
Compared to registering as a notification receiver in one place, and then firing a notification in some complete other place when the data changes. It's kind of magical, after doing all the "dig through the view hierarchy" work.
Um, I'm not sure I understand your problem correctly. You have a class derived from UIView which needs to send a message to another class derived from a UIViewController. It sounds like you are creating the UIView instance programmatically. Is there any reason my you could not have a property on the UIView which refers to the UIVIewController and just use that to send it a message directly.
You cannot use [super ...] because the super of your UIView derived class would be UIView.
Or am I miss-understanding the issue :-)
If I understand correctly, you want to send a message from your subclass of UIView to the view controller.
That means your subclass of UIView needs to have a property or ivar which is the view controller. The easiest way to do this is to add it as an outlet and connect it to the view controller in the nib file.
Generally you should not go via the app delegate. Having a typed pointer link is also less than ideal.
The optimal way of communicating - Apple does it like this as well - is to create a delegate protocol. When creating the view controller you pass a pointer to the delegate as id . Then when it gets to sending the message you ask the delegate:
if ([delegate respondsToSelector(didFinishSomething:)])
{
[delegate didFinishSomething:info_to_pass];
}
If you want to be extra-sophisticated then you can also add a pointer to the calling class instance. Like:
[delegate myViewController:self didFinishSomething:info_to_pass];
This way you always know what kind of class the message is coming from.
If there is more than one place that needs to be notified of a change, then instead of delegation you will use notifications.
In my iPhone project I have a
UIViewController in which I add an
instance of a subclass of UIView.
This implies that you have both a reference to the instance of the UIView subclass and the UIViewController in the same scope. I.e. something equivalent to:
UIViewControllerSubclass *myViewController;
UIViewSubclass *myView;
(It doesn't matter if they are actually instance variables or, even, globals)
And once those two variables are initialized, somewhere you do something like:
myViewController.view = myView;
In your UIViewSubclass, add a property that points back to your UIViewControllerSubclass:
#property(assign) UIViewControllerSubclass *myController;
Then, when you do the above assignment, add:
myView.myController = myViewController;
From there, messaging your controller from your view is easy:
[self.myController yoManHereIsAnInt: 42];
Note that I used assign instead of retain because the controller already retains the view. If the view were to also retain the controller, you would have a cycle that would eventually lead to a leak.
No super about it. super is entirely related to the inheritance hierarchy of your Objective-C classes. What you are asking has nothing to do with inheritance and everything to do with how the various instances of objects in your application are connected together.
Simply add an outlet to your UIView subclass, connect it to its view controller in Interface Builder, and call your method on that. Here’s how that might look:
MyUIView.h:
#interface MyUIView : UIView
{
UIViewController *viewController;
}
#property (assign) IBOutlet UIViewController *viewController;
#end
MyUIView.m:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self viewController] setTitle:#"Hello from MyUIView"];
}

Using Interface Builder efficiently

I am new to iPhone and objective c. I have spent hours and hours and hours reading documents and trying to understand how things work. I have RTFM or at least am in the process.
My main problem is that I want to understand how to specify where an event gets passed to and the only way I have been able to do it is by specifying delegates but I am certain there is an easier/quicker way in IB.
So, an example.
Lets say I have 20 different views and view controllers and one MyAppDelegate.
I want to be able to build all of these different Xib files in IB and add however many buttons and text fields and whatever and then specify that they all produce some event in the MyAppDelegate object. To do this I added a MyAppDelegate object in each view controller in IB's list view. Then I created an IBAction method in MyAppDelegate in XCode and went back to IB and linked all of the events to the MyAppDelegate object in each Xib file.
However when I tried running it it just crashed with a bad read exception.
My guess is that each Xib file is putting a MyAppDelegate object pointer that has nothing to do with the eventual MyAppDelegate adress that will actually be created at runtime.
So my question is...how can I do this?!!!
If you create an instance of MyAppDelegate in each nib file then, yes, you do end up with a lot of different instances of the class when all the nibs load. The app delegate is not identified by class or even protocol but rather by being the object pointed to by the application instance's delegate property. To find the true app delegate, you have have to ask the application object itself for its delegate
You should have all your view controllers descend from a parent view controller class that has an appDelegate property. Implement something like this:
#import "MyAppDelegateClass.h"
#interface ViewControllerBaseClass :UIViewController {
MyAppDelegateClass *appDelegate;
}
#property(nonatomic, retain) *appDelegate;
#end
#implementation ViewControllerBaseClass
#synthesize appDelegate;
-(MyAppDelegateClass *) appDelegate{
self.appDelegate=(MyAppDelegateClass *)[[UIApplication sharedInstance] delegate];
return appDelegate;
}
#end
When the view controller needs the app delegate it just calls self.appDelegate. If you want to access an attribute of the app delegate use self.appDelegate.attributeName.
The important thing is that you ask the application for its specific delegate instance at runtime. You can't do that from a nib file.
I'm not entirely clear what exactly you're trying to do, but it's probably a bad idea. There should only be one app delegate per application, and it should deal with behavior for the whole application. Typically, the app delegate initializes the root view controller(s) and displays them, but not much else (other than handling things like opening and saving data sources).
The view controllers (subclasses of UIViewController) should interact with the XIBs. Having the view-specific behavior in the view controllers makes the app much easier to manage and maintain. Typically, there should be 0 or 1 XIBs per view controller (more than that is complicated). You set up the interaction with the views using the Target/Action pattern with IBOutlets and IBActions (see here for a complete guide). It's generally a bad idea to make view controllers or XIBs dependent on the app delegate (since reducing dependencies again makes the code easier to manage).
In general you should be making a view controller for each of the views you are building, and link events to those view controllers - not the app delegate. In fact usually no event ever is wired to the app delegate from any nib file, even in the sample projects you'll note that view controllers are created and held onto by the app delegate, but it does not receive events.

Delegates Vs. Notifications in iPhoneOS

I am trying to call a method in my root view controller from a child view controller such that when I change my options they will automatically update the root view, which will in turn update several other view controllers. For the second part I have used notifications, but for this first I am trying to use a delegate because it (so I have been lead to believe) is a good programming practice. I am having trouble making it work and know that I can set up another notification easily to do the job. Should I continue trying to implement the delegate or just use a notification?
Delegating is a good programming practice for many situations but that doesn't mean you have to use it if you're not comfortable with it. Both delegating and notifications help decouple the view controllers from each other, which is a good thing. Notifications might be a little easier to code and offer the advantage that multiple objects can observe one notification. With delegates, such a thing cannot be done without modifying the delegating object (and is unusual).
Some advantages of delegating:
The connection between delegating object and delegate is made clearer, especially if implementing the delegate is mandatory.
If more than one type of message has to be passed from delegatee to delegate, delegating can make this clearer by specifying one delegate method per message. For notifications, you can use multiple notification names but all notifications end up in the same method on the side of the observer (possibly requiring a nasty switch statement).
Only you can decide what pattern is more appropriate for you. In any case, you should consider not having your view controller send the notification or the delegate message. In many cases, the view controller should change the model and then the model should inform its observers or its delegate that it has been changed.
Implementing a delegate pattern is simple:
In your ChildViewController.h, declare the delegate protocol that the delegate must implement later:
#protocol ChildViewControllerDelegate <NSObject>
#optional
- (void)viewControllerDidChange:(ChildViewController *)controller;
#end
At the top of the file, create an instance variable to hold the pointer to the delegate in your ChildViewController:
#protocol ChildViewControllerDelegate;
#interface ChildViewController : UIViewController {
id <ChildViewControllerDelegate> delegate;
...
}
#property (assign) id <ChildViewControllerDelegate> delegate;
...
#end
In RootViewController.h, make your class conform to the delegate protocol:
#interface RootViewController : UIViewController <ChildViewControllerDelegate> {
...
In the RootViewController implementation, implement the delegate method. Also, when you create the ChildViewController instance, you have to assign the delegate.
#implement RootViewController
...
// in some method:
ChildViewController *controller = [[ChildViewController alloc] initWithNibName:...
controller.delegate = self;
...
- (void)viewControllerDidChange:(ChildViewController *)controller {
NSLog(#"Delegate method was called.");
}
...
In the ChildViewController implementation, call the delegate method at the appropriate time:
#implementation ChildViewController
...
// in some method:
if ([self.delegate respondsToSelector:#selector(viewControllerDidChange:)]) {
[self.delegate viewControllerDidChange:self];
}
...
That's it. (Note: I have written this from memory so there are probably some typos/bugs in it.)
I would like to add:
objects receiving notifications can
react only after the event has
occurred. This is a significant
difference from delegation. The
delegate is given a chance to reject
or modify the operation proposed by
the delegating object. Observing
objects, on the other hand, cannot
directly affect an impending
operation.
Typically, if you need to update the UI based on a change to data in a model, you would have the view controllers observe the relevant model data and update their views when notified of changes.
I see delegation as a bit more formal and like the distinction that Peter Hosey shared recently:
The difference is that delegation is
for to-one (and bidirectional)
communication, whereas notifications
are for to-many, unidirectional
communication.
Also, I have found that (completely) updating the view in viewWillAppear: works fine (but this is not the best solution where performance is a concern).
Notifications can make the runtime behavior of your program significantly more complex. Think of it like a goto with multiple destinations. The order of those destinations is not defined. If you ever crash there is little stack trace information.
There are cases when it makes sense to use notifications--the typical one being to communicate a model change or a global state change to your views. Example, the network is down, the application will resign, etc!
It is worthwhile to learn the delegate pattern in iOS. Delegates give you complete stack traces when you debug. They result in significantly simpler runtime behavior while still achieving the goal of decoupling your objects.
Delegates are a little hard to get used to, but I think it's the best practice and, like Apple, they just work.
I always use the formal protocol declaration. It's a bit more logical in my mind, and it's very clear in the code. I suggest using a UIView to change your options instead of a controller. I always use one main controller and have a lot of subclassed UIViews that the one controller can control. (However, you can modify the following code for a controller, if you really need a controller instead of a normal view.) In the header file of the child view, make it look like this:
// ChildView.h
#import <UIKit/UIKit.h>
#protocol ChildViewDelegate; // tells the compiler that there will be a protocol definition later
#interface ChildViewController : UIView {
id <ChildViewDelegate> delegate;
// more stuff
}
// properties and class/instance methods
#end
#protocol ChildViewDelegate // this is the formal definition
- (void)childView:(ChildView *)c willDismissWithButtonIndex:(NSInteger)i; // change the part after (ChildView *)c to reflect the chosen options
#end
The method between #protocol and the second #end can be called somewhere in the implementation of the ChildView, and then your root view controller can be the delegate that receives the 'notification.'
The .m file should be like this:
// ChildView.m
#import "ChildView.h"
#implementation ChildView
- (id)initWithDelegate:(id<ChildViewDelegate>)del { // make this whatever you want
if (self = [super initWithFrame:CGRect(0, 0, 50, 50)]) { // if frame is a parameter for the init method, you can make that here, your choice
delegate = del; // this defines what class listens to the 'notification'
}
return self;
}
// other methods
// example: a method that will remove the subview
- (void)dismiss {
// tell the delegate (listener) that you're about to dismiss this view
[delegate childView:self willDismissWithButtonIndex:3];
[self removeFromSuperView];
}
#end
Then the root view controller's .h file would include the following code:
// RootViewController.h
#import "ChildView.h"
#interface RootViewController : UIViewController <ChildViewDelegate> {
// stuff
}
// stuff
#end
And the implementation file will implement the method defined in the protocol in ChildView.h, because it will run when the ChildView calls for it to be run. In that method, put the stuff that happens when you'd get the notification.
In this case, you don't need to use either delegation or notification because you don't really need to communicate directly between your views. As gerry3 said, you need to change the data model itself and then let all other views respond to that change.
Your data model should be an independent object that all your view controllers have access to . (The lazy way is to park it as an attribute of the app delegate.) When the user makes a change in View A, View A's controller writes that change to the data model. Then whenever Views B through Z open up, their controllers read the data model and configure the views appropriately.
This way, the neither the views, nor their controllers need to be aware of each other and all changes occur in one central object so they are easily tracked.
Is it really necessary for your root view controller to know about the changes, or just the subviews?
If the root controller does not have to know, having the settings send out the notifications the other views are looking for seems like a better answer to me, as it simplifies code. There is no need to introduce more complexity than you have to.

Architecting a multiview application on the iPhone

I have an app with a root view controller, a primary view controller, and a secondary view controller. I would like to be able to send a message to the primary view controller from the secondary view controller. How can I get a reference to the primary so that I can send messages to it? Is there a better way to architect this?
The short answer: you can get back to your application delegate like this:
YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
You likely already have a pointer to your root view controller in your application delegate class. And you probably have pointers to your primary and secondary view controllers in the root controller object. So, you could write code like this:
SecondaryViewController *primary = delegate.rootController.primaryController;
You can then send messages to it to your heart's content. No outlets required; just properties to each view controller.
There are many longer answers and also a discussion about why this practice might be questionable since it introduces potentially unwanted linkages between objects. In a "pure" object oriented design, you'll follow a clean design pattern with clear linkages between objects in various directions allowing you to better reuse the code.
Another option is to pass in pointers to the objects the class will need at initialization time. Implement a new initWithSomethingOrOther for your view controller classes and pass objects in as parameters. Cache these pointers you need (don't forget to retain them) for later use.
The clean way to do it is to define a protocol for a delegate for the secondary controller which lists the methods it needs the primary controller to provide:
#protocol SecondaryControllerDelegate <NSObject>
- (void)secondaryController:(SecondaryController*)secondaryController
frobFooWithBar:(Bar*)myBar;
- (BOOL)secondaryController:(SecondaryController*)secondaryController
shouldTwiddleBaz:(Baz*)currentBaz;
#end
Now add a delegate property to the SecondaryController:
#interface SecondaryController : UIViewController {
id <SecondaryControllerDelegate> delegate;
...
}
// delegates are one of the few places you don't retain an object
#property (assign) id <SecondaryControllerDelegate> delegate;
...
In SecondaryController's implementation section, synthesize the delegate property. (Do not release it in the destructor.) When SecondaryController needs to communicate with the PrimaryController, it should call the appropriate method on the delegate.
Now make your PrimaryController implement the SecondaryControllerDelegate protocol:
#interface PrimaryController : UIViewController <SecondaryControllerDelegate> {
...
Implement the delegate methods in PrimaryController.
Finally, have your PrimaryController set itself as the SecondaryController's delegate. Exactly how you do this will depend on whether you create SecondaryController in a nib or not. If you do, make the connection there; if not, make it just after you allocate and init the SecondaryController.
Why do you do this song and dance? Well, when you have to introduce another controller between the Primary and Secondary, or use the Secondary elsewhere in the app, or even use the Secondary in another app (I have one controller that gets used in three of my four apps), you don't have to change SecondaryController at all; you just change whatever class should now be its delegate. This is an incredible time saver in the long run.
If the controllers are loaded from a NIB, you could define an outlet on the secondary controller and connect it to the primary controller in interface builder.
Use NSNotificationCenter for decoupled communication between objects.