I want to make a simple IOS 6.0 application that shows the lat/lon on screen each time the location is changed. The ViewController files are pretty trivial.
I alloc a CCLocationManager objec, set its variables and start it.
didUpdateLocations is called once or twice and then it stops being fired, even if the iPhone is moved. If I press Home button and reopen the app, the data on screen is updated once or twice before it stops again.
At simulation it works fine but not on a real 3GS iPhone.
If I uncomment the start/stop inside didUpdateLocations and continuously stop and start the service, it works, but the battery gets drained in extreme rates.
Also, this is part of a much bigger project and didUpdateLocations must be called each time the location is changed.
ViewController.h
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController
#property (nonatomic , strong) CLLocationManager *locationManager;
#property (weak, nonatomic) IBOutlet UILabel *lbl;
#end
ViewConroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLHeadingFilterNone; // whenever we move
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
_locationManager.PausesLocationUpdatesAutomatically = NO;
[_locationManager startUpdatingLocation];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = [locations lastObject];
_lbl.text=[NSString stringWithFormat:#"%lf %lf",newLocation.coordinate.latitude, newLocation.coordinate.longitude];
//[_locationManager stopUpdatingLocation];
//[_locationManager startUpdatingLocation];
}
#end
If there's any advice on what's wrong, I would welcome it, I have lost a week already without solving it.
BTW, I have tested various values for the _locationManager variables but without any chnage in the behaviour
Additional info:
application is authorized to use location services
application is in foreground
Few tips for similar issues as per my experience with location-based programming on iOS.
If it works on simulator, then it should work on actual device as
well.
Unlike simulator, the actual device will only give update when its receives new information from GPS.
There are many cases where you won't get enough/required updates on a device, like when you are in a building (or covered area)
Try testing the app in open space (GPS works much better in open space rather than room/house/large-building).
PS. I don't know how much you moved but just to let you know, moving few steps doesn't make sure you will get a new location update, try using it on some vehicle (bike/car/bus).
I had this problem too — the easiest fix is simply to duplicate the target. You can then delete the original target.
It seems the problem occurs with older Xcode projects, not newly created ones.
You can add this call to your code:
locationManager.stopUpdatingLocation()
Related
During requesting for Always permission on iOS13, the user can tap "Allow Once" which will call appropriate delegate with status kCLAuthorizationStatusAuthorizedWhenInUse but requesting for "Always" again calls delegate with kCLAuthorizationStatusAuthorizedAlways. Why? When other combinations work only once like you request always, you get it and even calling again will not call delegate with status.
Sample code to test:
#import CoreLocation;
#interface ViewController () <CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
}
- (IBAction)doauthloc:(id)sender {
[self.locationManager requestAlwaysAuthorization];
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
switch(status) {
case kCLAuthorizationStatusNotDetermined:NSLog(#"AUTH STATUS:kCLAuthorizationStatusNotDetermined"); break;
case kCLAuthorizationStatusRestricted:NSLog(#"AUTH STATUS:kCLAuthorizationStatusRestricted"); break;
case kCLAuthorizationStatusDenied:NSLog(#"AUTH STATUS:kCLAuthorizationStatusDenied"); break;
case kCLAuthorizationStatusAuthorizedAlways:NSLog(#"AUTH STATUS:kCLAuthorizationStatusAuthorizedAlways"); break;
case kCLAuthorizationStatusAuthorizedWhenInUse:NSLog(#"AUTH STATUS:kCLAuthorizationStatusAuthorizedWhenInUse"); break;
};
}
#end
It's a little confusing, isn't it? When you ask for Always and the user taps Allow Once, you are told that you got WhenInUse. But that doesn't actually matter. You have provisional Always. So:
When you subsequently go into the background and start monitoring visits or regions or whatever your location monitoring usage is, this will be converted into Always authorization for purposes of usage. (Your logging should confirm this.)
And then, because you got only Once authorization, when you come back to the foreground you will be Not Determined again.
So the takeaway is, just laugh an evil laugh and move on. Your background location monitoring will work, and that's all that matters. Not only does it work, but as a bonus you get to present the user with the authorization alert again, which is the reason for all this change in iOS 13. Don't worry, be happy.
I am in the process of testing location updates via the background APIs that relaunch an app. I have successfully tested visits and regions and am using this same app to also test significantLocationChanges (SLC). I run my app, press home twice then swipe it off to kill it. Then I drive around. I can observe the app background activity without touching the phone via a server where (see code below) the singleton method sendBackgroundTestRecord uploads simple status messages to the server.
When I test visits and regions I have no problems. However when I test SLC, nothing happens until I turn the phone's screen on (pressing the power or home button once -- not re-running the app, or even unlocking the phone). I can drive a far distance without anything happening. I suspect the app has received an SLC event sometime during that drive because as soon as I turn the phone on for a about 2 seconds, the code below executes and I can observe the messages on the server.
I believe this is not just a network connectivity delay because the sendBackgrounTestRecord method uses a time stamp. The records on the server show a time stamp of when I turned the screen on, not when the SLC event is suspected to have fired.
I never have this problem with visits or regions, only SLC. By the way, the code has gone through many changes trying to chase this down. The latest attempt (code below) was to put everything in the appDelegate.
Any ideas?
Thanks in advance for any ideas whatsoever!
EDIT: I added beginBackgroundTaskWithExpirationHandler, which was in my original tests too.
// AppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
// AppDelegate.m
#import "AppDelegate.h"
#import "clsCommon.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Ask iOS for more time to process - before we do anything else
[clsCommon sharedInstance].backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}];
[[clsCommon sharedInstance] sendBackgroundTestRecord:#"didFinishLaunchingWithOptions"];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if([self.locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)]) {
[self.locationManager setAllowsBackgroundLocationUpdates:YES];
}
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
self.locationManager.allowsBackgroundLocationUpdates = YES;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startMonitoringSignificantLocationChanges];
return YES;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
[[clsCommon sharedInstance] sendBackgroundTestRecord:#"didUpdateLocations"];
CLLocation * currentLocation = [locations objectAtIndex:0];
if(currentLocation !=nil) {
[[clsCommon sharedInstance] sendBackgroundTestRecord:#"didUpdateLocations curLoc not nill"];
}
}
#end
Well, knowing how aggressively iOS resolves all tradeoffs in favor of battery life I would not be surprised very much. For example, behavior of "breadcrumbs" app (set 100m geofence at your current location, move that geofence to new location on exit event) is very different depending on whether iPhone sleeps all the time in your pocket, or you check lock screen occasionally.
However, running an app very similar to yours while traveling back home yesterday did generate location updates for me.
Distances between SLC readings were in 4 to 14 km range, the smallest time gap between two consequent readings - 14 minutes. The app is very similar to yours, it does not include any networking part, just logging events to a local file (NSLog via stderr redirection). Also, I do not configure properties, which are irrelevant for SLC in location manager, like desiredAccuracy, distanceFIlter, activityType. I suggest you start from barebone app and watch for the improvement, which breaks it.
One thing to check is if your app crashes when handling location updates. If it happens in background you will not notice this unless you run logging or check crash logs via Settings.app.
Put some distance in self.locationManager.distanceFilterand is better for battery change self.locationManager.pausesLocationUpdatesAutomatically=YES;
Start Location Manager in iOS 7 from background task
I have been trying to move my iOS7 app with MKMapview to support iOS8. However I couldn't get the new request for users to share their locations to work properly. I create my MKMapView on a storyboard and the delegate is set and works perfectly on iOS7. Here is what I've added to support iOS8 Location sharing:
myMapView.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#interface myMapView : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) CLLocationManager *locationManager;
myMapView.m
//Code omitted
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
//Code omitted
- (void)viewDidLoad {
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if(IS_OS_8_OR_LATER) {
//[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];
[self.locationManager startUpdatingLocation];
}
[self.mapView setShowsUserLocation:YES];
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
MKCoordinateRegion region = { { 0.0, 0.0 }, { 0.0, 0.0 } };
region.center.latitude = self.locationManager.location.coordinate.latitude;
region.center.longitude = self.locationManager.location.coordinate.longitude;
region.span.latitudeDelta = 0.0187f;
region.span.longitudeDelta = 0.0137f;
[self.mapView setRegion:region animated:YES];
_initialPosition = NO;
}
Also I have set NSLocationAlwaysUsageDescription key and its value in my InfoPlist, which shows the correct message when prompting the user to share their location.
Unfortunately the delegate function -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations never gets called. Although each time the viewController gets loaded the [self.locationManager startUpdatingLocation] is called, but the delegate does not seem to respond to it. Is there a problem of how I set the delegate or is there something else I am missing here?
UPDATE: It seems also that my gpx file is not being called on launch. I have cleared and reloaded my location file, even changed to a default location, but no location is found: Domain=kCLErrorDomain Code=0
UPDATE 2: Here is a SS from the settings that I have actually succeeded with the user request, but fail to get/update location no matter how much I refresh.
(source: barisaltop.com)
Thanks!
I had the same problem a few days ago. The solution was adding the string keys NSLocationAlwaysUsageDescription (for [CLLocationManager requestAlwaysAuthorization]) or NSLocationWhenInUseUsageDescription (for [CLLocationManager requestWhenInUseAuthorization]) to your Supporting Files/Info.plist
You can also edit the source code of the Info.Plist with Right click > open as > Source code and add these lines:
<!-- for requestAlwaysAuthorization -->
<key>NSLocationAlwaysUsageDescription</key>
<string>Explain for what are you using the user location</string>
<!-- for requestWhenInUseAuthorization -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>Explain for what are you using the user location</string>
Hope this helps.
Finally I have succeeded to run my gpx file on the simulator. It seems that after installing Xcode 6 the first time, there might be a bug causing for gpx files to simulate. Here is how I overcame the problem:
I have deleted my app from the simulator
Under App->Capabilities enabled Background Modes->Location updates
Run the app and let it install on simulator
Allow access, and I was able to locate the user with GPX
Afterwards I disabled Location Updates.
I don't know why, but this did the trick for me.
I am using a custom BeaconManager delegate so that beacon ranging is not determined by the life-cycle of the view controller. Everything works great but every once in a while (1-2 days) beacon ranging will stop working and didRangeBeacons will never get called. The only way to fix this is for me to reset my iPhone, once I do this, it works perfectly. Below is the code that I am using. The basic flow is that when my ViewController calls ViewDidLoad it sends a notification back to the AppDelegate to tell it to start ranging for beacons, I never tell it to stop then because I want it to continue to range for beacons no matter where the user navigates to in the app. I'm wondering if my code is causing this or if this is just a bug with Bluetooth. Thanks for your help!
BeaconManager.m
#import "BeaconManager.h"
#import "AppDelegate.h"
#interface BeaconManager()<CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) CLBeaconRegion *beaconRegion;
#end
#implementation BeaconManager
+ (id)sharedManager
{
static BeaconManager *sharedBeaconManager = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedBeaconManager = [[self alloc] init];
});
return sharedBeaconManager;
}
- (id)init
{
self = [super init];
if(self)
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
}
return self;
}
- (void)startBeaconMonitoring:(NSString*)forUUID
{
NSUUID * uuid = [[NSUUID alloc] initWithUUIDString:forUUID];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"com.beacons.publicRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)stopBeaconMonitoring
{
//Stop the region monitoring
if(self.locationManager != nil && self.beaconRegion != nil) {
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
}
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
self.beacons = beacons;
if(self.delegate != nil) {
[self.delegate beaconManager:self didRangeBeacons:self.beacons];
}
}
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] postNotificationName:#"startRanging" object:nil userInfo:nil];
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(startRangingForZombies) name:#"startRanging" object: nil];
return YES;
}
- (void)startRanging
{
//Start the beacon region monitoring when the controller loads
BeaconManager *beaconManager = [BeaconManager sharedManager];
beaconManager.delegate = self;
[beaconManager startBeaconMonitoring:#"1234-54324-34242-34242-43243"];
}
We have received many reports at Radius Networks of phones stopping detecting iBeacons and requiring a reboot or turning Bluetooth off and back on again to resolve the situation. Folks have reported this on iPhone 4S, iPhone 5s, iPhone 5c and iPads.
I do not have any hard evidence that this is something that broke as of iOS 7.1, but the report frequency has gone way up since its release. The circumstantial evidence is therefore pretty strong.
When this phone gets into this state, the phone can still scan for bluetooth devices, and can still transmit as an iBeacon. It is therefore not a hardware problem with Bluetooth. Based on the available evidence, it is most likely a newly introduced bug in CoreLocation.
Actually, it's a known bug in iOS 7.1. It's strictly a software problem with the Bluetooth stack for the latest version of iOS. Bluetooth devices detection sometimes just stops working - unfortunately, it's the case for all iOS 7.1-compatible devices. The bug has already been reported to Apple, but as long as they do not issue a fix for that, the best solution is to just reboot the device.
If reboot doesn't help, there is handy guide at SmartRobotic on how to solve it: http://www.smartbotics.com/#!4-Tips-to-Fix-Bluetooth-Problems-After-iOS-71-Upgrade/c118r/031A86F6-C8E8-4768-B4FD-E6F83D9E4317
If you are experiencing Bluetooth connectivity issues after upgrading to iOS 7.1, give these 4 tips a try.
Shut down and restart - some people have reported that this is all that was needed to fix their device after the iOS 7.1 upgrade.
Toggle Bluetooth OFF and back ON - Swipe up to access Control Center and tap the Bluetooth icon, wait at least 30 seconds, then toggle it back on. This can often repair a device which is experiencing connection issues.
Kill (force quit) the offending app - Start by double clicking Home to launch the multitasking cards interface. Touch and hold the card for the app, then toss it up and away. This will force the app to quit and it will be fully reloaded the next time the app is opened.
Clear and reset your Bluetooth device pairing - Go to Settings>Bluetooth and tap on the (i) icon for the offending device. Tap on Forget this Device. Now you should be able to re-add and re-pair the Bluetooth hardware to your device. To clear all paired devices, go to Settings > General > Reset > Reset Network Settings and then set up your Bluetooth pairings again.
Hopefully these suggestions will resolve your Bluetooth connectivity problems.
Cheers.
Apple introduced important changes to Bluetooth LE in 7.1 but also broke something inside.
From my experiments:
iPhone 4S 7.x - iBeacons work like a charm
2 x iPhone 4S 7.1, 2 x iPhone 5 7.1 - works fine but require restarts from time to time (undeterministic).
It looks like system issue - a big one.
I have contacted Estimote - they know about it.
Interesting fact: You can't find beacons - even estimote demo app can't, delegate methods are not called but You can turn (broken)device into a beacon and it will be discovered by other devices.
Disclaimer: I currently work for sensorberg, we´re selling beacons and a SDK.
We´ve hat numerous reports of this bug as well. We asked all our customers to file a bug report with Apple. Here is a template that you can use: https://gist.github.com/anonymous/5283b6941e1f7d4e4461
I personally had the behaviour twice, once I was able to record it: https://www.youtube.com/watch?v=6a6IJzaxxJg Only restarting the device helped.
Continue to file Apple rdar bug reports!
I had the similar issue, but after some time of investigation I realized that it is wrong to call the startMonitoringForRegion(region) and startRangingBeaconsInRegion(region) after each other. That is what you (Patrick) do:
- (void)startBeaconMonitoring:(NSString*)forUUID {
NSUUID * uuid = [[NSUUID alloc] initWithUUIDString:forUUID];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"com.beacons.publicRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
Instead the startRangingBeaconsInRegion(region) should to be called in the locationManagerDelegate method locationManager(manager: CLLocationManager!, didDetermineState state: CLRegionState, forRegion region: CLRegion!) (In this case the Swift code). This was my solution.
It make sense, because the first step is to find any beacon in the region, the second one is to get specific information from the already monitored beacon.
MKMapViewDelegate mapView:didUpdateUserLocation: method is not called when running on the 5.0 simulator, even if all location permissions are given in the device settings.
With 4.3 it's working fine.
Any ideas?
I got the same thing after refactor to ARC.
First I had the following code in viewDidLoad;
CLLocationManager *lm =[[CLLocationManager alloc] init];
lm.delegate = self;
lm.desiredAccuracy = kCLLocationAccuracyBest;
[lm startUpdatingLocation];
I did the following in de headerfile
#interface ViewController : UIViewController <CLLocationManagerDelegate>
{
CLLocationManager *locationManager;
}
#property(nonatomic, strong)CLLocationManager *locationManager;
And
#synthesize locationManager;
The code I change in viewDidLoad
locationManager =[[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
Check "setUserTrackingMode:animated:" and "userTrackingMode". Both are new in the iOS5. I had a similar problem and fixed it by setting MKUserTrackingModeFollow in the setUserTrackingMode function. I think the default tracking mode is MKUserTrackingModeNone and only calls mapView:didUpdateUserLocation once.
If your problem is that the mapView:didUpdateUserLocation is never called, then take a look at the options in the new xcode, right below the console outputs there's an icon like the gps icon in the ipad, which lets you simulate a location.
I know it's an old thread. I'll answer anyway, it might help others too.
I had similar problem with iOS 6. Which I was able to solve by setting the mapview delegate to self.
Like this:
[mapView setDelegate:self];
Make sure your ViewController.h has the MKMapViewDelegate along the lines:
#interface ViewController : UIViewController <CLLocationManagerDelegate, MKMapViewDelegate>