best way to use CoreLocation across multiple views - iphone

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.

Related

Separating delegates from ViewController to own classes

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.

Safely call method of later UIViewController from GCD of object init

Thanks to Lock a NSMutableArray of an object but not the rest of my object when using GCD I have a user object that populates an array of locations via gcd at init in the hope that by the time we modally get to LocationViewController the locations are present in user.locations (where user is passed along the viewcontrollers), if they aren't there then I simply display 'loading...' in the picker.
I would ideally like to force a refresh from within the gcd of user in the dispatch_sync method. I can added a method to LocationViewController that will refresh the picker etc but I'm not sure how to then safely access this.
My first thought was in LocationViewController if the locations aren't there (i.e. nil) then to set a reference to this LocationViewController in user. Within the dispatch_sync I could then call the method [locCont mymethod] if locCont isnt nil. But I'm not sure how to set up the property in the user class?
#property (strong, nonatomic) LocationsViewController * locCont;
What worries me is a user can at anypoint logout and return to the root view. I'll then set
user.locCont = nil
will ARC sort any hungover memory nicely?
My other concern is what will happen if they don't choose to set a location and are on a later view. I guess I could handle that by setting user.locCont to nil in a prepareforsegue.
Is there a better way to get the LocationsViewController to refresh if the user has got there?
I would suggest to use notifications to avoid a strong connection between the "User" object and a view controller.
The LocationViewController would register for a custom notification in its init method
and unregister in dealloc.
The GCD code "posts" the notification when it is finished. That causes all listeners
to be notified, so that the LocationViewController can refresh its view.
See the NSNotificationCenter documentation, and
Send and receive messages through NSNotificationCenter in Objective-C? for good sample code that should help for a start.
If you have retrieval of data happening asynchronously by some user object, and you want view controllers to get access to this object, you would generally maintain some external reference to this user object. One common approach would be to make this user object a singleton. Another would be to make it a property of the app delegate. Either way, the user object probably would not maintain a reference to any view controllers. The user object would just be in the business of retrieving the information asynchronously, view controllers could access the current state of the user object through either the singleton or the app delegate.
If you want the user object to be able to inform view controllers when the data retrieval is done, you'd probably have it post a notification via the notification center. View controllers could register themselves to observe those notifications (and, before a view controller dismisses itself, remove itself as an observer of that notification).

Using the AppDelegate to share data

I've found a couple of sources that explain how to use the AppDelegate to share data between objects in an iOS application. I've implemented it quite painlessly and it looks like a good approach in my situation. Thinking about what could be done using the AppDelegate, I am wondering where the line should be drawn.
Obviously there are other ways to share data across view controllers, use Singleton objects, and NSUserDefaults. When is it appropriate to use the AppDelegate to share data? In my current situation, I use this approach to store the appleDeviceToken used for push notifications. I use that token when users login or logout of the app.
In MyAppDelegate.h I declare the property:
#property (nonatomic, retain) NSString *appleDeviceToken;
In MyAppDelegate.m I synthesize appleDeviceToken and then set it:
#synthesize appleDeviceToken;
------------------------------------------------------
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *devToken = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
appleDeviceToken = devToken;
}
Then, in my LoginViewController.m I retrieve it and post it to my server:
NSString *urlForDeviceTokenPost = [NSString stringWithFormat: #"/api/users/%i/appleDeviceToken", userId];
MyAppDelegate *appDelegate = (MyAppDelegate*) [UIApplication sharedApplication].delegate;
NSString *appleDeviceTokenStr = appDelegate.appleDeviceToken;
AppleDeviceToken *appleDeviceToken = [[AppleDeviceToken alloc] init];
appleDeviceToken.deviceToken = appleDeviceTokenStr;
[[RKObjectManager sharedManager] postObject:appleDeviceToken delegate:self];
This works great so far, but is this the ideal approach? What else should I know?
When the data and objects are truly global and/or cannot be pushed further down the graph. Storage at this high level is usually not required. As well, your implementations should usually have little to no knowledge about the app delegate -- What's worse than a Singleton? The God-Singleton :) If the app delegate is complex, something's gone wrong. If the app delegate's interface is visible (via #import) to many of your implementations or they message it directly, then something is wrong.
There is no need for an (idiomatic ObjC) singleton -- there is one instance of the app delegate.
NSUserDefaults is for persistence of small values (as the name implies) -- the ability to share is a side effect.
Since the data is already sent into the app delegate by UIKit in this case, that may be a fine place to store the data, or an object representation of. You might also consider forwarding those messages to an appropriate handler. The important point -- In most cases, you would want initialization to flow down the object graph, and to flow from the lowest points possible (e.g. as opposed to many objects referring back to the app delegate). So you might see the app delegate set a top-level view controller's model, but the view controller can then set the models of the view controllers it pushes. This way you will reduce dependencies and control flow, cause and effect will be easier to trace, and you will be able to test it more easily -- free of the context of a massive global state.
The following line always indicates that you've done something wrong:
MyAppDelegate *appDelegate = (MyAppDelegate*) [UIApplication sharedApplication].delegate;
The application delegate is the delegate of the UIApplication. It is called that for a reason. It is not called the ApplicationDataStore or even the ApplicationCoordinator. The fact that you're asking the application for its delegate and then treating it as something other than id<UIApplicationDelegate> means that you've asked it to do something it isn't tasked with doing. It's tasked with managing the things UIApplication needs (which doesn't mean "everything the 'app' needs).
It appears that you've already built a place to store this information: RKObjectManager. I would have the app delegate pass the token there, and I'd have the login view controller just note that it is time to push it. I wouldn't even put the string #"/api/users/%i/appleDeviceToken" in the view controller. That's not related to displaying the view. That's something for you networking stack (which you seem to have housed in RKObjectManager). "ViewController" means "controller for helping display the view" not "processor of the operation that the view represents."
That seems like an appropriate use. The application delegate is tempting to misuse because it's an easily-accessible object that's already present in every app. It has a real purpose, though, which is, as its title indicates, to make decisions for the application object, just as a table view delegate does for its table view object.
In this case, you're storing information that was passed to the delegate from the application itself. I'd say that's where the line is drawn.
Storing this token seems to be in accordance with the app delegate's purpose, unless you had another controller object which was focussed on dealing with remote notifications. In that case, you would probably just pass the token right on to that controller.
I'm more pragmatic. Since the appDelegate in my app knows how the tabBarController was populated, and all the navigation controllers, I have several methods there that let some arbitrary class communicate with some other class - usually these are single instances of some class (but not singletons). That said, if what you want to put there does not have a compelling reason to be in the appDelegate, well, it probably doesn't belong there!

IPhone Catch any application interaction

Im just wondering if there is any method of catching any user interaction with the application.
The reason i ask is that when a user interacts with the app i update a date in the db. if they date is older than 10 minutes they are seen as offline by other users until they comeback and interact with the program.
Does anybody have any ideas on how i could catch any user interaction to update this field?
Thanks
p.s. it is a navigation app. so even if i can add a handler to the navigation controller to say when a page is changed that might do???
The most convenient way would probably be to subclass UIApplication and override the sendAction:to:from:forEvent: method. You will also need to add a key to your Info.plist file called “NSPrincipalClass” with a string value containing the name of your subclass, which tells UIKit to your subclass in place of UIApplication.
MyApplication.m:
- (BOOL)sendAction:(SEL)action to:(id)target
from:(id)sender forEvent:(UIEvent *)event
{
ResetInterationTimeout();
return [super sendAction:action to:target from:sender forEvent:event];
}
Info.plist:
<key>NSPrincipalClass</key>
<string>MyApplication</string>
If by interaction you mean 'touches' then you can easily do that by putting a UIView (if do not have a global one on top of the others) on each UIViewController in your NavigationController that has the duty of registering a touch, saving the current date to an higher level controller (even the AppDelegate might work). Then this controller can compare the date to the previous one ( you have to store it) and if the time difference overcomes your threshold you can fire your action by messaging the proper object.
Here is a guide from Apple on how to catch the touches (you will need to overrid the touchesBegan:WithEvent: method to get the first touch)

iPhone Delegates - How to use them correctly

I have 2 or 3 views in my iPhone application where I have various pieces of functionality that use delegates. In all cases the delegates are assigned to "self", responding specifically to that view's actions and interacting with instance variables.
However, if I do something that takes a bit of time with a delegate, and leave the view, obviously it crashes my app as the delegate methods get called on a view I've left.
Typically in my delegate methods I am doing things like interacting with IBOutlets, calling other instance methods, saving data to Core Data etc...
How can I work with delegates better? Is what I'm doing typical, or not?
Thanks for any guidance!
Depends on the use case. If, for example, you've got a UINavigationController that manages a ViewController that use something such as Location Services, when you pop the View Controller off of the stack you're going to want to set the CLLocationManager's delegate to nil. You can do this in the dealloc method.
Can you give a specific example of an issue you're facing?
I've encountered this situation once when dealing with MapKit (race conditions involving delegate callbacks and delegate deallocation). However, in general, I think it's an indication of a bad design decision when your delegate becomes invalidated as a result of race conditions, but I could be wrong.
Typically, the objects that make use of your delegate should exist within the context of the delegate itself. So, for example, the same class that contains various IBOutlets that you want to manage with delegate callbacks should also be the delegate of those IBOutlets. That way, when the class (i.e., the delegate) is deallocated, the IBOutlets are (hopefully) also deallocated, so they won't be making callbacks to anything.
bpapa's right. More generally, if you have a potentially lengthy delegate callback, either make sure that 1) the delegate is an object that won't be deallocated during the lifecycle of the delegator (e.g., UINavigationController managing UIViewControllers) or 2) the delegator's delegate object is set to nil during the delegate's deallocation.
... That last sentence was a mouthful. :)
Your delegates should be declared as
#property (nonatomic, assign) id <MyDelegateProtocol> delegate;
This ensures a weak reference so that when you dealloc the object which has delegate, it only removes the reference and NOT the corresponding object. using (strong) will cause a crash on dealloc.
When calling your delegate, you can check
if (self.delegate && [self.delegate respondsToSelector:#selector(MyDelegateProtocolCallbackMethodName:)] {
[self.delegate MyDelegateProtocolCallbackMethodName:result];]
}
Generally I use delegates for proxy classes which fetch data from a server or edit screens like changing the title of a task in a todo list or editing a model in a database.
The advantage of a delegate is a very clear use case. If you find that multiple parts of your app need to be aware of when an event happens (triggered on 1 screen but perhaps listened for in another), I suggest using NSNotificationCenter to raise notifications, instead of or perhaps in addition to sending a message to your delegate.