Web call in DidUpdateLocations while app is terminated - ios

As my user changes location, I need to compare his location with an array of locations I pull from the web. However, in my appDelegate, I'm not sure where exactly to place my code as I'm not sure what methods are called or not called when the app is terminated, but the CLLocationManager still works.
Specifically, I need to input this code where it will actually be called when the app is still terminated:
// alloc and init the various (Mutable)Array properties
self.locations = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
The _homeModel will then call this method:
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_locations = [items copy];
}
Which I will further edit to compare the user's location to the array of locations.
The thing is, this array of locations only changes once a week. Does the app really have to pull it from the web every time the user's location changes? Or is there a way to cache this and only pull it when self.locations has been deallocated?
This is what I have now, but I feel there must be a better way:
#interface AppDelegate () <CLLocationManagerDelegate>
{
DealsModel *_homeModel;
}
#property BOOL didRunBefore;
#property CLLocationManager *locationManager;
#property NSMutableArray *deals;
#end
#implementation AppDelegate
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_deals = [items copy];
[self compareSponsorLocations:_deals toUserLocation:[self.locationManager location]];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
if (!self.deals) {
// alloc and init the various (Mutable)Array properties
self.deals = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
} else {
[self compareSponsorLocations:self.deals toUserLocation:[locations lastObject]];
}
}
- (void) compareSponsorLocations: (NSArray *) array toUserLocation: (CLLocation *) location
{
for (Deal *deal in array) {
NSLog(#"%#", deal.name);
}
NSLog(#"%#", location.description);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...

To handle location updates when you app is in background or even terminated, you can use "Significant Location Changes". It will trigger application to start in background mode when location has been changed significantly. Then you can start a background task to perform the operations on your need.
Documentation:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW8
Easy way to simulate it and debug:
XCode / iOS simulator: Trigger significant location change manually

Related

CTCallCenter doesn't update the currentCalls property after I set callEventHandler

I'm trying to see if there are any ongoing calls, but I'm having trouble with keeping an instance of CTCallCenter as a property. Here's basically what I'm doing now that I'm debugging (everything is in MyClass):
-(void)checkForCurrentCalls
{
CTCallCenter *newCenter = [[CTCallCenter alloc] init];
if (newCenter.currentCalls != nil)
{
NSLog(#"completely new call center says calls in progress:");
for (CTCall* call in newCenter.currentCalls) {
NSLog(call.callState);
}
}
if (self.callCenter.currentCalls != nil) {
NSLog(#"property-call center says calls in progress:");
for (CTCall* call in self.callCenter.currentCalls) {
NSLog(call.callState);
}
}
}
My self.callCenter is a #property (nonatomic, strong) CTCallCenter *callCenter;. It has synthesized setter and getter. It's initialized in MyClass' init method:
- (id)init
{
self = [super init];
if (self) {
self.callCenter = [[CTCallCenter alloc] init];
}
return self;
}
If I call another of my methods before checkForCurrentCalls, then my self.callCenter.currentCalls stops updating the way it should. More precisely, the phonecalls I make (to myself) just keep piling on, so that if I've dialed and hung up three phonecalls I get printouts of three CTCalls being in the "dialing" state. The newCenter works as expected.
All I have to do to break it is call this:
- (void)trackCallStateChanges
{
self.callCenter.callEventHandler = ^(CTCall *call)
{
};
}
I have come across answers that say that CTCallCenter has to be alloc-init'd on the main queue. I have since then taken care to only call my own init on the main queue, using dispatch_async from my app delegate:
dispatch_async(dispatch_get_main_queue(), ^{
self.myClass = [[MyClass alloc] init];
[self.myClass trackCallStateChanges];
}
My checkForCurrentCalls are later called in applicationWillEnterForeground.
I don't understand why just setting the callEventHandler-block breaks it.
Everything is tested on iphone 5 ios 7.1.2.
This has been reproduced in a new project. Filed a bug report on it.

Trying to implement Key-Value Observing for first time, get some error

I have trouble implementing a Key-Value Observer at my attempt to follow the MVC pattern. I have a controller class, a model class and a view class. I update my model from the controller class and I want to put a key value observer in my view class to monitor when a NSMutableArray changes in model (like through addObject) and then redraw itself automatically. I used answer in this thread to guide me: How to add observer on NSMutableArray?
Code so far:
From my Scene (using sprite kit if it matters). Setting of letters will be done from Ctrl class, this is just to test.
BarCtrl *barCtrl = [[BarCtrl alloc] init];
BarModel *barModel = [[BarModel alloc] init];
BarView *barView = [[BarView alloc] init];
barCtrl.barModel = barModel;
barCtrl.barView = barView;
barView.barModel = barModel;
ScrabbleDeck *sd = [[ScrabbleDeck alloc] init];
if([barModel addLetter:[sd getLetter] onSide:BarModelSideRight])
NSLog(#"Added letter");
BarModel.h
#import <Foundation/Foundation.h>
#import "Letter.h"
typedef NS_ENUM(int, BarModelSide) {
BarModelSideLeft,
BarModelSideRight
};
#interface BarModel : NSObject
#property (nonatomic, strong) NSMutableArray *addedLetters;
- (instancetype)init;
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side;
#end
BarModel.m
#import "BarModel.h"
#interface BarModel ()
#property (nonatomic) int capacity;
#end
#implementation BarModel
- (instancetype)init
{
self = [super init];
if (self) {
self.capacity = letterCapacity;
_addedLetters = [[NSMutableArray alloc] init];
}
return self;
}
// We'll use automatic notifications for this example
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
if ([key isEqualToString:#"arrayLetter"]) {
return YES;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side{
if([_addedLetters count] > _capacity){
return FALSE;
}
switch (side) {
case BarModelSideLeft:
[_addedLetters insertObject:letter atIndex:0];
return TRUE;
break;
case BarModelSideRight:
[_addedLetters addObject:letter];
return TRUE;
break;
default:
return FALSE;
break;
}
}
// These methods enable KVC compliance
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index
{
self.addedLetters[index] = object;
}
- (void)removeObjectFromDataAtIndex:(NSUInteger)index
{
[self.addedLetters removeObjectAtIndex:index];
}
- (id)objectInDataAtIndex:(NSUInteger)index
{
return self.addedLetters[index];
}
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes
{
return [self.addedLetters objectsAtIndexes:indexes];
}
- (NSUInteger)countOfData
{
return [self.addedLetters count];
}
#end
BarView.h
#import <SpriteKit/SpriteKit.h>
#import "BarModel.h"
#interface BarView : SKSpriteNode
#property (nonatomic, strong) BarModel *barModel;
#end
BarView.m
#import "BarView.h"
#implementation BarView
static char MyObservationContext;
- (instancetype)init
{
self = [super init];
if (self) {
//_barModel = [[BarModel alloc] init];
}
return self;
}
-(void)setBarModel:(BarModel *)barModel{
if(_barModel != barModel)
_barModel = barModel;
[_barModel addObserver:self
forKeyPath:#"arrayLetter"
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
context:&MyObservationContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Check if our class, rather than superclass or someone else, added as observer
if (context == &MyObservationContext) {
// Check that the key path is what we want
if ([keyPath isEqualToString:#"arrayLetter"]) {
// Verify we're observing the correct object
if (object == self.barModel) {
[self draw:change];
}
}
}
else {
// Otherwise, call up to superclass implementation
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void) draw: (NSDictionary*) change{
NSLog(#"KVO for our container property, change dictionary is %#", change);
}
#end
When I ru this I get this "error":
2014-08-31 00:23:02.828 Testing[329:60b] Added letter
2014-08-31 00:23:02.830 Testing[329:60b] An instance 0x17803d340 of class BarModel was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x17804eb50> (
<NSKeyValueObservance 0x1780cf180: Observer: 0x178111670, Key path: arrayLetter, Options: <New: YES, Old: YES, Prior: NO> Context: 0x100101428, Property: 0x17804eb80>
I tried to follow the instructions in error but can not find where to set break point. Please help me figure this out!
The error is pretty descriptive. You add self as an observer of a BarModel object. At some point that object gets deallocated. But you never remove self as an observer by calling removeObserver:forKeyPath:context:. You need to do that.
First, in setBarModel, make sure to remove self as an observer of the previous value of _barModel.
Next, you probably need to add a dealloc method that does the same thing.
There are multiple problems with the code. In addition to what Tom Harrington said with respect to the specific error that was logged about failing to remove the observation:
You implemented the indexed collection accessors for a (non-existent) property named "data". That is, they have "Data" in their name where the property name should be.
The indexed collection property is addedLetters. So, the indexed collection mutating accessors should be:
- (void)insertObject:(id)object inAddedLettersAtIndex:(NSUInteger)index;
- (void)removeObjectFromAddedLettersAtIndex:(NSUInteger)index;
You don't really need the non-mutating accessors, since you have an array-type public property with a normal getter (i.e. -addedLetters).
By the way, that property is of type NSMutableArray which it should not be. The property should be of type NSArray, backed by an instance variable of type NSMutableArray. That is, the mutability of the type (as opposed to the property) should not be exposed through the public interface. When you do this, you have to manually declare the instance variable (since it should differ from the type of the property and auto-synthesis will get it wrong), make the property copy instead of strong, and implement the setter yourself to do a mutable copy of the passed-in immutable array:
- (void) setAddedLetters:(NSArray*)addedLetters
{
if (addedLetters != _addedLetters)
_addedLetters = [addedLetters mutableCopy];
}
Once you have implemented the indexed collection mutating accessors with the correct names, you must use only those methods to mutate the collection (after initialization). In particular, your -addLetter:onSide: method must not directly operate on the _addedLetters instance variable. This is the part that makes the class KVO-compliant for that property. The mere presence of the indexed collection mutating accessors does not help. They must be used for all actual mutations.
Your implementation of +automaticallyNotifiesObserversForKey: is redundant. Automatic notification is the default.
The BarView class is key-value observing a key path "arrayLetter" on its _barModel object, but that's not the name of the property on BarModel. I suppose you meant to use the key path "addedLetters".
Finally, for proper adherence to MVC design, your view should not have a reference to your model. It should have a reference to the controller. The controller can reflect the model to the view (or, in theory, adapt a model of a different internal design to what the view expects). Or, in a more traditional non-KVO approach, the controller would actually tell the view when something has changed and give it the updated data it should show.
So, your controller could expose its own addedLetters property:
#property (readonly, copy, nonatomic) NSArray* addedLetters;
It could be implemented as a derived property, forwarded through to the _barModel object:
+ (NSSet*)keyPathsForValuesAffectingAddedLetters
{
return [NSSet setWithObject:#"barModel.addedLetters"];
}
- (NSArray*)addedLetters
{
return self.barModel.addedLetters;
}
Then, the view would have a reference to the controller and not the model, and it would key-value observe the "addedLetters" key path on the controller.

iOS singleton viewDidLoad empty and on viewDidAppear not

I created a singleton in ios7 like this:
SharedData.h
#interface SharedData : NSObject
{
}
+ (id)sharedInstance;
#property (strong, nonatomic) NSMutableArray *list;
#end
SharedData.m
#import "SharedData.h"
#implementation SharedData
#synthesize list;
// Get the shared instance thread safe
+ (SharedData *)sharedInstance {
static dispatch_once_t once = 0;
static SharedData *sharedInstance = nil;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
self = [super init];
if (self) {
//initialize
list = [[NSMutableArray alloc] init];
}
return self;
}
#end
I always use this code to access this class:
SharedData *sharedData = [SharedData sharedInstance];
The problem is now when I switch the view in my viewDidLoad method the list is empty but in my viewDidAppear method everything is fine. Any ideas?
EDIT:
This is the code how I change the views:
SharedData *sharedData = [SharedData sharedInstance];
//clear feed and add new feed
[sharedData.list removeAllObjects];
[sharedData.list addObjectsFromArray:newList];
//show new gui
[self.navigationController performSegueWithIdentifier:#"goToMain" sender:self];
NOTE: I push from a normal ViewController to a TabBarController -> NavigationController -> TableViewController to display the list.
I guess you have the confusion between these two viewcontroller methods:
-(void)viewDidLoad{
//
}
&
-(void) viewDidAppear{
//
}
viewDidAppear is the method which is called each time your view changes but viewDidLoad is the method which is not necessarily called each time your view changes.
ViewDidLoad method is called when view loads for the first time, after that it doesn't get called until the views are removed/released.
P.S: I suggest you to put the breakpoint in your viewDidLoad and viewDidAppear method and feel it. Your answer lies there.
Hope this helps you alot.
Good Luck.
The problem was i created a segue which went from the button to the next view. Because of this the viewDidLoad gets earlier called than the list assigned. I just changed the segue to go from view to view.
How are you changing from one viewController to the other? Wich classes are the parents of your destination ViewController?,
If you are modifying properties of the view in the prepareForSegue method... you are forcing the view to load.
For example, you are setting the list of your singleton in prepareForSegue, but before setting the list you are modifying a property of your destination viewController. (doing something like destVC.view = XXX or destVC.viewControllers = XX if you are subclassing a UITabBarViewController...) Then you are triggering the viewDidLoad method , and it's executing before you have set the list to the correct value.
Or maybe you are seguing in two different places to the destinationViewController. And when the viewDidLoad happens, you still have not updated the list on the singleton.
Here is the transcription of the chat with the poster of the question: https://chat.stackoverflow.com/transcript/55218

Pass BOOL value

I am working on an app and I got stuck at the point where I can't seem to retrieve the value of a BOOL set in a class.
I spent too much time already on it, been through all the questions I found that seem to cover the matter.
The bad thing here is that I get something, but not what I need (I get a 0, which means, I guess, that the value wasn't retrieved correctly as it should be 1).
The things I tried are :
pass a pointer to my first class and access to my BOOL like this:
//in some method
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
by declaring it (talking of the pointer) as a property in my second class (and importing the h. file from my first class, where my BOOL is declared as property too):
#property FirstClass *pointerFirstClass;
But I got 0 using this.
The other shot I gave was add my BOOL in the first class and create an instance of the class in my second class
//in some method
FirstClass *firstClass = [[FirstClass alloc] init];
if (firstClass.myBOOL){
//Do something
}
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
But I got 0 too.
As Booleans are primitive types, like in C, I get a bit confused since I am new to object-oriented programming, I don't know how I could like create a getter for this, for example.
I also tried to do a - (BOOL)getBOOLValue method in my first class, and call this method in my second class and assign it to a BOOL in that second class.
But the result wasn't better.
Am I missing something?
Is there a way to get my value that I didn't think of or didn't know about yet?
I am running low on thoughts on how to get around this, it shouldn't be that hard IMO so I hope it is something simple that I just left aside.
EDIT :
Some actual code. I am working between 2 files called AppDelegate (yes, the actual one) and WelcomeViewController (so a VC).
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
BOOL inRegion; //thought of this making my BOOL as a property of AppDelegate
}
#property (strong, nonatomic) UIWindow *window;
#property BOOL inRegion; //Declaring my BOOL here to make it accessible for another class
- (BOOL)getBOOLValue; //An attempt to pass my BOOL value
AppDelegate.m
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if (state == CLRegionStateInside)
{
self.inRegion = YES; //Set my BOOL to TRUE
}
else if (state == CLRegionStateOutside)
{
self.inRegion = NO; //Else set it to False
}
- (BOOL)getBOOLValue
{
return inRegion; //Tried to create a custome "getter"
}
WelcomeViewControler.m (I changed nothing in the .h file)
I said I tried many things, right now, this is the last version of my code.
//Simply trying to do a Segue on a condition...
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}
As said, I want to retrieve the BOOL value of the AppDelegate.
Thank you.
This code doesn't make sense:
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
The first line doesn't do anything. You're not assigning anything to the property, and you're not doing anything with the value. Furthermore, the second line doesn't relate to the first line in any way that we can see from the code you've provided. Try this instead:
self.pointerFirstClass = [[FirstClass alloc] init];
self.pointerFirstClass.myBOOL = YES;
NSLog(#"myBOOL = %d", self.pointerFirstClass.myBOOL);
In other words, you need to be sure that self.pointerFirstClass points to a valid object. And then you need to make sure that you've assigned the value you want to the myBOOL property of that object.
Update: This looks like a case where you're talking to the wrong object. Look at this:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
This is surely not what you really want. The application object is a single object -- a real singleton, in fact, meaning that there is and can be only one application object. That object has a delegate object, and that's a specific instance of your AppDelegate class. In this code, though, you're creating a new instance of AppDelegate, one that's different from the one that the application is using. Any changes that are made to the actual application delegate in response to messages from the application will not be reflected in the new object that you've created.
What I think you want is to get the actual application delegate object, and you can do that using:
[[UIApplication sharedApplication] delegate];
So, change your code to look like this:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];;
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
// note: you don't need an else clause if it doesn't do anything
}
That way, you'll be talking to the same object that the app uses, which is the one that has the inRegion property set in response to the location manager call.
UPDATE - Now we can see your code the problem is obvious, you are trying to access the appDelegate by creating a new one...
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
Instead you should be doing this....
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]
--
Not sure if you are posting your actual code? but the first example you give...
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
Shouldn't the second line be
NSLog(#"%d", self.pointerFirstClass.myBOOL);
Also this property...
#property FirstClass *pointerFirstClass;
Won't retain it once you've set it, it needs to be
#property (nonatomic,strong) FirstClass *pointerFirstClass;
In the second example...
FirstClass *firstClass = [[FirstClass alloc] init];
if (firstClass.myBOOL){
//Do something
}
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
You allocate and initialise a new FirstClass object and then check the property straight away, if you are not setting this to YES in the init then it will be false
Like I say, maybe you're not posting your actual code?
I guess what you want is initializing myBOOL to 1.
If so, you need do something as following
#implement FirstClass
- (id)init
{
self = [super init];
if(self) {
_myBOOL = 1;
}
return self;
}
// Other methods
#end
EDIT:
The comments is why you get 0.
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init]; // this is the problem.
// you create a new appdelegate,
// and never call locationManager:didDetermineState:forRegion:
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}
rewrite your code as following:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}

method not called from app delegate in ios

I want to call a method from elsewhere in the app to get the user's location that was obtained in the app delegate. When calling CLLocation *getCurLoc = [AppDelegate getCurrentLocation]; from another view controller, nothing is returned.
The App Delegate is,
#synthesize locationManager;
CLLocation *location;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager.desiredAccuracy=kCLLocationAccuracyKilometer;
[locationManager startUpdatingLocation];
return YES;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
[locations lastObject];
[manager stopUpdatingLocation];
CLLocation *location = [locations lastObject];
}
+(CLLocation *)getCurrentLocation{
return location;
}
Changing it to an instance method with a "-" didn't work. Should "location" be made into an instance? Should the delegate be made into an instance, or is there a better way to access it from elsewhere?
In didUpdateLocations, you define and set a local variable location, not the global variable location defined at the top of the file. Changing the line
CLLocation *location = [locations lastObject];
to
location = [locations lastObject];
would fix the problem. But the better solution is to use a property in the AppDelegate class.
You can define it in a class extension:
#interface AppDelegate ()
#property(strong, nonatomic) CLLocation *location;
#end
Then you access it in instance methods like
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
[locations lastObject];
[manager stopUpdatingLocation];
self.location = [locations lastObject];
}
and in a class method like
+(CLLocation *)getCurrentLocation{
// Get the instance:
AppDelegate *app = [[UIApplication sharedApplication] delegate];
return app.location;
}
Update: If the AppDelegate is declared to conform so some protocol (in the
public interface or in the class extension), for example:
#interface AppDelegate () <CLLocationManagerDelegate>
#property(strong, nonatomic) CLLocation *location;
#end
then the above code creates a warning
initializing 'AppDelegate *__strong' with an expression of incompatible type 'id<UIApplicationDelegate>'
and an explicit cast is necessary:
+(CLLocation *)getCurrentLocation{
// Get the instance:
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return app.location;
}
Martin's answer is an effective quick&dirty way of solving this problem in a way that is consistent with your approach - storing the location in appDelegate.
If you want to take a step further you might want to consider implementing a special object that would hold the data - the data model. It is considered a bad practice to store data in application delegate - it is not what it is there for (though it works perfectly fine in sample or small applications).
You could do something like this:
DataModel.h
#import <Foundation/Foundation.h>
#interface DataModel : NSObject
#property (strong) CLLocation *location;
+ (DataModel *)sharedModel;
#end
DataModel.m
#import "DataModel.h"
#class CLLocation;
#implementation DataModel
- (id) init
{
self = [super init];
if (self)
{
_location = nil;
}
return self;
}
+ (DataModel *)sharedModel
{
static DataModel *_sharedModel = nil;
static dispatch_once_t onceSecurePredicate;
dispatch_once(&onceSecurePredicate,^
{
_sharedModel = [[self alloc] init];
});
return _sharedModel;
}
#end
You would then need to #import "DataModel.h" wherever you need it. You would change your didUpdateLocations: to:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
[locations lastObject];
[manager stopUpdatingLocation];
[DataModel sharedInstance].location = [locations lastObject];
}
And from anywhere in the code you could get this location simply by [DataModel sharedInstance].location.
EDIT:
For a very simple app this approach might look as an overkill. But as soon as your app grows it surely pays off to use it.
This kind of class/object/singleton is ment to hold all the data your app needs (fixed and temporary). So all the data sources can make a good use of it. In short: it enables you to easily follow the model-view-controller guidelines.
You cold of course use another class/object/singleton to hold the temporary data - it depends on the complexity of your data-structure.
You don't have to specifically initialize this kind of object. It is initialized the first time you reference it. That is why dispatch_once is there for. It makes sure that there is one and only one instance of this shared object present: https://stackoverflow.com/a/9119089/653513
And this one single instance of [DataModel sharedInstance] will remain there until your app is terminated.
Apple uses similar approach for [NSUserDefaults standardDefaults], [UIApplication sharedApplicaton] for example.
I tend to put the #import "DataModel.h" into my projects Prefix.pch so I don't have to import it every single time I use it (it is used trough all the app).
PROS:
- data accesible throughout the app
- code reusability
- MVC structure
- code is more readable
CONS:
- I couldn't really find one. Except that the dispatch_once(&onceSecurePredicate,^... might confuse one for the first couple of seconds.
You can access the AppDelegate from any point in your application using [UIApplication sharedApplication].delegate so you could do:
CLLocation *location = [(YourAppDelegateClassName*)[UIApplication sharedApplication].delegate getCurrentLocation];
The method getCurrentLocation needs to be an instance method (-(CLLocation *)getCurrentLocation). You will need also to import #import "YourAppDelegateClassName.h" in those files you need to use that method.
To avoid the casting and accessing [UIApplication sharedApplication].delegate everytime I prefer to implement a static method in my AppDelegates:
+ (YourAppDelegateClassName*)sharedDelegate {
return (YourAppDelegateClassName*)[UIApplication sharedApplication].delegate;
}
So you can use any method like this:
CLLocation *location = [[YourAppDelegateClassName sharedDelegate] getCurrentLocation];

Resources