Why is my first reponse null? - ios

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!

Related

How to get location at AppDelegate, and pass the lat and long to another VC and load webview

Edited
I have managed to get a current location by using this. After that I am trying to pass lat and long as a string to another method that loads a webview. A problem is every time I load this VC, it doesn't call a following method
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
and straight away jump to the following method.
-(void)viewWillAppear:(BOOL)animated
How do I get a location first and store it an variables, pass them to another method as a string, append a string and pass it to NSURL and load a webview?
Is a lat and long retained? How do I retain it throughout my project?
What happens when I click on other tab controller and click back on this VC. Does a lat and long be refreshed again?
- (void)viewDidLoad
{
[super viewDidLoad];
geocoder = [[CLGeocoder alloc] init];
if (locationManager == nil)
{
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
dtDate = [[NSMutableArray alloc] init];
self.currentPageIndex = 0;
self.hasAppearedFlag = NO;
}
//=== It doesn't call a following method first.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *newLocation = [locations lastObject];
[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
latitude = [NSString stringWithFormat:#"%f",newLocation.coordinate.latitude];
longitude = [NSString stringWithFormat:#"%f",newLocation.coordinate.longitude];
state = placemark.administrativeArea;
country = placemark.country;
NSLog(#"This is the latitude%#", latitude);
NSLog(#"This is the longitude%#", longitude);
} else {
NSLog(#"This is the error debug%#", error.debugDescription);
}
}];
// Turn off the location manager to save power.
[manager stopUpdatingLocation];
//*** It will start loading this part below
[self setupSegmentButtons];
NSDate *now = [NSDate date];
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/YYYY"];
NSString *dateString = [dateFormatter stringFromDate:now];
[self LoadClasses:dateString];
self.hasAppearedFlag = YES;
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
NSLog(#"Cannot find the location.");
}
//=== Load webview
- (void)LoadClasses : (NSString *)sDate{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
sMemCode = [defaults objectForKey:#"txtMemCode"];
NSLog(#"Load Class This is the memCode:%#", sMemCode);
NSLog(#"Load Class This is the latitude:%#", latitude);
NSLog(#"Load Class This is the longitude:%#", longitude);
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
sURL = appDelegate.gURL;
sURL = [sURL stringByAppendingString:#"/apps/class.asp?"];
sURL = [sURL stringByAppendingString:#"memCode="];
sURL = [sURL stringByAppendingString:sMemCode];
sURL = [sURL stringByAppendingString:#"&dtpClass="];
sURL = [sURL stringByAppendingString:sDate];
sURL = [sURL stringByAppendingString:#"&lat="];
sURL = [sURL stringByAppendingString:latitude];
sURL = [sURL stringByAppendingString:#"&long="];
sURL = [sURL stringByAppendingString:longitude];
NSLog(#" The sURL to load for the current page : %# ", sURL);
NSURL *url = [NSURL URLWithString:sURL];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[webView loadRequest:urlRequest];
[webView setDelegate:(id<UIWebViewDelegate>)self];
}
Solution
I have put the `Location Delegates` to `AppDelegates`
AppDelegates.h
#import <UIKit/UIKit.h>
#import<CoreLocation/CoreLocation.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>{
CLLocationManager *locationManager;
}
#property (nonatomic, strong) NSString *slatitude;
#property (nonatomic, strong) NSString *slongitude;
#end
AppDelegates.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- Get current location ---
if (locationManager == nil)
{
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
//-- Pop up authrorization to use current location ---
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}
- (void)locationManager: (CLLocationManager *)manager didUpdateToLocation: (CLLocation *)newLocation fromLocation: (CLLocation *)oldLocation
{
float latitude = newLocation.coordinate.latitude;
slatitude = [NSString stringWithFormat:#"%f",latitude];
float longitude = newLocation.coordinate.longitude;
slongitude = [NSString stringWithFormat:#"%f", longitude];
NSLog(#"App:This is the latitude%#", slatitude);
NSLog(#"App:This is the longitude%#", slongitude);
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
NSLog(#"Cannot find the location.");
}
In any VC,
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
latitude = appDelegate.slatitude;
longitude = appDelegate.slongitude;
I would suggest you move your code of Location Delegates to your Appdelegate File, so that it registers and keeps updating to the location delegate methods,
You can either make two property variabled from your appdelegate file and keep updating them as the delegate is called or make a getter or setter method something like this:
#interface AppDelegate : UIResponder<UILocationDelegate>{
CLLocationManager * locationManager;
}
#property (nonatomic, strong) NSString *lattitude;
#property (nonatomic, strong) NSString *longitude;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
if (locationManager == nil)
{
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}
and then Implement the location delegate method here itself as
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
self.latitude = [NSString stringWithFormat:#"%f",newLocation.coordinate.latitude];
self.longitude = [NSString stringWithFormat:#"%f",newLocation.coordinate.longitude];
state = placemark.administrativeArea;
country = placemark.country;
NSLog(#"This is the latitude%#", latitude);
NSLog(#"This is the longitude%#", longitude);
}
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
NSLog(#"Cannot find the location.");
}
Late you can access this from your ViewController as
- (void)viewDidAppear:(BOOL)animated {
AppDelegate* appdelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSString *lat = appdelegate.lattitude;
NSString *long = appdelegate.longitude;
}
In this manner even if you switch tab bars or change controllers, you will still have the latest locations.
Actually You are calling your method in ViewWillAppear and you are getting data in didUpdateLocations .. There is no guarntee that ViewWillappear will call after that,, better you call your method from didUpdateLocations after you recieve the data

Pass Data to another view controller

I have a two view controllers (DatePickerViewController and RouteHistoryViewController). I also have the server response in DatePickerViewController. How can I pass that response to the RouteHitoryViewController. The RouteHistoryViewController has a map view.
Here is the code DatePicker.m :
#import "DatePickerViewController.h"
#import "MapAnnotation.h"
#interface DatePickerViewController ()
#end
#implementation DatePickerViewController
{
//#define URL #"http://140e3087.ngrok.com"
#define URL3 #"http://784effb4.ngrok.com/bustracking/json/student/route_history"
NSString *formatedDate;
NSString *lat;
NSString *longi;
NSString *server_created_date;
}
#synthesize appDelagate,datePicker;
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"%#", appDelegate);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm"];
formatedDate = [dateFormatter stringFromDate:self.datePicker.date];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)sendPicker:(id)sender;
{
[self sendDataToServer : #"GET"];
// NSLog(#"%#", formatedDate);
//self.selectedDate.text =formatedDate;
}
-(void) sendDataToServer : (NSString *) method{
NSString *beaconiD = #"EC112729B51B";
NSString *trackerID = #"e61078a67e4233ad";//appDelagate.tracker_id;
NSString *date = formatedDate;
NSMutableURLRequest *request = nil;
NSString *getURL = [NSString stringWithFormat:#"%#?beacon_id=%#&tracker_id=%#&date=%#", URL3, beaconiD, trackerID, date];
getURL = [getURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString: getURL];
request = [NSMutableURLRequest requestWithURL:url];
NSLog(#"link: %#", getURL);
[request setHTTPMethod:#"GET"];
[request addValue: #"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"connection: %#", connection);
if( connection )
{
mutData = [NSMutableData new];
}
else
{
NSLog (#"NO_CONNECTION");
return;
}
}
#pragma mark NSURLConnection delegates
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *)response
{
[mutData setLength:0];
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[mutData appendData:data];
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog (#"NO_CONNECTION");
return;
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// NSString *jsonresultString =[jsonresultDict objectForKey:#"result"];
// NSLog(#"%#", jsonresultString);
// //serverResponse.text = jsonresultString;
NSError *error = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:mutData options:kNilOptions error:&error];
NSArray *fetchedArr = [json objectForKey:#"result"];
for (NSDictionary *user in fetchedArr)
{
lat = [user objectForKey:#"latitude"];
longi = [user objectForKey:#"longitude"];
server_created_date = [user objectForKey:#"server_created_date"];
NSLog(#"Item date&time : %#", server_created_date);
NSLog(#"Item longitude : %#", longi);
NSLog(#"Item latitude : %#", lat);
}
}
Here is the code RouteHistory.m:
#import "RouteHistoryViewController.h"
#import "MapAnnotation.h"
#interface RouteHistoryViewController ()
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#end
#implementation RouteHistoryViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView.delegate = self;
//[self.mapView removeAnnotations:self.mapView.annotations];
MapAnnotation *mapPoint = [[MapAnnotation alloc] init];
mapPoint.coordinate = CLLocationCoordinate2DMake([self.appDelagate.latitude doubleValue], [self.appDelagate.longitude doubleValue]);
mapPoint.title = self.appDelagate.name;
mapPoint.time = self.appDelagate.server_created_date;
mapPoint.mapimage = self.appDelagate.image;
// Add it to the map view
[self.mapView addAnnotation:mapPoint];
// Zoom to a region around the pin
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(mapPoint.coordinate, 500, 500);
[self.mapView setRegion:region];
//testing
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"MapUpdate"
object:nil];
// Do any additional setup after loading the view.
}
#pragma mark - MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKPinAnnotationView *view = nil;
static NSString *reuseIdentifier = #"MapAnnotation";
// Return a MKPinAnnotationView with a simple accessory button
view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
if(!view)
{
view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
view.canShowCallout = YES;
view.animatesDrop = YES;
}
return view;
}
datepicker to route history using prepareforsegue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"b"])
{
RouteHistoryViewController *rh = segue.destinationViewController;
NSLog(#"%#", rh);
}
}
In first view controller inside connectionDidFinishLoading after this line
NSArray *fetchedArr = [json objectForKey:#"result"];
add the following two lines
_responseFromServer = fetchedArr;
[self performSegueWithIdentifier:#"segueToRouteHistory" sender:self];
and then add this method in your first View Controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"segueToRouteHistory"])
{
RouteHistoryViewController *routeHistoryController = [segue segueToRouteHistory];
[routeHistoryController setFetchedArray:_responseFromServer];
}
}
In your First View Controller .h file add this
#property NSArray *responseFromServer;
Now We have assigned the Response array received from server to a object in your destination view controller.
Don't forget to add
#property NSArray *fetchedArray;
inside your Second ViewController's .h file
Now you can access this array in second view controller.
PS: Do not forget to give segue from storyboard from first view controller to second view controller and name the Segue Identifier as "segueToRouteHistory"

how to find the current location in iOS

now i find the my current location in simulator
when press button show my current location
but my app located other locations
.h
#import <MapKit/MapKit.h>
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController<CLLocationManagerDelegate>
#property (nonatomic,retain)MKMapView *mapView;
- (IBAction)myview:(id)sender;
#property (strong, nonatomic) IBOutlet CLLocationManager *locationManger;
#end
.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)myview:(id)sender {
_locationManger =[[CLLocationManager alloc]init];
_locationManger.distanceFilter=kCLDistanceFilterNone;
_locationManger.desiredAccuracy=kCLLocationAccuracyHundredMeters;
[_locationManger startUpdatingLocation];
[_mapView setMapType:MKMapTypeStandard];
[_mapView setZoomEnabled:YES];
[_mapView setScrollEnabled:YES];
MKCoordinateRegion region={ {0.0,0.0 },{0.0,0.0}};
region.center.latitude=_locationManger.location.coordinate.latitude;
region.center.longitude=_locationManger.location.coordinate.longitude;
region.span.longitudeDelta=0.007f;
region.span.latitudeDelta=0.007f;
[_mapView setRegion:region animated:YES];
[_mapView setDelegate:sender];
}
#end
i want when button press my current location show in map
use this following link it is very hopeful for you to find the current Location and etc, the link is http://www.appcoda.com/how-to-get-current-location-iphone-user/
Step 1: #import <MobileCoreServices/MobileCoreServices.h> in header file
Step 2: Add delegate CLLocationManagerDelegate
#interface yourViewController : UIViewController<CLLocationManagerDelegate>
{
CLLocationManager *locationManager;
CLLocation *currentLocation;
}
Step 3: Add this code in class file
- (void)viewDidLoad
{
[super viewDidLoad];
[self CurrentLocationIdentifier]; // call this method
}
Step 4: Method to get location
//------------ Current Location Address-----
-(void)CurrentLocationIdentifier
{
//---- For getting current gps location
locationManager = [CLLocationManager new];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
//------
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
currentLocation = [locations objectAtIndex:0];
[locationManager stopUpdatingLocation];
CLGeocoder *geocoder = [[CLGeocoder alloc] init] ;
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error)
{
if (!(error))
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"\nCurrent Location Detected\n");
NSLog(#"placemark %#",placemark);
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
NSString *Address = [[NSString alloc]initWithString:locatedAt];
NSString *Area = [[NSString alloc]initWithString:placemark.locality];
NSString *Country = [[NSString alloc]initWithString:placemark.country];
NSString *CountryArea = [NSString stringWithFormat:#"%#, %#", Area,Country];
NSLog(#"%#",CountryArea);
}
else
{
NSLog(#"Geocode failed with error %#", error);
NSLog(#"\nCurrent Location Not Detected\n");
//return;
CountryArea = NULL;
}
/*---- For more results
placemark.region);
placemark.country);
placemark.locality);
placemark.name);
placemark.ocean);
placemark.postalCode);
placemark.subLocality);
placemark.location);
------*/
}];
}
-(void)getLocationCurrentAddresslatitude:(NSString *)lat andlongitude:(NSString *)longitude
{
NSHTTPURLResponse *response = nil;
NSString *jsonUrlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/geocode/json?latlng=%#,%#&key=gfhhfhfhfghfghfhghfghfghDyk&result_type=street_address",lat,longitude];
NSLog(#"%#",jsonUrlString);
NSURL *url = [NSURL URLWithString:[jsonUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//-- Get request and response though URL
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
//-- JSON Parsing
NSDictionary * rootDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
NSArray * result = [rootDictionary objectForKey:#"results"];
NSDictionary *dic=[result objectAtIndex:0];
NSString *address=[dic objectForKey:#"formatted_address"];
self.myAddress.text=address;
}

Access to a class property dependent on asyncronous content

The goal I want to achieve:
I want to display the user address in a view. The view is taken care of by a view controller . This VC is using the GeoAPI to get access to the user location. The GeoAPI is wrapped around the LocationManager-Class which in turn is taking care of all the locationmanager stuff. In this location Manager class I would like to reversegeocode as mentioned above.
The geoAPI has some convenience methods that can be used to get the information I want.
GeoAPI:
#import <Foundation/Foundation.h>
#interface MFGeoAPI : NSObject
#property (nonatomic, strong) NSDictionary *userLocationInfoDict;
+ (MFGeoAPI *)sharedInstance;
- (CLLocation *)getCurrentUserLocation;
#end
#interface MFGeoAPI () {
LocationManager *locationManager;
MotionManager *motionManager;
}
#end
#implementation MFGeoAPI
+(MFGeoAPI *)sharedInstance {
static MFGeoAPI *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[MFGeoAPI alloc] init];
});
return _sharedInstance;
}
- (id)init {
if (self = [super init]) {
locationManager = [[LocationManager alloc] initLocationManager];
motionManager = [[MotionManager alloc] initMotionManager];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getUserLocationInformation:) name:#"UserLocationInfo" object:nil];
return self;
}
- (CLLocation *)getCurrentUserLocation {
return [locationManager getCurrentUserLocation];
}
- (void)getUserLocationInformation:(NSNotification *)notification {
NSLog(#"OK");
self.userLocationInfoDict = [[NSDictionary alloc] initWithDictionary:notification.userInfo];
}
#end
Now.. due to the fact of the async character of the reverse geocode function I won't be sure when the dictionary with the user address will be available.
The Location Manager class contains the following:
Location Manager:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface LocationManager : NSObject <CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) CLLocation *userLocation;
#property (nonatomic, strong) NSDictionary *userLocDict;
- (id)initLocationManager;
- (CLLocation *)getCurrentUserLocation;
#end
#import "LocationManager.h"
#implementation LocationManager
- (id)initLocationManager {
if (self = [super init]) {
if (self.locationManager == nil) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if (![self.locationManager locationServicesEnabled]) {
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 asked to confirm whether location services should be reenabled." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[servicesDisabledAlert show];
}
}
[self startUpdatingLocation];
[self startFetchingCurrentUserLocationInformation];
}
return self;
}
- (void)startUpdatingLocation {
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.headingFilter = kCLHeadingFilterNone;
[self.locationManager startUpdatingHeading];
[self.locationManager startUpdatingLocation];
self.userLocation = self.locationManager.location;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"FAILED");
}
- (CLLocation *)getCurrentUserLocation {
return self.userLocation;
}
- (void)startFetchingCurrentUserLocationInformation {
[self delayedReverseGeocodeLocation];
}
- (void)delayedReverseGeocodeLocation {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self reverseGeocodeLocation];
}
- (void)reverseGeocodeLocation {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:self.userLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSDictionary *dict = [[placemarks firstObject] addressDictionary];
if (placemarks.count > 0) {
NSDictionary *info = [NSDictionary dictionaryWithObject:dict forKey:#"transferDict"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"UserLocationInfo" object:self userInfo:info];
}
}];
}
In the view Controller I would like to get access to the userLocation Information Dictionary that I am passing through the API
View Controller
- (id)init {
if (self = [super init]) {
self.userLocation = [[MFGeoAPI sharedInstance] getCurrentUserLocation];
NSLog(#"%#", [[MFGeoAPI sharedInstance].userLocationInfoDict description]);
}
return self;
}
But the userlocationInfoDict is empty. I guess this is due to the fact that my function-call "startFetchingCurrentUserLocationInformation" is returning before the async block returns the values I need. Then I thought by taking care of this through NSNotificationCenter I will be informed when the data is available. But now I am stuck:
What do I have to do to get access to the userLocationInformation passed by the GeoAPI to do it in time when the data has arrived?
Any help appreciated!
Thank you
Sebastian

CLLocationManager SignificantLocationChanges Force Refresh

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.

Resources