How to convert CLLCoordinate2D lat/long into NSNumber - ios

I'm trying to use Yelp API, and i'm trying to have the lat/long as the params for the API search. However, it does not take the type double, it only accepts Objective-C objects. Having no knowledge in Objective-C, what do you suggest the type for the parameter of lat and long be? I tried NSNumber, but when i try to turn my lat/long coordinates of type CLLocationCoordinate2D to an NSNumber that takes in a double, its value is nil
Here is my Yelp API that I am using:
- (void)queryTopBusinessInfoForTerm:(NSString *)term location:(NSString *)location latitude:(NSNumber *)latitude longitude:(NSNumber *)longitude completionHandler:(void (^)(NSDictionary *topBusinessJSON, NSError *error))completionHandler {
NSLog(#"Querying the Search API with term \'%#\' and location \'%#'", term, location);
//Make a first request to get the search results with the passed term and location
NSURLRequest *searchRequest = [self _searchRequestWithTerm:term location:location latitude:latitude longitude:longitude];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:searchRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (!error && httpResponse.statusCode == 200) {
NSDictionary *searchResponseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *businessArray = searchResponseJSON[#"businesses"];
if ([businessArray count] > 0) {
NSDictionary *firstBusiness = [businessArray firstObject];
NSString *firstBusinessID = firstBusiness[#"id"];
NSLog(#"%lu businesses found, querying business info for the top result: %#", (unsigned long)[businessArray count], firstBusinessID);
[self queryBusinessInfoForBusinessId:firstBusinessID completionHandler:completionHandler];
} else {
completionHandler(nil, error); // No business was found
}
} else {
completionHandler(nil, error); // An error happened or the HTTP response is not a 200 OK
}
}] resume];
}
And here is the params
- (NSURLRequest *)_searchRequestWithTerm:(NSString *)term location:(NSString *)location latitude:(NSNumber *) latitude longitude:(NSNumber *)longitude {
NSDictionary *params = #{
#"term": term,
#"location": location,
#"cll": latitude,
#"cll": longitude,
#"limit": kSearchLimit
};
return [NSURLRequest requestWithHost:kAPIHost path:kSearchPath params:params];
}
And here is my current Swift method calling the Query from YelpAPi:
func yelpApi() {
var latitude = NSNumber(double: businessStreetAddress.latitude)
var longitude = NSNumber(double: businessStreetAddress.longitude)
var searchTerm: NSString = "Asian Food";
var defaultLocation: NSString = "New York"
var APISample:YPAPISample = YPAPISample();
var requestGroup:dispatch_group_t = dispatch_group_create();
APISample.queryTopBusinessInfoForTerm(searchTerm as String, location: defaultLocation as String, latitude: latitude, longitude: longitude) { (topBusinessJSON: [NSObject: AnyObject]!, error: NSError!) -> Void in
if((error) != nil) {
println("Error happened during the request" + error.localizedDescription);
} else if((topBusinessJSON) != nil) {
println("Top business info",topBusinessJSON);
} else {
println("No business was found");
}
dispatch_group_leave(requestGroup);
}
dispatch_group_wait(requestGroup, DISPATCH_TIME_FOREVER);
}

To convert the CLLocationCoordinate2D to NSNumber, You cannot have it in a single NSNumber. You can convert to two NSNumber objects like follows:
CLLocationCoordinate2D location; // This is the location you have.
NSNumber *latitude = [NSNumber numberWithDouble:location.latitude];
NSNumber *longitude = [NSNumber numberWithDouble:location.longitude];
with Modern ObjC you can covert as:
CLLocationCoordinate2D location; // This is the location you have.
NSNumber *latitude = #(location.latitude);
NSNumber *longitude = #(location.longitude);
and call your
NSURLRequest *searchRequest = [self _searchRequestWithTerm:term location:location latitude:latitude longitude:longitude];
Don't confuse with the variable named like location which just I have named it. because your function searchRequestWithTerm:location:latitude:longitude: is having one parameter named location which accepts NSString.
This may help you.

Related

iOS MKMapItem - how get access to all members?

I am using MapKit to do a local search which returns one or more MKMapItem objects. But there are members of the object I can see in the debugger but I can't access. The one I particularly need is UID.
I have tried item.placemark, but it does not let me access the UID. This seems like it should be really simple. What am I missing here?
This does not work:
NSString *uid = item.placemark.UID
This does not work:
NSDictionary *mapItemDictionary = (NSDictionary *)item;
NSString *uid = [mapItemDictionary objectForKey:#"UID"];
But the debugger command po item shows me all the members of the object:
Name: Shell CurrentLocation: 0 Place: <GEOPlace: 0x17014e650>
{
address = {
business = (
{
**UID = 2478578482074921045**;
URL = "www.shell.com";
canBeCorrectedByBusinessOwner = 1;
name = Shell;
source = (
{
"source_id" = A3H0281540;
"source_name" = "acxiom_us";
},
{
"source_id" = 2276257;
"source_name" = localeze;
}
);
telephone = "+14803968213";
}
);
Any help with this would be appreciated. Here is the code I'm using:
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error)
{
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop)
{
MKPlacemark *placemark = (MKPlacemark *)item.placemark;
NSDictionary *addressDict = placemark.addressDictionary;
NSArray *businessArray = addressDict[#"business"];// businessArray is NIL
NSString *uid=nil;
if (businessArray != nil && businessArray.count >0) {
NSDictionary *businessDict=businessArray[0];
uid=businessDict[#"UID"];
}
NSLog(#"UID is %#",uid);
}];
Ok, so after a lot of digging it seems that the information is in a couple of private objects. The "place" property is a GEOPlace, and this has a property, business, which is an array that contains a GEOBusiness object. Since this is private data you cannot access it directly via properties, but you can get it via key-value encoding. The following code extracts the UID -
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop) {
NSValue *place = [item valueForKey:#"place"];
NSArray *businessArray = (NSArray *)[place valueForKey:#"business"];
NSNumber *uid=nil;
if (businessArray != nil && businessArray.count >0) {
id geobusiness=businessArray[0];
uid=[geobusiness valueForKey:#"uID"];
}
NSLog(#"UID is %#",[uid stringValue]);
}];
As this is private data structures there is no guarantee that it won't change. I am also unsure whether the App store validation process will flag this as private api access - Since it is using valueForKey I don't think it will, but there are no guarantees.

Search on Google Map Sdk

I need to implement the map view in my app to locate the required place. I had tried with the SVGeocoder concept.
[SVGeocoder geocode:searchfield.text
completion:^(NSArray *placemarks, NSHTTPURLResponse *urlResponse, NSError *error) {
}
But suppose I am trying to search any restaurent then the result is nil.
I was looking on Google map sdk but don't know how to do search functionality on GMSCameraPosition class.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:latitude
longitude:longitude
zoom:5];
how to search with the address using google sdk.
Thanks in advance.
If I understood it correctly, you need the location co-ordinates from a address string. Its Forward geo-coding. You can take a look at Google's free api for this: Link1
You will need a API key from your google account to access this api and there is way to select a free or business plan depending on your number of requests.
You need to use a CLLocation object for getting co-ordinates from your address. I wrote a similar function. CLLocation* temp_location=[[CLLocation alloc]init];
temp_location=[GeoCoding findAddressCordinates:sourceAddressTxtField.text];
// Class GeoCoding to find Co-ordinates
#import <Foundation/Foundation.h>
#interface GeoCoding : NSObject {
}
+(CLLocation*)findAddressCordinates:(NSString*)addressString;
#end
#import "GeoCoding.h"
#import <CoreLocation/CLAvailability.h>
#implementation GeoCoding
+(CLLocation*)findAddressCordinates:(NSString*)addressString {
CLLocation *location;
NSString *url = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?address=%#&sensor=true", addressString];
url = [url stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSURL *wurl = [NSURL URLWithString:url];
NSData *data = [NSData dataWithContentsOfURL: wurl];
// Fail to get data from server
if (nil == data) {
NSLog(#"Error: Fail to get data");
}
else{
// Parse the json data
NSError *error;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
// Check status of result
NSString *resultStatus = [json valueForKey:#"status"];
// If responce is valid
if ( (nil == error) && [resultStatus isEqualToString:#"OK"] ) {
NSDictionary *locationDict=[json objectForKey:#"results"] ;
NSArray *temp_array=[locationDict valueForKey:#"geometry"];
NSArray *temp_array2=[temp_array valueForKey:#"location"];
NSEnumerator *enumerator = [temp_array2 objectEnumerator];
id object;
while ((object = [enumerator nextObject])) {
double latitude=[[object valueForKey:#"lat"] doubleValue];
double longitude=[[object valueForKey:#"lng"] doubleValue];
location=[[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
NSLog(#"CLLocation lat is %f -------------& long %f",location.coordinate.latitude, location.coordinate.longitude);
}
}
}
return location;
}
#end
You can then use this co-ordinates in your Google Map to focus your camera position.

Autofill city and state from zip code in iOS

I have three textfields in my view.
1. Zip code, 2. City and 3. State.
How to autofill city and state field from the zip code in iOS?
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *currentString = [textField.text stringByReplacingCharactersInRange:range withString:string];
int length = [currentString length];
if(length > 5)
{
return NO;
}
if(length == 5)
{
[self getCityAndState];
}
return YES;
}
- (void) getCityAndState
{
//How to use google (or any) api to autofill city and state in objective - c?
}
I try to avoid Google's services because they tend to charge at a certain level of usage. Here's the solution using Apple's frameworks:
#import <CoreLocation/CoreLocation.h>
#import <AddressBookUI/AddressBookUI.h>
- (void)didEnterZip:(NSString*)zip
{
CLGeocoder* geoCoder = [[CLGeocoder alloc] init];
[geoCoder geocodeAddressDictionary:#{(NSString*)kABPersonAddressZIPKey : zip}
completionHandler:^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0) {
CLPlacemark* placemark = [placemarks objectAtIndex:0];
NSString* city = placemark.addressDictionary[(NSString*)kABPersonAddressCityKey];
NSString* state = placemark.addressDictionary[(NSString*)kABPersonAddressStateKey];
NSString* country = placemark.addressDictionary[(NSString*)kABPersonAddressCountryCodeKey];
} else {
// Lookup Failed
}
}];
}
Use the Google GeoCoding API to extract Information, if you want to send zip code to receive other information, use this:
NSString *strRequestParams = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?address=&components=postal_code:%#&sensor=false",zipCode];
strRequestParams = [strRequestParams stringByAddingPercentEscapesUsingEncoding:NSStringEncodingConversionExternalRepresentation];
NSURL *url = [NSURL URLWithString:strRequestParams];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
NSError *error;
NSURLResponse *response;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (!response) {
// "Connection Error", "Failed to Connect to the Internet"
}
NSString *respString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] ;
//NSLog(#"RECEIVED DATA : %#", respString);
If your zipcode variable is 32000, you will get the this JSON result:
You can parse this json to extract any information you want including Country, City, longitude, latitude etc
The answer by a-r-studios is spot on, since it doesn't introduce a dependency on Google's service.
However, I would also restrict the country code based either on the user's input or US-only if it makes sense. Not restricting it gives unpredictable results because the geocoder can return multiple hits from different countries.
#import <CoreLocation/CoreLocation.h>
#import <AddressBookUI/AddressBookUI.h>
- (void)didEnterZip:(NSString*)zip
{
CLGeocoder* geoCoder = [[CLGeocoder alloc] init];
[geoCoder geocodeAddressDictionary:#{(NSString*)kABPersonAddressZIPKey : zip,
(NSString*)kABPersonAddressCountryCodeKey : #"US"}
completionHandler:^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0) {
CLPlacemark* placemark = [placemarks objectAtIndex:0];
NSString* city = placemark.addressDictionary[(NSString*)kABPersonAddressCityKey];
NSString* state = placemark.addressDictionary[(NSString*)kABPersonAddressStateKey];
NSString* country = placemark.addressDictionary[(NSString*)kABPersonAddressCountryCodeKey];
} else {
// Lookup Failed
}
}];
}
While the answers from alex_c and a-r-studios work well, if you don't feel like fussing with AddressBookUI or dictionaries, you can simply use the geocodeAddressString:completionHandler: method on the geocoder passing in the zip code alone which is sufficient for the lookup:
[[CLGeocoder new] geocodeAddressString:zip completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count) {
CLPlacemark *placemark = placemarks.firstObject;
NSString *city = placemark.locality;
NSString *state = placemark.administrativeArea;
}
}];
In Swift:
CLGeocoder().geocodeAddressString(zip) { (placemarks, error) in
if let result = placemarks?.first {
let city = result.locality
let state = result.administrativeArea
}
}
Here is the Swift 3 version with all above corrections.
func zipToAddress(zip: String, onSuccess: #escaping (String, String, String) -> Void, onFail: #escaping () -> Void) {
let geoCoder = CLGeocoder();
let params = [
String(CNPostalAddressPostalCodeKey): zip,
String(CNPostalAddressISOCountryCodeKey): "US",
]
geoCoder.geocodeAddressDictionary(params) {
(plasemarks, error) -> Void in
if let plases = plasemarks {
if plases.count > 0 {
let firstPlace = plases[0]
print( "City \(firstPlace.locality) state \(firstPlace.administrativeArea) and country \(firstPlace.country) and iso country \(firstPlace.country)")
let city = firstPlace.locality
let state = firstPlace.administrativeArea
let country = firstPlace.country
onSuccess(city != nil ? city! : "", state != nil ? state! : "", country ?? "Not found")
return;
}
}
onFail()
}
}
Here is the Swift 4 version, with a proper optionals handling as a bonus.
Use this if you want to manually specify the country of the queried ZIP code.
private func zipToAddress(zip: String?, onSuccess: #escaping (String, String) -> Void, onFail: ((Error?) -> Void)?) {
guard let zip = zip else {
onFail?(nil)
return
}
let geoCoder = CLGeocoder()
let params: [String: Any] = [
String(CNPostalAddressPostalCodeKey): zip,
String(CNPostalAddressISOCountryCodeKey): "US"
]
geoCoder.geocodeAddressDictionary(params) { placemarks, error -> Void in
/// Read CLPlacemark documentation to see all available fields
if let place = placemarks?[0], let city = place.locality, let state = place.administrativeArea {
onSuccess(city, state)
} else {
onFail?(error)
}
}
}
And here is the solution based on the Nathan's answer.
Use this to query for the city and the administrative area, based on user locale.
private func localZipToAddress(zip: String?, onSuccess: #escaping (String, String) -> Void, onFail: ((Error?) -> Void)?) {
guard let zip = zip else {
onFail?(nil)
return
}
CLGeocoder().geocodeAddressString(zip) { placemarks, error in
if let result = placemarks?.first, let city = result.locality, let state = result.administrativeArea {
onSuccess(city, state)
} else {
onFail?(error)
}
}
}
static func zipToAddress(zip: String, onSuccess: (String, String) -> Void, onFail: () -> Void) {
var geoCoder = CLGeocoder();
var params = [
String(kABPersonAddressZIPKey): zip,
String(kABPersonAddressCountryCodeKey): "US",
]
geoCoder.geocodeAddressDictionary(params) {
(plasemarks, error) -> Void in
var plases = plasemarks as? Array<CLPlacemark>
if plases != nil && plases?.count > 0 {
var firstPlace = plases?[0]
var city = firstPlace?.addressDictionary[String(kABPersonAddressCityKey)] as? String
var state = firstPlace?.addressDictionary[String(kABPersonAddressStateKey)] as? String
var country = firstPlace?.addressDictionary[String(kABPersonAddressCountryKey)] as? String // US
onSuccess(city != nil ? city! : "", state != nil ? state! : "")
return;
}
onFail()
}
}
same with swift, I can't add this as comment(points doesn't enoght)

Get values out of data from NSJSONSerialization

I have some JSON data which is pulled from a URL. The code I have written works fine to download the JSON and parse it, but I cannot seem to access it how I need too, especially where the data is contained as a sub-element of another one.
Here is the JSON format:
{
address = "<null>";
city = "<null>";
country = UK;
"country_code" = GB;
daylight = 1;
for = daily;
items = (
{
asr = "5:22 pm";
"date_for" = "2013-7-1";
dhuhr = "1:01 pm";
fajr = "2:15 am";
isha = "11:47 pm";
maghrib = "9:24 pm";
shurooq = "4:39 am";
}
);
latitude = "50.9994081";
link = "http://muslimsalat.com/UK";
longitude = "0.5039011";
"map_image" = "http://maps.google.com/maps/api/staticmap?center=50.9994081,0.5039011&sensor=false&zoom=13&size=300x300";
"postal_code" = "<null>";
"prayer_method_name" = "Muslim World League";
"qibla_direction" = "119.26";
query = "51.000000,0.500000";
state = "<null>";
timezone = 0;
title = UK;
"today_weather" = {
pressure = 1020;
temperature = 14;
};
}
(These are Islamic prayer times.)
My Objective-C so far is this:
-(CLLocationCoordinate2D) getLocation{
CLLocationManager *locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
CLLocation *location = [locationManager location];
CLLocationCoordinate2D coordinate = [location coordinate];
return coordinate;
}
//class to convert JSON to NSData
- (IBAction)getDataFromJson:(id)sender {
//get the coords:
CLLocationCoordinate2D coordinate = [self getLocation];
NSString *latitude = [NSString stringWithFormat:#"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", coordinate.longitude];
NSLog(#"*dLatitude : %#", latitude);
NSLog(#"*dLongitude : %#",longitude);
//load in the times from the json
NSString *myURLString = [NSString stringWithFormat:#"http://muslimsalat.com/%#,%#/daily/5.json", latitude, longitude];
NSURL *url = [NSURL URLWithString:myURLString];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
if(jsonData != nil)
{
NSError *error = nil;
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
NSArray *jsonArray = (NSArray *)result; //convert to an array
if (error == nil)
NSLog(#"%#", result);
NSLog(#"%#", jsonArray);
for (id element in jsonArray) {
NSLog(#"Element: %#", [element description]);
}
}
}
When running this code, the only output I get is a list of element names (address, city, country, so on). items is given, but not its child elements. I understand that this is what I am asking the code for with:
for (id element in jsonArray) {
NSLog(#"Element: %#", [element description]);
}
but I do not know how to move onto the next step.
The only data values which I require are in fact the times themselves (so, items>asr, items>dhuhr, etc).
How can I get these values themselves and then save them as values I can work with?
Thank you!
(...); - is Array
{...}; - is Dictionary
so your "element" is Dictionary
use objectForKey:
example:
for (id element in jsonArray) {
NSLog(#"Element asr: %#", [element objectForKey:#"asr"]); // or element[#"asr"]
}
NSArray *jsonArray = (NSArray *)result; //convert to an array
This doesn't 'convert', it's just you promising the compiler that result is really an NSArray. And in this case it's a lie.
Your code is currently just printing a list of the keys in the dictionary that is returned in the JSON. Try this to get to the list of items (it's an array so you need to deal with there possibly being multiple entries):
NSDictionary *result = [NSJSONSerialization ...
for (NSDictionary *itemDict in result[#"items"]) {
NSLog(#"item: %#", itemDict);
}
Then you can extract the times.
You can extract info by following:
NSError* error = nil;
NSDictionary *userInfo; //your main data
if([NSJSONSerialization class])
userInfo = [NSJSONSerialization JSONObjectWithData:[request responseData] options:kNilOptions error:&error];
//to extract items
NSDictionary *items = [[[userInfo objectForKey:#"items"] JSONValue] objectAtIndex:0];

Get current city and country from CLGeocoder?

I've been all over the internet trying to find out how to get the city and country from CLGeocoder. I can get the longitude and latitude easily but I need the city and country information, and I keep running into deprecated methods and such, any ideas? It basically needs to get the location, then have an NSString for the country, and an NSString for the city, so I can use them to look up more info or put them on labels, etc.
You need to revise your terminology a bit - CLGeocoder (and most geocoders) won't give you a 'city' per-se - it uses terms such as 'Administrative Area', 'Subadministrative Area', etc. The CLGeocoder object will return an array of CLPlacemark objects which you can then query for the information you need. You init a CLGeocoder and call the reverseGeocodeLocation function with a location and a completion block. Here's an example:
if (osVersion() >= 5.0){
CLGeocoder *reverseGeocoder = [[CLGeocoder alloc] init];
[reverseGeocoder reverseGeocodeLocation:self.currentLocation completionHandler:^(NSArray *placemarks, NSError *error)
{
DDLogVerbose(#"reverseGeocodeLocation:completionHandler: Completion Handler called!");
if (error){
DDLogError(#"Geocode failed with error: %#", error);
return;
}
DDLogVerbose(#"Received placemarks: %#", placemarks);
CLPlacemark *myPlacemark = [placemarks objectAtIndex:0];
NSString *countryCode = myPlacemark.ISOcountryCode;
NSString *countryName = myPlacemark.country;
DDLogVerbose(#"My country code: %# and countryName: %#", countryCode, countryName);
}];
}
Now note that the CLPlacemark doesn't have a 'city' property. The full list of properties can be found here: CLPlacemark Class Reference
You can get city, country and iso country code using this (Swift 5):
private func getAddress(from coordinates: CLLocation) {
CLGeocoder().reverseGeocodeLocation(coordinates) { placemark, error in
guard error == nil,
let placemark = placemark
else
{
// TODO: Handle error
return
}
if placemark.count > 0 {
let place = placemark[0]
let city = place.locality
let country = place.country
let countryIsoCode = place.isoCountryCode
}
}
}

Resources