Can't get data from Google Places - iphone

I am trying to get the google places API to work on my iPhone project. Now, I had it working about an hour ago, but I can't seem to figure out what I did to make it stop working. Any help would be appreciated.
Here is what I have so far:
- (NSString *)searchString {
// this mutable string allows me to dynamically create the search string
// we start with the static part of the api search URL
NSMutableString *result = [NSMutableString stringWithString:#"https://maps.googleapis.com/maps/api/place/search/json?location="];
// since I need to get the user's location, I need to create a location manager
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
// we need to now update the current location,
// otherwise there will be no coordinates
[locationManager startUpdatingLocation];
// now that it's updated, we stop it because I
// am not tracking anything
[locationManager stopUpdatingLocation];
// this appends the lattitude/longitude, as double values, into the URL
[result appendFormat:#"%g,%g", [[locationManager location] coordinate].latitude, [[locationManager location] coordinate].longitude];
// release the location manager for memory management
[locationManager release];
// if a filter is present, add the keyword item to try to filter
// the results
if([[self filterString] length] > 0) {
[result appendFormat:#"&keyword=%#", filterString];
}
// add the rest of the validated URL now
//[result appendString:#"&types=food|meal_delivery|meal_takeaway|restaurant&rankby=distance&sensor=true&key=AIzaSyBmO_f6h4_Q0xArw6tdxUF7TH7rZpaiFfQ"];
[result appendString:#"&types=food&rankby=distance&sensor=true&key=mykey"];
// log the result for testing
NSLog(#"Completed Search String: %#", result);
return result;
}
Now, when I look at my log, copy the 'completed search string' into Safari, it brings up the results that I need.
But if I use the following code, the app hangs:
- (void)performSearch {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self searchString]]]; // hangs on this line!
NSDictionary *jsonDictionary = [data objectFromJSONData];
NSArray *resultsArray = [jsonDictionary objectForKey:#"results"];
currentList = [ARGooglePlace placesWithArray:resultsArray];
[self.tableView reloadData];
}
I think I should mention that I am using the JSONKit to do the JSON parsing. Also, the ARGooglePlace is a custom class that isn't relevant right now (it doesn't even get there...)
Thanks for any help that you can provide.

Pull the location manager and the lat/long out of the searchString method... just put it in the performSearch method. And instead, pass the lat/long as received from location manager into searchString.
It sounds like a timing issue with your location manager. It could have been working earlier b/c location manager had previously cached location data... and was able to grab the correct coords.
2+ Possible scenarios:
1) problem with location manager not updating it's coords and just hanging there
2) google website being the culprit (maybe loading too much data??)
Pulling location manager out of the searchString method will help isolate cause... and one can just pass lat/long values directly to test google website as well.

Related

how to avoid calling location did update for three times

location did update calling three time and location did fail with error not call even when no data receive
how to avoid calling location did update three times and call did fail with error for no data receive?
im not taking about internet connection
How to avoid calling three times location did update?
for ios location update delegate may call several time depend upon on speed not three times
so use bool variable to indicate
How to call did fail with error for no internet connection?
you have set location manger delegate with self
How to avoid calling three times location did update?
Set location manager's delegate to nil in this method.
How to call did fail with error for no internet connection?
Why should it be called? Locations may come from three sources: GPS radio, cellular and WiFi, but not from the internet.
LocationManager didUpdateToLocation always check for change in location. If it fing multiple location it will call multiple time. You can avoid it in different way. But this will not update the location if user changes his current place.
You can user this code just to get location and proceed further.
-(void)getLocation{
locationManager = [[CLLocationManager alloc] init] ;
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
// [locationManager startUpdatingLocation]; commented to avoid multiple calling or you can say updating current location
CLLocation *newLocation=[locationManager location];
CLGeocoder * geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSString * location = ([placemarks count] > 0) ? [[placemarks objectAtIndex:0] locality] : #"Not Found";
NSLog(#"location:%#",location);
NSLog(#"lat:%f",locationManager.location.coordinate.latitude);
NSLog(#"long:%f",locationManager.location.coordinate.longitude);
}];
}

Geocode + MKPointAnnotation not playing nicely

I'm having the weirdest issue, and it's doing my head in. A global variable which I've set up within a Singleton is reporting correctly from within the function it's set in, then as NULL from within the very next function (which is where I need to access it), but as correct from another View! So the variable is correctly set, but it's not behaving within a certain function. There is also a weird error warning being generated by the offending line (which I've marked between *).
The warning is:
Property access result unused - getters should not be used for side effects.
Apologies for the very spotty code. I'm prototyping and learning as I go, so it's a mishmash of things I've cobbled from the net. What the code does is recognise a long tap on a mapview, and then places a pin at the location (while recording the location), and I'm trying to use Geocode to show the address at the pin position.
The first function is as follows:
- (void)handleLongPress:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan)
return;
CGPoint touchPoint = [gestureRecognizer locationInView:self.fullMapView];
CLLocationCoordinate2D touchMapCoordinate =
[self.fullMapView convertPoint:touchPoint toCoordinateFromView:self.fullMapView];
//save new birthplace in global variable
globalsSingle.gblBirthplace = touchMapCoordinate;
//place user location and record it
MKUserLocation *location = fullMapView.userLocation;
globalsSingle.gblCurrentLocation = location.coordinate;
//first remove any previous birthplace pin
[self removeAllPinsButUserLocation];
[self reverseGeocode];
//place new birthplace pin
MKPointAnnotation *birthPlacePin = [[MKPointAnnotation alloc] init];
birthPlacePin.coordinate = touchMapCoordinate;
birthPlacePin.title = #"My Birthplace";
**** birthPlacePin.subtitle = #"%#", globalsSingle.gblAddress; ****
[self.fullMapView addAnnotation:birthPlacePin];
NSLog(#"gblAddress = %#", globalsSingle.gblAddress);
}
The above function calls the next:
-(void)reverseGeocode {
CLGeocoder *ceo = [[CLGeocoder alloc]init];
CLLocation *loc = [[CLLocation alloc]initWithLatitude:globalsSingle.gblBirthplace.latitude longitude:globalsSingle.gblBirthplace.longitude]; //insert your coordinates
[ceo reverseGeocodeLocation: loc completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
// save the address text
globalsSingle.gblAddress = locatedAt;
NSLog(#"addressDictionary %#", placemark.addressDictionary);
NSLog(#"placemark %#",placemark.region);
NSLog(#"placemark %#",placemark.country); // Give Country Name
NSLog(#"placemark %#",placemark.locality); // Extract the city name
NSLog(#"location %#",placemark.name);
NSLog(#"location %#",placemark.ocean);
NSLog(#"location %#",placemark.postalCode);
NSLog(#"location %#",placemark.subLocality);
NSLog(#"location %#",placemark.location);
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
NSLog(#"gblAddress from reverse Geocode = %#", globalsSingle.gblAddress);
}
];
}
What's even weirder (to me) is that the NSLog's from within reverseGeocode are all printing correctly, but the NSLog from the first function is reporting NULL, and is printing before the one from reverseGeocode even though it's (I assume) being executed second! For example, a debug output is:
2013-05-21 23:41:04.662 Project Name[5659:c07] gblAddress = (null)
2013-05-21 23:41:04.808 Project Name[5659:c07] gblAddress from reverse Geocode = Januária - MG, Brazil
Any help anyone could be bothered to offer I'd appreciate, as I'm bamboozled :)
The method reverseGeocodeLocation:completionHandler: is executed asynchronously, which means that it will move on to the next lines before it finishes.
Asynchronous vs synchronous execution, what does it really mean?
It is called asynchronously because the method reverseGeocodeLocation:completionHandler: might need some time to do it, and when it is finished, the completion block is called.
You should place the new birthplace pin only after the completion block of the reverseGeocodeLocation is called, for example inside the completion block, to ensure you have got the placemark data first before placing the pin. Or you can just update the subtitle of the newly added pin inside the completion block.
[ceo reverseGeocodeLocation: loc completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
// save the address text
globalsSingle.gblAddress = locatedAt;
//place new birthplace pin
MKPointAnnotation *birthPlacePin = [[MKPointAnnotation alloc] init];
birthPlacePin.coordinate = globalsSingle.gblBirthplace;
birthPlacePin.title = #"My Birthplace";
birthPlacePin.subtitle = globalsSingle.gblAddress;
[self.fullMapView addAnnotation:birthPlacePin];
}
];
}
When you are calling [self reverseGeocode]; the rest of handleLongPress will continue to run without waiting for reverseGeocode to finish. This is why you are seeing the print functions being called in an order you weren't expecting.
[self performSelectorOnMainThread:#selector(reverseGeocode) withObject:nil waitUntilDone:YES];
If handleLongPress is running on the main thread, the above line can replace [self reverseGeocode] and should produce the expected results.

geocode method not executing second time in the for loop

I have written following code to find out the coordinates of list of countries.
int count=[objCountries.countryName count];
CLGeocoder *geoCode = [[CLGeocoder alloc] init];
for(int i=0;i<count;i++)
{
NSString *strCountry=[[NSString alloc]initWithString:[objCountries.countryName objectAtIndex:i]];
[geoCode geocodeAddressString:strCountry completionHandler:^(NSArray *placemarks, NSError *error)
{
if (!error)
{
CLPlacemark *place = [placemarks objectAtIndex:0];
CLLocation *location = place.location;
CLLocationCoordinate2D coord = location.coordinate;
NSString *tempLati=[[NSString alloc]initWithFormat:#"%g",coord.latitude];
NSString *tempLongi=[[NSString alloc]initWithFormat:#"%g",coord.longitude];
NSLog(#"-------------------------");
NSLog(#"Country : %#",strCountry);
NSLog(#" Latitude : %# ",tempLati);
NSLog(#" Longitude : %# ",tempLongi);
[objCountries.countryLatitude addObject:tempLati];
[objCountries.countryLongitude addObject:tempLongi];
[db insertAsiaCountry:strCountry :tempLati :tempLongi];
}
}];
}
}
In my countryName array there 20 objects available
Problem:
It's working Fine first time only. But Second Time when for loop executing
[geocode geo....] method is not calling. I Can't understand what to do ? Please Help. Thank You Sir.
after this line..
[db insertAsiaCountry:strCountry :tempLati :tempLongi];
add
[geoCode cancelGeocode];
even if error occurs try allocating geoCoder wihtin the 'for' loop
You cant call any other Method in for loop because loop doesn't wait for until the work is finished. In your code you're calling geoCode method in for loop that is wrong. Try to call that method where your first method is being finished.
CLGeocoder is not intended to be used in a batch fashion as you are trying, so it is possible that subsequent requests are being rejected by the Apple servers. Have a look at the CLGeocoder reference documentation for guidelines on how to use this.
Copy and paste this data need-a-list-of-all-countries-in-the-world into a spreadsheet and save it as a csv. Add this file to the project. Then your app reads the file and creates the annotations as an array which you then add to the mapview.

iPhone One Time Events Programming

How can I make my application display something only when they first launch the application for the first time. Example: They open up my app, an alert comes up, saying something like, "Do you want to play the tutorial?" Then, if they close the app, then re-open it, it won't show up again.
Thanks
I'd recommend using NSUserDefaults:
- (void)openOneTime
{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
static const NSString* kKey = #"One Time Key";
NSObject* keyValue = [defaults objectForKey:kKey];
if (keyValue == nil)
{
[self doMyOneTimeThing]; // pop a dialog, etc...
}
// Adds an object for our key which will get found the next time around,
// bypassing the above code block. The type and value of the object is
// not important; what matters more is that an object exists for that
// key at all.
[defaults setBool:YES forKey:kKey];
}
More tips on storing data persistently:
Method 1: Use the global user preferences system. You can do this, but it might be considered slightly hacky because it is designed to store user preferences, and I think this is a gray area, since the user doesn't have explicit control here. In any case, check out the docs for NSUserDefaults to find out how to do that.
Method 2: Write to a file whose existence indicates whether or not the tutorial has been viewed. You can easily create a file with an NSData object by calling its writeToFile:atomically: method. Later, you can use the NSFileManager class to check if that file exists or not.
Sample code:
- (NSString*) filename {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"notFirstTime"];
}
- (void) setNotFirstTime {
NSData* data = [[[NSData alloc] init] autorelease];
[data writeToFile:[self filename] atomically:YES];
}
- (BOOL) isNotFirstTime {
return [[NSFileManager defaultManager] fileExistsAtPath:[self filename]];
}
You could store in your property store a boolean value saying whether it's the first time or not, then check that on application start.

iphone gps cllocation and making variables globally accessible

I'm pretty new to iPhone development and have been trying to work out how to include GPS information into an app I'm working on.
I've gone through the HelloThere tutorial, which is a great start
http://www.mobileorchard.com/hello-there-a-corelocation-tutorial/
And had no problems getting this to run on my iPhone. I then took the example and have since been trying to incorporate the GPS info into a much larger and more complicated app. The larger application has an existing function which will send a post request to the server, and I'd like to simply provide the location data, specifically the coordinate.latitude and coordinate.longitude to this function, if possible without altering it.
This is pretty trivial in the other languages I've worked with but it's turned out to be quite challenging in objective C.
Basically, as per the tutorial I have gotten to the point where I'm logging the location info,
//GPS stuff
- (void)locationUpdate:(CLLocation *)location {
//locationLabel.text = [location description];
locationString = [location description];
locationLabel.text = locationString;
locLat = [NSString stringWithFormat:#"%lf", location.coordinate.latitude];
locLong = [NSString stringWithFormat:#"%lf", location.coordinate.longitude];
}
but I can't figure out how I can then make the locLat and locLong variables available to other parts of the application. Pretty lame but I'm still a bit lost with objective C.
There are many ways to do this. The quick and dirty way (and some will frown upon it) is to just declare those as globals in this file and use extern to access them from other files.
Better is to make those #properties of the class, and provide a getter so you can access those from another class or part of the app. That does assume that this class will be available for other classes to access later on.
You also can use delegate to get information. And...
Thinking a bit more, I would probably store data like this someplace else, and will use this routine to update the value in that location (by using a setter of that class), so this method here would just get the location and then store it elsewhere.
You might want to read Scott Knaster's book on Objective C and Mac development for a primer on Obj C.
Here's how I recommend doing it:
Store lat/long in a dictionary and fire them off as strings bundled in a notification. Setup an observer in the application delegate and have the callback function store the lat/long in class properties of the application delegate and/or store them in the application defaults.
In your class where you acquire the coordinates:
- (void)locationUpdate:(CLLocation *)location {
NSString *locationString, *locLat, *locLong;
locationString = [location description];
locLat = [NSString stringWithFormat:#"%lf", location.coordinate.latitude];
locLong = [NSString stringWithFormat:#"%lf", location.coordinate.longitude];
NSDictionary *locationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:locationString, #"description",
locLat, #"latitude", locLong, #"longitude", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateSearchLocation"
object:self userInfo:locationDictionary];
}
In your application delegate class:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Listen for search coordinates broadcast
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(setCoordinates:)
name:#"updateSearchLocation" object:nil];
}
- (void)setCoordinates:(id)sender {
self.latitude = [[sender userInfo] objectForKey:#"latitude"];
self.longitude = [[sender userInfo] objectForKey:#"longitude"];
NSLog(#"location = %#", [[sender userInfo] objectForKey:#"description"]);
}
Dont forget to setup the class properties in the application delegate header file as NSString. You can then access the coordinates by calling directly from the application delegate:
YourAppDelegateClassName *appDelegate = [[UIApplication sharedApplication] delegate];
NSLog(#"lat = %#, long = %#", appDelegate.latitude, appDelegate.longitude);
Or you can access them anywhere from the user defaults:
[[NSUserDefaults standardUserDefaults] objectForKey:#"latitude"];
[[NSUserDefaults standardUserDefaults] objectForKey:#"longitude"];
I hope that helps.