How to calculate the current user location in Watch Kit extension as we can't use CoreLocation in watch kit.
Thanks in advance
You can use CoreLocation in your watch app extension very similarly to how you use it in your iPhone app. The key difference is that a user can't authorize your extension to have access to Core Location. They will need to do that from your iPhone app. So you will need to check if the user has authorized location services for your app and if they haven't, you will need to instruct them how to do it.
Here is the code I use in my watch kit extension for tracking the current location. (GPWatchAlertView is a custom controller I made to show alert messages.)
#pragma mark - CLLocation Manager
-(void)startTrackingCurrentLocation:(BOOL)forTrip
{
if (self.locationManager == nil)
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.activityType = CLActivityTypeFitness;
self.locationManager.distanceFilter = 5; //Require 15 meters of movement before we show an update
}
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse)
{
NSLog(#"%# Start tracking current location", self);
self.trackingCurrentLocation = YES;
self.gpsTrackingForTrip = forTrip;
//We wait until we have a GPS point before we start showing it
self.showCurrentLocation = NO;
[self.locationManager startUpdatingLocation];
}
else
{
[self presentControllerWithName:#"GPWatchAlertView" context:#"Unauthorized GPS Access. Please open Topo Maps+ on your iPhone and tap on current location."];
}
}
-(void)stopTrackingCurrentLocation:(id)sender
{
NSLog(#"%# Stop tracking current location", self);
self.trackingCurrentLocation = NO;
[self.locationManager stopUpdatingLocation];
self.showCurrentLocation = NO;
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation* loc = [locations lastObject];
...
}
The answer by Stephan should work (haven't tested it), with just one exception.
WatchKit requires "Always" permission for the location manages. This is because your phone is really running the watch extension, in background mode. So if you only ask for "When in Use" permission, you will never get locations returned to your watch extension.
Try changing the line:
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse)
with:
if (status == kCLAuthorizationStatusAuthorizedAlways)
you should get user location on iphone app not in extension. Please check apple documentation.
Related
I am facing one strange issue while using CLLocationManager for fetching the current Location. When I request for current location first time I am getting the permission popup having Allow and Don't Allow options. I have choose Don't Allow and then I went to App Settings and Turn On Location by choosing While Using the App and come back to the app and Uninstall the app. When I Reinstall the same ipa file I am not getting permission popup again.
Observation: When I was debugging the app I came to know that When App was installed first time and requested for Location Update I was receiving AuthorizationStatus = kCLAuthorizationStatusNotDetermined and from there I was calling requestWhenInUseAuthorization but when I was reinstalling the app instead of kCLAuthorizationStatusNotDetermined AuthorizationStatus i am directly getting requestWhenInUseAuthorization. I have observed this issue in iPhone 7 Plus with iOS 10. I have added permission keys into my info.plist file.
Please find the code snippet which i have used in the app.
- (Void)getCurrentLocation {
// Check if location services enabled in settings..
if ([CLLocationManager locationServicesEnabled]) {
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
}
else{
NSLog(#"Location Services Turn Off");
}
}
[1]: https://i.stack.imgur.com/vo4An.png
#pragma mark- CoreLocation Delegate Methods
// This will be called when app level location permissions are changed.
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
NSLog(#"didChangeStatus called.. status: %d", status);
switch (status) {
case kCLAuthorizationStatusNotDetermined:{
NSLog(#"kCLAuthorizationStatusNotDetermined");
[locationManager requestWhenInUseAuthorization];
}
break;
case kCLAuthorizationStatusRestricted:{
NSLog(#"kCLAuthorizationStatusRestricted");
}
break;
case kCLAuthorizationStatusDenied:
{
NSLog(#"kCLAuthorizationStatusDenied");
}
break;
default:{
[locationManager startUpdatingLocation];
}
break;
}
}
// This delegate method will be called in case of iOS 6 or later when location data is fetched successfully.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations{
CLLocation* receivedLocation = [locations lastObject];
[self formatLocationData:receivedLocation];
}
Please check and Let me know what should I do to get the Location Permission Popup while Re-installation of same ipa file.
I'm trying to use MapKit on iOS 8 and I keep getting the error:
Trying to start MapKit location updates without prompting for location authorization. Must call
-[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager
requestAlwaysAuthorization] first.
Looking it up here, I found that I had to implement NSLocationWhenInUsageDescription in my plist but nothing happens and I still get that error in the console. What am I doing wrong?
1.- Add the following lines to your info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>The spirit of stack overflow is coders helping coders</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>I have learned more on stack overflow than anything else</string>
2.- Prompt for location authorization:
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
3.-Update location when user have accepted...
- (void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
[self.locationManager startUpdatingLocation];
}
}
just add those code in CLLocationManager initialization.
for example:
#property (strong, nonatomic) CLLocationManager *locationManager;
then in your implementation just call [self.locationManager requestWhenInUseAuthorization]; before calling the update function.
after your cllocationmanager initilaisation check for ios 8 and ask for the permission
locationManager = [[CLLocationManager alloc] init];// [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
// Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
if ([locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[locationManager requestWhenInUseAuthorization];
}
reference http://nevan.net/2014/09/core-location-manager-changes-in-ios-8/
In addition to implementing the keys in PList (I have NSLocationWhenInUseUsageDescritpion and Privacy - Location Usage Description), I solved the warning with the following code:
if ([CLLocationManager locationServicesEnabled])
{
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusRestricted || status == kCLAuthorizationStatusDenied || status == kCLAuthorizationStatusNotDetermined) {
} else {
_mapView.showsUserLocation = YES;
}
}
I have a switch for the user to disable / enable the use of the location by the application. I am having two problems.
1 - When the native ios popup appears to ask if he wants to allow the use of location, and he says no, the promixa time I request permission popup is no longer displayed, and the only way to enable the user permission It is in the iPhone settings.
2 - If the user has allowed the use of the location, but then at some point you want to disable the switch is present in the application, it can not.
below is the code I am using.
-(IBAction)avancar:(id)sender{
if (locationManager == nil) {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
}
if (switchPermissao.isOn) {
[locationManager startUpdatingLocation];
if(IS_OS_8_OR_LATER) {
[locationManager requestWhenInUseAuthorization];
[locationManager requestAlwaysAuthorization];
}
}else{
[locationManager stopUpdatingLocation];
[self performSegueWithIdentifier:#"tela2" sender:self];
}
}
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusDenied) {
[switchPermissao setOn:NO animated:YES];
}
else if (status == kCLAuthorizationStatusAuthorizedAlways
|| status == kCLAuthorizationStatusAuthorizedWhenInUse) {
[switchPermissao setOn:YES animated:YES];
}
[self performSegueWithIdentifier:#"tela2" sender:self];
}
RE: 1) The OS will only show the permission dialog once. After the user makes their selection, the OS will not show the permission dialog again even if your code asks for permission again. So, you must handle this with your code to present the user with a custom dialog/alert. To do this, use the CLLocationManager authorizationStatus to get the current status. Only when this status is kCLAuthorizationStatusNotDetermined will the OS show the system permission dialog. In all other cases you need to handle the permission with a custom dialog.
RE: 2) Your app cannot change the status of location services, this can only be changed by the user under the system Setting. To handle this you can present a custom dialog that will open the system Settings for your app so the user can change the status. You can use UIApplicationOpenSettingsURLString, in iOS 8, to open the system Settings for your app.
I'm working on a very simple GPS-based app where users can monitor their driving. Typically, this is what a user-driver does :
go in his/her car
launch the app
press a button that says "I'm driving"
drive
press a "I'm arrived" button when arrived to his/her destination
I want the Apple Watch companion app where I would find these two buttons. So there are two scenarios :
A/ When the app is in foreground, there are no problems.
B/ When the app is in background or not started though, I don't get any gps updates. Nothing happens. Nada.
Here's some code.
When user pressed the "I'm driving" button on the watch and the app is not started in the simulator :
// Regular location manager
self.standardLocationManager = [[CLLocationManager alloc] init];
self.standardLocationManager.delegate = self;
self.standardLocationManager.desiredAccuracy = self.accuracy;
self.standardLocationManager.activityType = CLActivityTypeAutomotiveNavigation;
self.standardLocationManager.distanceFilter = self.distanceFilter;
[self.standardLocationManager requestWhenInUseAuthorization];
// Significant Location Change location manager
self.significantLocationChangeLocationManager = [[CLLocationManager alloc] init];
self.significantLocationChangeLocationManager.delegate = self;
self.significantLocationChangeLocationManager.activityType = CLActivityTypeAutomotiveNavigation;
[self.significantLocationChangeLocationManager requestWhenInUseAuthorization];
// Start the managers
[self.standardLocationManager startUpdatingLocation];
[self.significantLocationChangeLocationManager startMonitoringSignificantLocationChanges];
I'm using MWormhole to "debug" what is happening.
The only called delegate method is :
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways){
if (manager == self.significantLocationChangeLocationManager) {
[[WatchController sharedController]notify:#"starting slc loc manager"]; // call MWWormHole to debug
[self.significantLocationChangeLocationManager startMonitoringSignificantLocationChanges];
} else if (manager == self.standardLocationManager) {
[[WatchController sharedController]notify:#"starting standard loc manager"]; // call MWWormHole to debug
[self.standardLocationManager startUpdatingLocation];
}
}
}
In this delegate method, status is Authorized because I have both debug message in my watch app.
My questions :
Is what I want to do even possible in the Apple world ?
What am I doing wrong ? Am I missing something ?
Is this post clear enough ?
I have an app that uses background location updates, it needs to constantly track the devices position regardless if the app is in the foreground or background. I have the background tracking setup in the app delegate.
In the front end I have a single UIViewController with a working mapkit view all the CCLocationManager code triggers without error but didUpdateUserLocation is never fired within the custom UIViewController class.
Background location tracking is working with absolutely no problems.
Here is the code i'm using in viewDidLoad
[self.mapView setShowsUserLocation:YES];
[self.mapView setShowsPointsOfInterest:YES];
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager requestWhenInUseAuthorization];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
[self.locationManager setDelegate:self];
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
NSString *error;
if (![CLLocationManager locationServicesEnabled]) {error = #"Error message";}
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusRestricted || status == kCLAuthorizationStatusDenied) {error = #"Error message";}
if (error) {NSLog(error);}
else
{
status = [CLLocationManager authorizationStatus];
self.locationManager.pausesLocationUpdatesAutomatically = NO;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
}
Any help would be appreciated
Thanks
You need to check below details:
Testing this on simulator?? didUpdateLocations must be tested on real device.
Make sure that you specified the NSLocationAlwaysUsageDescription string or else the app won't present the authorization message, and you won't be authorized.
Have you implemented locationManager:didFailWithError:? did you got any error?
Have you implemented locationManager:didChangeAuthorizationStatus:? Are you getting this called with a successful authorization status?
I worked out the problem finally.
Turns out even if you're using the location stuff in Schema and Xcode you still need to set it in the Simulator via Debug > Location
Hopefully this helps someone else.
and now, with iOS 9, the following needs to be included for any update;
if ([ locationManager respondsToSelector:#selector(requestWhenInUseAuthorization )])
{
locationManager.allowsBackgroundLocationUpdates = YES;
}
Else background updates will be impaired.