I am trying to get the users Lat and Long from GPS data on iphone.
here is my code:
//
// SendLocView.m
// AMR App
//
// Created by Fletcher on 07/04/2016.
// Copyright © 2016 LaserCamel Programing. All rights reserved.
//
#import "SendLocView.h"
#import CoreLocation;
#interface SendLocView ()
#end
#implementation SendLocView
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
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];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (IBAction)location_send:(id)sender {
[self.locationManager requestAlwaysAuthorization];
NSString *Globalusername = #"1111";
float latitude = locationManager.location.coordinate.latitude;
float longitude = locationManager.location.coordinate.longitude;
NSLog(#"Value of username is %#", Globalusername);
NSLog(#"Value of latitude is %f", latitude);
NSLog(#"Value of longitude is %f", longitude);
NSString *post =[[NSString alloc] initWithFormat:#"lat=%f&long=%f&username=%#",latitude,longitude,Globalusername];
NSLog(#"PostData: %#",post);
NSURL *url=[NSURL URLWithString:#"https://www.webaddress.org.uk/mobile/jsontracking.php"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"%#", [locations lastObject]);
}
- (void)requestAlwaysAuthorization
{
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
// If the status is denied or only granted for when in use, display an alert
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusDenied) {
NSString *title;
title = (status == kCLAuthorizationStatusDenied) ? #"Location services are off" : #"Background location is not enabled";
NSString *message = #"To use background location you must turn on 'Always' in the Location Services Settings";
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Settings", nil];
[alertView show];
}
// The user has not enabled any location services. Request background authorization.
else if (status == kCLAuthorizationStatusNotDetermined) {
[self.locationManager requestAlwaysAuthorization];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1) {
// Send the user to the Settings for this app
NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:settingsURL];
}
}
#end
My Current output from NSLog(#"Value of latitude is %f", latitude); is
2016-04-08 21:54:29.170 AMR App[1394:467604] Value of latitude is 0.000000
But the NSLog(#"%#", [locations lastObject]); output is
2016-04-08 21:55:38.186 AMR App[1394:467604] <+51.50483880,-3.16954134> +/- 65.00m (speed -1.00 mps / course -1.00) # 08/04/2016, 21:55:38 British Summer Time
any help as to why this is happening would be great
The documentation for CLLocationManager states that it resolves location information asynchronously, meaning that you cannot make any assumptions about what might be available to you at any given time.
The AppleDoc discussion for -startUpdatingLocation explains why the values are nil in the first log message:
This method returns immediately. Calling this method causes the
location manager to obtain an initial location fix (which may take
several seconds) and notify your delegate by calling its
locationManager:didUpdateLocations: method.
So, you should not expect to have any valid location information until the -locationManager:didUpdateLocations: delegate method is called. Based on the timestamps in your log message, it's clear that the location took about 1 second longer to resolve, which is actually in line with the documented timeframe from Apple engineering.
Note: the docs also suggest implementing the failure method...
In addition to your delegate object implementing the
locationManager:didUpdateLocations: method, it should also implement
the locationManager:didFailWithError: method to respond to potential
errors.
Related
I have a problem with the refresh information on your GPS position.
The function given by me "locationManager" when you click the button does not refresh the information in the "Label".
My code: http://pastebin.com/hWeq6gTS
I am a novice programmer iOS. Please help.
The issue that is not always obvious with the location services is that you have to have one of these two keys in your Info.plist:
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
Then, when starting updating of your position, don't forget to request permissions first (again, depending on your requirements (when in use/always):
[self.locationManager requestWhenInUseAuthorization]
[self.locationManager requestAlwaysAuthorization]
Add these two properties in info.plist
'NSLocationAlwaysUsageDescription' and below property
Create CocoaTouch Class 'LocationManager' inherit from NSObject like below class.
Singleton Location Manager Class .h
#import <Foundation/Foundation.h>
#interface LocationManager : NSObject <CLLocationManagerDelegate>
{
CLLocationManager *locationManager;
}
#property (strong, nonatomic) NSString *longitude;
#property (strong, nonatomic) NSString *latitude;
#property (strong, nonatomic) CLLocation *currentLocation;
+ (instancetype)sharedInstance;
#end
Implementation here .m
#import "LocationManager.h"
#implementation LocationManager
- (id) init
{
self = [super init];
if (self != nil)
{
[self locationManager];
}
return self;
}
+ (instancetype)sharedInstance
{
static LocationManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[LocationManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
- (void) locationManager
{
if ([CLLocationManager locationServicesEnabled])
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
if ([locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)])
{
[locationManager requestWhenInUseAuthorization];
}
[locationManager startUpdatingLocation];
}
else{
UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:#"Location Services Disabled" message:#"You currently have all location services for this device disabled. If you proceed, you will be showing past informations. To enable, Settings->Location->location services->on" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:#"Continue",nil];
[servicesDisabledAlert show];
[servicesDisabledAlert setDelegate:self];
}
}
- (void)requestWhenInUseAuthorization
{
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
// If the status is denied or only granted for when in use, display an alert
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusDenied) {
NSString *title;
title = (status == kCLAuthorizationStatusDenied) ? #"Location services are off" : #"Background location is not enabled";
NSString *message = #"To use background location you must turn on 'Always' in the Location Services Settings";
UIAlertView *alertViews = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Settings", nil];
[alertViews show];
}
// The user has not enabled any location services. Request background authorization.
else if (status == kCLAuthorizationStatusNotDetermined) {
[locationManager requestWhenInUseAuthorization];
}
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
// [errorAlert show];
}
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
case kCLAuthorizationStatusNotDetermined:
case kCLAuthorizationStatusRestricted:
case kCLAuthorizationStatusDenied:
{
// do some error handling
}
break;
default:{
[locationManager startUpdatingLocation];
}
break;
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
CLLocation *location;
location = [manager location];
CLLocationCoordinate2D coordinate = [location coordinate];
_currentLocation = [[CLLocation alloc] init];
_currentLocation = newLocation;
_longitude = [NSString stringWithFormat:#"%f",coordinate.longitude];
_latitude = [NSString stringWithFormat:#"%f",coordinate.latitude];
// globalObjects.longitude = [NSString stringWithFormat:#"%f",coordinate.longitude];
// globalObjects.latitude = [NSString stringWithFormat:#"%f",coordinate.latitude];
}
#end
import
#import "LocationManager.h"
in your AppDelegate.h
and call that in your AppDelegate.m's like this
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[LocationManager sharedInstance];
return true;
}
Then just get
[LocationManager sharedInstance].longitude or latitude for updated lat long.
Instead of using
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation{
// Location update code
}
use this function to get updated location
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
// Assigning the last object as the current location of the device
CLLocation *currentLocation = [locations lastObject];
}
Sometimes we do face problem when we do not check the authorisation status. You can check this code
// Custom initialization code
CLLocationManager *manager = [[CLLocationManager alloc]init];
manager.delegate = self;
manager.desiredAccuracy = kCLLocationAccuracyBest;
// Setting distance fiter to 10 to get notified only after location change about 10 meter
manager.distanceFilter = kCLDistanceFilterNone;
// Requesting for authorization
if ([manager respondsToSelector:#selector(requestWhenInUseAuthorization)]){
[manager requestWhenInUseAuthorization];
}
// Immediately starts updating the location
[manager startUpdatingLocation];
[manager startUpdatingHeading];
if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
// user activated automatic attraction info mode
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusDenied ||
status == kCLAuthorizationStatusAuthorizedWhenInUse ||
status == kCLAuthorizationStatusNotDetermined) {
// present an alert indicating location authorization required
// and offer to take the user to Settings for the app via
// UIApplication -openUrl: and UIApplicationOpenSettingsURLString
[manager requestAlwaysAuthorization];
}
[manager requestWhenInUseAuthorization];
else
[manager requestAlwaysAuthorization];
endif
I created a small application based on ibeacons and location.
I am calling a web service to download some information.
When I call the function "downloadDataFromServerWithMajor" from didFinishLaunchingWithOptions its works ok.
The problem comes when the app is on background and the "startMonitoringSignificantLocationChanges" calls "didUpdateLocations" callback. The "fetchNetworkFromMajor" from Manager.m is firing, and i get the two logs:
NSLog(#"fetchNetworkFromMajor -->PATH:%#", path);
NSLog(#"fetchNetworkFromMajor -->major:%# -->version:%ld -->language:%#", major, (long)version, language);
but since then nothing happens, no response object or error on get function.
[self GET:path parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
NSMutableArray *networks = [[NSMutableArray alloc] initWithCapacity:[responseObject count]];
NSLog(#"responseObject: %#", responseObject );
Network *net = [[Network alloc] initWithJSONDictionary:responseObject];
if (net) {
NSLog(#"NET:%#", net);
[networks addObject:net];
}
successHandler(networks);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"error manager:%#", error);
errorHandler(error);
}];
My code (from AppDelegate.m and Manager.m) is below:
AppDelegate.m
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self downloadDataFromServerWithMajor:#"1" andVersion:1];
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
id locationValue = [launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey];
[self.locationManager startMonitoringSignificantLocationChanges];
NSString *message =[NSString stringWithFormat:#"UIApplicationLaunchOptionsLocationKey %#",locationValue];
[self sendLocalNotificationWithMessage:message];
}
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]) {
NSLog(#"RESPONDS!!!!!");
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound|UIUserNotificationTypeBadge categories:nil]];
}
// Override point for customization after application launch.
NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6A"];
NSString *regionIdentifier = #"iBeacons region 1";
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: beaconUUID identifier: regionIdentifier ];
beaconRegion.notifyEntryStateOnDisplay = YES;
self.locationManager = [[CLLocationManager alloc]init];
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]){
[self.locationManager requestAlwaysAuthorization];
}
self.locationManager.delegate = self;
[self.locationManager startMonitoringForRegion:beaconRegion];
return YES;
}
- (void) locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
if(beacons.count > 0){
for (CLBeacon *beacon in beacons){
NSLog(#"beacon detectado major: %# minor: %#", beacon.major,beacon.minor);
}
}
}
- (void) locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
[manager startRangingBeaconsInRegion:(CLBeaconRegion*) region];
NSLog(#"didEnterRegion --> has entrado en la region");
}
- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
[manager stopRangingBeaconsInRegion:(CLBeaconRegion*) region];
NSLog(#"didExitRegion --> has salido de la region");
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{
if (state == CLRegionStateInside) {
//Start Ranging
[manager startRangingBeaconsInRegion:(CLBeaconRegion*) region];
NSLog(#"didDetermineState -->has entrado en la region");
}else{
//Stop Ranging
[manager stopRangingBeaconsInRegion:(CLBeaconRegion*) region];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
//[self detectBluetooth];
NSString *message = #"didUpdateLocations";
[self sendLocalNotificationWithMessage:message];
[self downloadDataFromServerWithMajor:#"1" andVersion:1];
}
-(void)sendLocalNotificationWithMessage:(NSString*)message {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = message;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSString *message =#"applicationDidEnterBackground";
[self sendLocalNotificationWithMessage:message];
[self.locationManager startMonitoringSignificantLocationChanges];
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void) downloadDataFromServerWithMajor: (NSString *) major andVersion: (NSInteger) version
{
//downloadDataFromServer
NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSLog(#"downloadDataFromServerWithMajor -->major: %# -->version:%ld", major,(long)version);
[[MyFacade sharedInstance] fetchNetworkFromMajor: major andVersion: version andLanguage: language withDelegate:self errorHandler:self];
}
-(void)didEndFetchingNetwork:(NSArray *)network{
NSLog(#"didEndFetchingNetwork");
}
-(void)networkFetchingEror:(NSError *)error
{
NSLog(#"error networkFetchingEror");
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSString *message =#"applicationDidBecomeActive";
[self sendLocalNotificationWithMessage:message];
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#end
Manager.m
-(void)fetchNetworkFromMajor:(NSString *)major andVersion:(NSInteger )version andLanguage: (NSString *) language
successHandler:(void (^)(NSArray *pois))successHandler
errorHandler:(void (^)(NSError *error))errorHandler {
NSString *path = [NSString stringWithFormat:ManagerFetchNetworkRelativeURLFormat,
major,(long)version, language];
NSLog(#"fetchNetworkFromMajor -->PATH:%#", path);
NSLog(#"fetchNetworkFromMajor -->major:%# -->version:%ld -->language:%#", major, (long)version, language);
[self GET:path parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
NSMutableArray *networks = [[NSMutableArray alloc] initWithCapacity:[responseObject count]];
NSLog(#"responseObject: %#", responseObject );
Network *net = [[Network alloc] initWithJSONDictionary:responseObject];
if (net) {
NSLog(#"NET:%#", net);
[networks addObject:net];
}
successHandler(networks);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"error manager:%#", error);
errorHandler(error);
}];
}
I dont understand why the same call works correctly when run from "didFinishLaunchingWithOptions" and when run from "didUpdateLocations" nothing happens.
Any ideas? I'm really frustrated with this issue.
add initialization of Manager
+ (instancetype)sharedInstance
{
static MyManager *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Network activity indicator manager setup
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
// Session configuration setup
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = #{
#"User-Agent" : #"iOS Client"
};
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:10 * 1024 * 1024 // 10MB. memory cache
diskCapacity:50 * 1024 * 1024 // 50MB. on disk cache
diskPath:nil];
sessionConfiguration.URLCache = cache;
sessionConfiguration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
//sessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
// Initialize the session
_sharedInstance = [[MyManager alloc] initWithBaseURL:[NSURL URLWithString:MyManagerBaseURL] sessionConfiguration:sessionConfiguration];
});
return _sharedInstance;
}
EDIT: I added the call [self downloadDataFromServerWithMajor:#"1" andVersion:1]; to the method didRangeBeacons: and when the app detcts a ibeacon on background the call works ok too.
In order to download in the background your NSURLSessionConfiguration has to be initialized as a background session.
Apple Documentation for background downloads
Try this instead in your Manager initialization
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:#"bgDownload"];
#import "MyLocationViewController.h"
#define NSLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#interface MyLocationViewController ()
#end
#implementation MyLocationViewController
{
CLLocationManager *locationManager;
}
- (void)requestAlwaysAuthorization
{
[locationManager requestAlwaysAuthorization];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.mapView.showsUserLocation = YES;
self.mapView.delegate = self;
locationManager = [[CLLocationManager alloc] init];
}
- (IBAction)unwindToMap:(UIStoryboardSegue *)segue
{
}
- (IBAction)getCurrentLocation:(id)sender {
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
MKCoordinateRegion mapRegion;
mapRegion.center = mapView.userLocation.coordinate;
mapRegion.span.latitudeDelta = 0.001;
mapRegion.span.longitudeDelta = 0.001;
CLLocationCoordinate2D location = userLocation.coordinate;
float lat = location.latitude;
float lng = location.longitude;
NSDictionary *locationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:lat] , #"Latitude",
[NSNumber numberWithFloat:lng], #"Longitude", nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
locationDictionary, #"Location_A",
nil];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
NSString *str = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"%#",str);
if (mapView.userLocation != nil)
{
_longitudeLabel.text = [NSString stringWithFormat:#"%.8f", mapView.userLocation.coordinate.longitude];
_latitudeLabel.text = [NSString stringWithFormat:#"%.8f", mapView.userLocation.coordinate.latitude];
}
[mapView setRegion:mapRegion animated: YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This is my code.
Now, I understand that I needed to edit my info.plist file (which I did) with a NSLocationAlwaysUsageDescription key, and I added a string for the description.
However, I'm having issues implementing the authorization portion, as the error still reads as such:
Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.
I've read the Apple IOS 8 docs for the CLLocationManager, but somehow my code will not work.
Can someone help me so that the above error message goes away by looking at where in my code I need to modify so that it works?
Thanks!
You should not turn on MKMapView's showsUserLocation before the user has authorised your app to use Location services.
You can implement CLLocationManagerDelegate's locationManager:didChangeAuthorizationStatus: method and turn on showsUserLocation there like so:
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
self.mapView.showsUserLocation = YES;
}
}
I have a Location.m model, which I use to obtain latitude and longitude. When I have those, I call my delegate method which is implemented in my Foursquare.m model.
Location.h looks like this:
#class Location;
#protocol LocationDelegate <NSObject>
- (void)location:(Location *)loc didFinishFindingLocation:(CLLocation *)location;
#end
#interface Location : NSObject <CLLocationManagerDelegate>
#property (nonatomic, weak) id <LocationDelegate> delegate;
- (void)startLocationManager;
#end
Location.m:
#implementation Location
{
CLLocationManager *_locationManager;
BOOL _locationIsUpdated;
}
- (void)startLocationManager
{
NSLog(#"In startLocationManager");
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
_locationManager.delegate = self;
_locationIsUpdated = NO;
[_locationManager startUpdatingLocation];
}
#pragma mark - LocationManager Delegates
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
if (locations && _locationIsUpdated == NO) {
self.location = [locations lastObject];
NSLog(#"%#", _location);
_locationIsUpdated = YES;
[self.delegate location:self didFinishFindingLocation:self.location];
}
}
#end
When the delegate method is called from the Foursquare model, and I NSLog out the response in an array (called self.dataResponse). It always outputs (null) first, then all the JSON. Why does that happen? If I NSLog out dataDictionary (see code below) directly, the JSON output shows a error-code:400 and nothing is printed. Couple of milliseconds after everything is printed and seems to be working fine...
Here is a picture:
When I log the dataDictionary
Here is the delegate method implemented in Foursquare.m:
- (void)location:(Location *)loc didFinishFindingLocation:(CLLocation *)location
{
loc.delegate = self;
self.resultFromResponse = [[NSMutableArray alloc] init];
NSString *search = [NSString stringWithFormat:#"https://api.foursquare.com/v2/venues/search?ll=%f,%f&radius=2000&categoryId=4d4b7105d754a06374d81259&client_id=%#&client_secret=%#&v=%#", location.coordinate.latitude, location.coordinate.longitude, kFourdquareAPIKey, fFoursquareSecret, [Foursquare getCurrentDate]];
NSURL *url = [NSURL URLWithString:search];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *dataDictionary = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!error) {
self.dataResponse = dataDictionary[#"response"][#"venues"];
NSLog(#"%#", self.dataResponse);
for (NSDictionary *venue in self.dataResponse) {
FSDataFromJSON *data = [[FSDataFromJSON alloc] init];
data.name = venue[#"name"];
data.phone = venue[#"phone"];
data.location = venue[#"location"];
[self.resultFromResponse addObject:data];
}
//[[NSNotificationCenter defaultCenter] postNotificationName:#"JSONRequestFinishedLoading" object:nil];
} else {
NSLog(#"%#", error.description);
}
}];
[task resume];
}
At the very end, these models is started in my MainViewController:
_location = [[Location alloc] init];
[_location startLocationManager];
_foursquare = [[Foursquare alloc] init];
[_foursquare location:_location didFinishFindingLocation:_location.location];
From what I can tell, the last line of code in your question is likely the problem:
[_foursquare location:_location didFinishFindingLocation:_location.location];
You shouldn't be calling your delegate method explicitly (before the location has been set). Instead, you should be waiting for the CLLocation callback to kick it off.
Other thoughts:
If the Location class has a .location property, I think that's confusing.
Location.m is not a model, it's the file containing the class implementation. I'd say the Location class is a model.
You could simplify your delegate method as didFindLocation:(Location *)location since you can get the CLLocation in the method as location.location.
What are the three most important factors in real estate? location.location.location!
I have an app that uses CLLocation manager and I need ability to force a location event to be delivered. Apple documentation states...
After returning a current location fix, the receiver generates update events only when a significant change in the user’s location is detected. For example, it might generate a new event when the device becomes associated with a different cell tower. It does not rely on the value in the distanceFilter property to generate events. Calling this method several times in succession does not automatically result in new events being generated. Calling stopMonitoringSignificantLocationChanges in between, however, does cause a new initial event to be sent the next time you call this method.
So I read that as I can call stopMonitoringSignificantLocationChanges and then startMonitoringSignificantLocationChanges and receive a location event, however, in practice this is not working. See code below (I've removed pieces so as to not disclose private reverse geo APIs). This is a wrapper class to combine reverse geo with CLLocationManager. In my demo app I am calling beginMonitoringSignificantChanges and then stopMonitoringSignificantChanges and I am only seeing -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations called when I change locations or when I first install the app, not when I stop and start. Any thoughts?
//
// TTGeoWrapper.m
// LocationManagementDemoApp
//
// Created by Kyle Jurick on 12/17/12.
// Copyright (c) 2012 Kyle Jurick. All rights reserved.
//
#import "TTGeoWrapper.h"
#import "OpenXML.h"
#implementation TTGeoWrapper
-(id)initWith//removed code
{
self = [super init];
if (self) {
//removed code
_transactionTimeout=30;
//removed code
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
authorizationStatus = [CLLocationManager authorizationStatus];
if (authorizationStatus != kCLAuthorizationStatusAuthorized) {
ableToMonitorLocation = false;
}
else
{
ableToMonitorLocation = true;
}
}
return self;
}
-(void)dealloc
{
locationManager.delegate = nil;
}
-(void)beginMonitoringSignificantChanges
{
//if (ableToMonitorLocation) {
[locationManager startMonitoringSignificantLocationChanges];
/*}
NSMutableDictionary *requestFailureUserInfo = [[NSMutableDictionary alloc] init];
[requestFailureUserInfo setValue:#"NOT AUTHORIZED" forKey:#"FAILURE REASON"];
[requestFailureUserInfo setValue:[NSString stringWithFormat:#"%d", authorizationStatus] forKey:#"FAILURE REASON"];
NSError *requestFailure = [[NSError alloc] initWithDomain:#"TTGeoWrapper" code:0 userInfo:requestFailureUserInfo];
[_delegate requestDidFail:requestFailure TTGeoWrapper:self];*/
}
-(void)stopMonitoringSignificantChanges
{
[locationManager stopMonitoringSignificantLocationChanges];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//per Apple documentation, the last item in the array is the most current location
//http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/CLLocationManagerDelegate/CLLocationManagerDelegate.html
int latestPollIndex = locations.count-1;
CLLocation *location = [locations objectAtIndex:latestPollIndex];
_timeStamp = location.timestamp;
CLLocationCoordinate2D coordinate = [location coordinate];
[self updateLocationWithLatAsync:coordinate.latitude Long:coordinate.longitude];
}
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus: (CLAuthorizationStatus)status
{
if (status != kCLAuthorizationStatusAuthorized) {
ableToMonitorLocation = false;
}
authorizationStatus = status;
}
-(NSString *)updateLocationWithLatAsync:(float)latitude Long:(float)longitude;
{
webRequest = [ASIHTTPRequest requestWithURL:[[NSURL alloc] initWithString:_URL]];
[webRequest setDelegate:self];
[webRequest setTimeOutSeconds:_transactionTimeout];
[self buildPacketLat:latitude Long:longitude];
PostGUID = [self generateUuidString];
[self getPostResponseAsync];
return PostGUID;
}
-(NSString *)updateLocationWithLatSync:(float)latitude Long:(float)longitude
{
webRequest = [ASIHTTPRequest requestWithURL:[[NSURL alloc] initWithString:_URL]];
[webRequest setDelegate:self];
[webRequest setTimeOutSeconds:_transactionTimeout];
[self buildPacketLat:latitude Long:longitude];
PostGUID = [self generateUuidString];
[self getPostResponse];
return PostGUID;
}
-(void)buildPacketLat:(float)latitude Long:(float)longitude
{
//removed code
[webRequest appendPostData:[requestXML dataUsingEncoding:NSASCIIStringEncoding]];
}
- (NSString *)generateUuidString
{
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef str = CFUUIDCreateString(kCFAllocatorDefault, uuid);
NSString *uuidString = (__bridge NSString *)str;
CFRelease(uuid);
CFRelease(str);
return uuidString;
}
-(void)getPostResponse
{
NSLog(#"Beginning Synchronous POST");
[webRequest startSynchronous];
}
-(void)getPostResponseAsync
{
NSLog(#"Beginning Asynchronous POST");
[webRequest startAsynchronous];
}
-(void)cancelAsyncRequest
{
NSLog(#"Cancelling Asynchronous POST");
[webRequest cancel];
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
OpenXML *geoResponse = [[OpenXML alloc] initWithData:[request responseData]];
_LocationToXMLString = [geoResponse openXMLToString];
_LocationToOpenXML = geoResponse;
//removed code
else
{
NSMutableDictionary *requestFailureUserInfo = [[NSMutableDictionary alloc] init];
//removed code
NSError *requestFailure = [[NSError alloc] initWithDomain:#"TTGeoWrapper" code:0 userInfo:requestFailureUserInfo];
[_delegate requestDidFail:requestFailure TTGeoWrapper:self];
}
}
-(void)requestFailed:(ASIHTTPRequest *)request
{
NSError *requestFailure = [request error];
[_delegate requestDidFail:requestFailure TTGeoWrapper:self];
}
#end
Apparently, this works only if you re-init CLLocationManager each time too. The documentation does not seem to support that, but my testing certainly does. If someone has a better answer, I would love to hear it.
You are right. Only a re-init of the CLLocationManager will cause the SignificantLocationChanges update for to occur. If you need a fresh location update, then why not use standard location service? Using the standard you also get a much more accurate fix.