iOS Significant Location Change Only Works When Device is Awake - iphone

I am trying to write an app in iOS for the iPhone 4 using significant location change that fires a local notification each time the location delegate receives a location update. My code is posted below. I resign the app and close it out of the background. As long as the phone is awake (the screen is lit up), the application works well, firing the notifications, but when I put the phone to sleep (the screen is black), I no longer receive notifications until I wake the phone up by pressing the home button, receiving a text, etc. Then the notification is fired. What do I need to do in order for location updates to register even when the device is asleep? Thanks in advance.
SigLoc.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface SigLocViewController : UIViewController <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
}
#property (nonatomic, retain) CLLocationManager *locationManager;
#end
SigLoc.m
#import "SigLocViewController.h"
#implementation SigLocViewController
#synthesize locationManager;
- (void)viewDidLoad {
self.locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startMonitoringSignificantLocationChanges];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
UIApplication *app = [UIApplication sharedApplication];
UILocalNotification *alarm = [[[NSClassFromString(#"UILocalNotification") alloc] init] autorelease];
if (alarm) {
alarm.fireDate = nil;
alarm.repeatInterval = 0;
alarm.alertBody = #"Location Update";
alarm.soundName = UILocalNotificationDefaultSoundName;
alarm.hasAction = NO;
[app presentLocalNotificationNow:alarm];
}
}

My guess is your view is not active when your app goes into the background, so it has no way to get the notifications. I would assign your app delegate as a location manager delegate, and then pass the callbacks to your SigLoc location manager delegate.
I had a similar issue trying to receive the callbacks, which didn't resolve until I moved the location manager delegate to my app delegate. I would start there and work your way back to your method.

Related

CLLocationManager delegates not working after initialization

I am trying to get the compass of the iphone to work using the rhomobile framework.
I already did the rhomobile part, created a working wrapper that calls native methods on the iphone, but I cant manage to get the events to work.
Locationmanager.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface locationController : NSObject <CLLocationManagerDelegate>
{
CLLocationManager *locationManager;
}
#property (strong, nonatomic) CLLocationManager *locationManager;
- (id)init;
- (void)dealloc;
#end
Locationmanager.m
#import "Locationmanager.h"
#include "ruby/ext/rho/rhoruby.h"
//store the values
double gx, gy, gz, gth;
//init location
locationController *lc;
#implementation locationController
#synthesize locationManager;
- (id)init {
if (self = [super init]) {
self.locationManager = [[CLLocationManager alloc] init];
NSLog(#"%#", [CLLocationManager headingAvailable]? #"\n\nHeading available!\n" : #"\n\nNo heading..\n");
NSLog(#"%#", [CLLocationManager locationServicesEnabled]? #"\n\nLocation available!\n" : #"\n\nNo location..\n");
// check if the hardware has a compass
if ([CLLocationManager headingAvailable] == NO) {
// No compass is available. This application cannot function without a compass,
// so a dialog will be displayed and no magnetic data will be measured.
locationManager = nil;
UIAlertView *noCompassAlert = [[UIAlertView alloc] initWithTitle:#"No Compass!" message:#"This device does not have the ability to measure magnetic fields." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[noCompassAlert show];
[noCompassAlert release];
NSLog(#"\n***** ERROR *****\n No compass found !!!");
} else {
// setup delegate callbacks
locationManager.delegate = self;
// heading service configuration
locationManager.headingFilter = kCLHeadingFilterNone;
// location service configuration
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
//start location services
[locationManager startUpdatingLocation];
// start the compass
[locationManager startUpdatingHeading];
}
return self;
}
}
- (void)dealloc {
[super dealloc];
// Stop the compass
[locationManager stopUpdatingHeading];
[locationManager release];
}
// This delegate method is invoked when the location manager has heading data.
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading {
NSLog(#"\n\n***** New magnetic heading *****\n %f\n", heading.magneticHeading);
NSLog(#"\n\n***** New true heading *****\n %f\n", heading.trueHeading);
gx = heading.x;
gy = heading.y;
gz = heading.z;
gth = heading.trueHeading;
}
// This delegate method is invoked when the location managed encounters an error condition.
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
if ([error code] == kCLErrorDenied) {
// This error indicates that the user has denied the application's request to use location services.
NSLog(#"\n ***** ERROR *****\n Location not allowed!");
[locationManager stopUpdatingHeading];
} else if ([error code] == kCLErrorHeadingFailure) {
NSLog(#"\n ***** ERROR *****\n Magnetic interference or something!");
}
}
#end
//ruby wrappers
void locationmanager_init(void) {
// make sure we can only start this method once
static bool started = false;
if(!started) {
// Initialize the Objective C accelerometer class.
lc = [[locationController alloc] init];
started = true;
}
}
void locationmanager_get_heading(double *x, double *y, double *z, double *th) {
NSLog(#"\n ***** DEBUGGER *****\n Getting heading x: %f, y: %f, z: %f, heading: %f", gx, gy, gz, gth);
*x = gx;
*y = gy;
*z = gz;
*th = gth;
}
I'm running the code on an iphone 4 with iOS 5.1, in the console I can see the debug messages of init, but I never see a debug message of the didUpdateHeading delegate. Anyone got a clue what I missed here?
UPDATE
I think I need to run my code in a background thread to get it working. Currently the locationmanager_init initializes + leaves the code, therefor its not active and the events are not fired.
Anyone got a simple solution initializing this in the background to keep it active?
UPDATE 2
Returned the id, used self = [super init] and still no fix :(
GitHub code
Initializes with locationmanager_init, retrieves data with locationmanager_get_heading
You have to init the CLLocationManager on the main thread, check this SO here, or run it from a thread with an active run loop, check this SO here, From Apple documentation:
Configuration of your location manager object must always occur on a thread with
an active run loop, such as your application’s main thread.
Make sure your locationController and the CCLocationManager inside it are alive past initialization, check here. I may be wrong here, but from your Github code, it seems that the *lc variable is getting released in the autorelease pool. Try giving it an extra retain.
lc = [[[locationController alloc] init] retain];
I guess this is the cause of your problem. If the object is released you wont get any updates.
Not related to the question but:
You should call [super dealloc] last but not first, check this SO here
Put the return statement in your init method before the last parenthesis, and not before the second last.
...
// start the compass
[locationManager startUpdatingHeading];
}
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)heading;
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
These are your instance methods not delegate methods.
check
https://developer.apple.com/library/mac/#documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/CLLocationManagerDelegate/CLLocationManagerDelegate.html
Possibly this is not the solution to the overall problem, but your init method is missing a return statement:
- (id)init {
if (self = [super init]) {
// do your setup here
}
return self;
}

GEO-reminders launching app

I am thinking about an app which would use the GEO-reminders (those added in iOS5,Remind me at a Location when I leave/arrive ). But I need using this feature (in fact, only using the location) to get the current location and compare it with the defined location by my app and check whether it's the same loc or it isn't. If the current and defined location are the same, launch my app.
Is this possible?
I hope you to understand my objective.
Thanks in advance
While you will be able to monitor your location from the background, keep in mind, it will not automatically launch your app. You can use something like local notifications to prompt the user to open the app. But launching from the background automatically is not an option. At least not an App Store approved option.
The check for current region and defined region are done automatically when you set a region to monitor. The best place to start is by reading the docs for CLLocationManagerDelegate, especially startMonitoringForRegion:. The thing you want to do is called "Geofencing". You can also find more information in the Location Awareness guide.
As 'm new to iPhone Development i don't know how to programmatically lunch an app but i can help you out with the trigger on arriving on predefined location. here is the code.
1: import CoreLocation.framework
2: in viewController.h file place below code
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController<CLLocationManagerDelegate>
#end
3: inviewController.m
#import "ViewController.h"
#interface ViewController (){
CLLocationManager *locationManager;
CLRegion *mexicoBoundary;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
CLLocationCoordinate2D regionCords ;
//19.432608,-99.133208 lat, lon for mexico city
regionCords=CLLocationCoordinate2DMake(19.432608,-99.133208);
//5000 below, is in meters-radius
mexicoBoundary =
[[CLRegion alloc]initCircularRegionWithCenter:regionCords
radius:5000.0
identifier:#"mexico_Day"];
[locationManager startMonitoringForRegion:mexicoBoundary];
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(#"%#: %#", #"region entered", region.identifier);
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(#"%#: %#", #"region exited", region.identifier);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end

Should an app start location tracking in order to get ANY last known location from CLLocationManager?

Currently, developing an app that needs to get last location from CLLocationManager (without any regular tracking). It doesn't matter how old, accurate it is. I don't need and want to start tracking - I just need to just grab some last location from a cache and that's it. IMHO, CLLocationManager is a shared component in iOS and if some app uses location tracking then another app should be able to use the most recent location from CLLocationManager.location. It should be sufficient just to alloc/init CLLocationManager and grab its location. However it's not. I have tested on iPhone4 - started google maps, saw my current location, then went to my app, but after [[CLLocationManager alloc] init] location property is nil.
UPDATE: tried [locationManager startUpdatingLocation]; and [locationManager stopUpdatingLocation]; but the result is the same. I guess, the only solution is to start regular tracking?
UPDATE2: Strange but there's no alert with "The app wants to use location services" after alloc/init of CLLocationManager. Here's my code fragment:
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
[locationManager startUpdatingLocation];
[locationManager stopUpdatingLocation];
NSLog(#"%#", locationManager.location); //prints nil
First you should check if your locationManager has a, let's say, 'static' location pre-saved.
If it does, you're done.
If not, you should startUpdatingLocation and then, in the didUpdateToLocation:fromLocation: callback, stopUpdatingLocation once you get the location.
My experience says that's the best way to get only one location.
UPDATE TO MATCH AUTHOR UPDATES:
You should not stopUpdatingLocation just after startUpdatingLocation. startUpdatingLocation starts a service in background, so you should wait until you get a location, so invoque it in the callback methods.
To make any use of CLLocationManager you need to implement CLLocationManagerDelegate somewhere.
-[CLLocationManager startUpdatingLocation] starts an async process. If you stop it in the same runloop cycle the process never gets started and that is the reason you never see the permission dialog.
It goes something like this:
#interface MyClass : NSObject <CLLocationManagerDelegate> {
CLLocationManager *manager;
CLLocation *lastLocation;
}
#end
#implementation
- (id)init {
self = [super init];
if (self) {
manager = [[CLLocationManager alloc] init];
manager.delegate = self;
[manager startUpdatingLocation];
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation;
{
lastLocation = newLocation;
[manager stopUpdatingLocation];
}
// in your real implementation be sure to handle the error cases as well.
#end

stop location updates from the iphone UI?

Is it possible to start / stop location updates from the UI of the iphone? All I need from the app is to show me my location unless I click "stop" and then "start" again.
I can't seem to be able to do that...I have my location displayed properly, and I also created two IBButtons and created a function for each of them, however, my app crashes when I click on each one of those buttons. I placed those functions under the viewcontroller.m.
I am kind of new to this, so any help would be appreciated. Thanks!
(IBAction)startUpdating: (CLLocation *)location
{
[location startUpdatingLocation];
}
(IBAction)stopUpdating: (CLLocation *)location
{
[location stopUpdatingLocation];
}
start/stopUpdatingLocation are CLLocationManager instance methods, rather than CLLocation instance methods... so create a CLLocationManager instance.
.h
#interface someClass:somesuperclass{
CLLocationManager * locationManager;
BOOL updating;
}
-(IBAction)toggleUpdating:(id)sender;
#end
.m somewhere in the view load/ or init cycle:
-(void)viewDidLoad{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
}
-(void)viewDidUnload{
[locationManager stopUpdatingLocation];
[locationManager release];
[super viewDidUnload];
}
-(IBAction)toggleUpdating:(id)sender
{
if(!updating)
{
[locationManager startUpdatingLocation];
}else{
[locationManager stopUpdatingLocation];
}
updating = !updating;
}
also your action above will never work, because the thing after a colon in an action will be the object that sent the action, a UIButton in your case.

CLLocationManager never calls delegate methods [duplicate]

This question already has answers here:
Why the CLLocationManager delegate is not getting called in iPhone SDK 4.0?
(5 answers)
Closed 7 years ago.
Hi I'm playing around with locations on iPhone and right from the start I ran into problems. I've localized my problem and determined it's CLLocationManager that's bugging me.
So I developed very simple application. I just have a view controller with a CLLocationManager in it. On view did load I initialize CLLocationManager and start updating. I've also implemented two methods didFailWithError and didUpdateToLocation.
I've read a lot of questions and what i have learned so fare is this. You have to retain CLLocationManager during initialization. Also it's wise to set CLLocationManagers delegate to nil during unloading of a view (something to do with messages passing to CLLocationManager because framework retains it and it's never properly release)
All in all I just cant find a decent explanation on what to do and how to make it work.
Here's my code so if anybody could figure it out I would appreciate it.
viewControllers header file
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface CoreLocationViewController : UIViewController <CLLocationManagerDelegate>
{
CLLocationManager *locManager;
}
#property (nonatomic, retain) CLLocationManager *locManager;
#end
viewController .m file
#import "CoreLocationViewController.h"
#implementation CoreLocationViewController
#synthesize locManager;
- (void)viewDidLoad
{
[super viewDidLoad];
self.locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
[self.locManager startUpdatingLocation];
self.view.backgroundColor = [UIColor grayColor];
}
#pragma mark -
#pragma mark Location Delegate
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"in fail with error");
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"in update to location");
}
#pragma mark -
#pragma mark Memory Management
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload
{
self.locManager.delegate = nil;
[super viewDidUnload];
}
- (void)dealloc
{
[locManager release];
[super dealloc];
}
#end
BTW: I'm using iOS 4.2. I'm pointing this out because I read that Apple has changed location delegates in iOS 4
In my case, the delegate never gets called because of threading problem
the thread that operates LocationManager must have an NSRunLoop set
up
if you use main thread, you already have a runloop, now you just
need to instantiate the manager and use it on main thread
Read this to know more about CLLocationManagerDelegate
Furthermore in iOS8 you must have two extra things:
Add a key to your Info.plist and request authorization from the location manager asking it to start.
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
You need to request authorization for the corresponding location method.
[self.locationManager requestWhenInUseAuthorization]
[self.locationManager requestAlwaysAuthorization]
Code example:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];
Source: http://nevan.net/2014/09/core-location-manager-changes-in-ios-8/
Other posters have mentioned checking for authorization, and you should definitely add that, but I do not think that is your problem. If you were not authorized your didFailWithError: method would be called. This leads to believe something else is going on here.
It may be a good idea to make sure that your location manager is indeed being allocated and while your at it you can fix your memory leak.
Try this:
CLLocationManager* lm = [[CLLocationManager alloc] init];
NSAssert(lm != nil, #"Failed to allocate CLLocation manage");
self.locManager = lm;
[lm release];
self.locManager.delegate = self;
[self.locManager startUpdatingLocation];
// Let's just print what we got
NSLog(#"CLLocationManager is %#", lm);
If your run this your will get one of the following results:
1) if the location manager is returning nil, your program will crash into the debugger (because of NSAssert)
2) if "CLLocationManager is ...." prints and still you see no updates then you have a real mystery on your hands.
3) Nothing prints. That would mean, perhaps due to something incorrectly linked up in Interface Builder, that viewDidLoad is not being called at all.
4) You will get a didFailWithError: call because you are not authorized. Which means that at least everything is working as expected.
As an aside you had a memory leak because you were assigning the result of an alloc directly to a property with the retain attribute. After alloc your count would be +1 after assignment it would +2, so if the view was unloaded and reloaded you would leak.
Hope some of this helps.
This may be nit picky but in viewDidLoad change:
locManager.delegate = self;
to:
self.locManager.delegate = self;
I don't know if it will fix it, but is best to be consistent.
So I managed to find a bug. It was in memory management. In my app delegate file I initialized a CoreLocationViewController and added it as a subview to my window, after that I released it and all is well. But, the releasing part was not a good way to go. After I released my ViewController, dealloc method gets called and it releases locationManager.
So the proper thing to do is not to release a ViewController that holds locationManager, or find another way of dealing with it. I'm not quite sure why that was a problem because I thought that after adding a ViewController to a window it gets retained, therefore its localtionManager gets retained as well. If anybody can clear it up for me it would be much appreciated.
are you testing on device? are location services enabled?
if (self.locManager.locationServicesEnabled){
//do domething
}
Idk if it is a matter but in my code I also setup the desired accuracy
[locManager setDesiredAccuracy:kCLLocationAccuracyBest];
For example.
Try this:
NSLog(#"location services enabled: %d", [locManager locationServicesEnabled]);
From the docs:
The user can enable or disable location services altogether from the Settings application by toggling the switch in Settings > General > Location Services.
I've got the same problem and as paxx said, it's a memory management problem. My solution is retain 'self' manually. As the code shows below:
- (void) viewDidload
{
[super viewDidload];
if (![CLLocationManager locationServicesEnabled]) {
NSLog(#"Location Service Disabled!");
} else {
[self retain];
//initialize the cllocationmanager obj.
}
}
-(void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
//do your surf
[self release];
}
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
//do your stuff
[self release];
}
-(void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
//do your stuff
[self release];
}
Not pretty, but works fine for me.