Make address lines blank if they don't exist (Objective-C) - ios

My app finds the user's location, and shows the address in a label. The problem is that if something doesn't exist at a certain place, a postal code for example, then the line says (null). How do I make that line blank? I suppose it has to be set to nil somehow, somewhere...
Please help!
Here's my code:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"Location: %#", newLocation);
CLLocation *currentLocation = newLocation;
[geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (error == nil && [placemarks count] > 0) {
placeMark = [placemarks lastObject];
NSString *locationString = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placeMark.subThoroughfare,
placeMark.thoroughfare,
placeMark.postalCode,
placeMark.locality,
placeMark.administrativeArea,
placeMark.country];
locationLabel.text = locationString;
}
else {
NSLog(#"%#", error.debugDescription);
}
}];
}

This code checks all fields for nil (which causes the <null> output) and replaces nil values with an empty string.
NSString *locationString = [NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placeMark.subThoroughfare ?: #"",
placeMark.thoroughfare ?: #"",
placeMark.postalCode ?: #"",
placeMark.locality ?: #"",
placeMark.administrativeArea ?: #"",
placeMark.country ?: #""];

Quick and (very) dirty solution, but ones may find it readable:
substitute placeMark.postalCode,
with
placeMark.postalCode ? placeMark.postalCode : #"",
for each element you want nothing (or whatever other string) to appear in case of nil.
Or just write custom code to check for each element in local variables.
--
Edit:
In the remote case that you actually have a string containing "(null)" the you may want to consider checking for this value substituting again each line with:
[placeMark.postalCode isEqualToString:#"(null)"]? #"" : placeMark.postalCode,
consider anyway that you should really take care of this earlier in your logic, probably some parsing went wrong when creating the placeMark object string members.

You really need to write code to check for missing fields and handle them appropriately.
Consider that if some of the items are missing there may be blank lines in the resulting string.

Related

Objective-C get variable out of block

I'm struggling with getting a variable out of a block. Seems to be a pretty basic thing but I can't figure it out! How can I access it? e.g. usercity out of the block? Usercity is declared as NSString in .h.
[ceo reverseGeocodeLocation: loc completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//NSLog(#"placemark %#",placemark);
//String to hold address
//NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//NSLog(#"addressDictionary %#", placemark.addressDictionary);
//NSLog(#"placemark %#",placemark.region);
NSLog(#"Land %#",placemark.country); // Give Country Name
NSLog(#"City %#",placemark.locality); // Extract the city name
NSLog(#"Adresse %#",placemark.name);
//NSLog(#"location %#",placemark.ocean);
NSLog(#"Zip %#",placemark.postalCode); //ZipCode
NSLog(#"sonstiges %#",placemark.subLocality);
//Save values in variables
usercountry = placemark.country;
usercity = placemark.locality;
userzip = placemark.postalCode;
NSLog(#"usercity: %#",usercity);
//NSLog(#"location %#",placemark.location);
}
];
Is this what you're looking for?
__block NSString *userCity;
[ceo reverseGeocodeLocation: loc completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
...
userCity = placemark.locality;
}];
But if you want to actually be able to check its value outside of the block, you'll have to do so after the completion handler updates the value. Perhaps make it a property, ie. self.userCity?
Your code in the block has to store usercity where you want it. You can't "get something out" of a block, the code in the block has to do it.
You do know that a block can access all variables in the surrounding method, don't you?

Remove "(null)" from string if no value available

In have an app where I show the user's current position in address. The problem is that if example the Postal Code or Administrative Area isn't available, the string prints (null) where that value should be staying - all the other data is there.
Example:
(null) Road No 19
(null) Mumbai
Maharashtra
What I was wondering was if it was possible to just have a blank space instead of (null)?
My current code:
_addressLabel.text = [NSString stringWithFormat: #"%# %#\n%# %#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea];
This is very easily accomplished with the NSString method
- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement
For example, after you have populated your _addressLabel.text string with all of the (possibly nil) values, just replace the occurrences of the undesired string with the desired string. For example, the following will solve your problem.
_addressLabel.text = [NSString stringWithFormat: #"%# %#\n%# %#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea];
// that string may contain nil values, so remove them.
NSString *undesired = #"(null)";
NSString *desired = #"\n";
_addressLabel.text = [_addressLabel.text stringByReplacingOccurrencesOfString:undesired
withString:desired];
Use an NSMutableString, and have some if statements which append a string to it if the value isn't [NSNull null] or nil.
CLPlacemark *placemark = ...;
NSMutableString *address = [NSMutableString string];
if (placemark.subThoroughfare) {
[address appendString:placemark.subThoroughfare];
}
if (...) {
[address appendFormat:#"%#\n", ...];
}
// etc...
_addressLabel.text = address;
Use this to replace null with empty string
addressLabel.text = [NSString stringWithFormat: #"%# %#\n%# %#\n%#",
placemark.subThoroughfare?:#"", placemark.thoroughfare?:#"",
placemark.postalCode?:#"", placemark.locality?:#"",
placemark.administrativeArea?:#""];
I believe a cleaner(*) solution is to use the "?" operator here, like this, for each of the string values:
Instead of placemark.subThoroughfare write:
(placemark.subThoroughfare ? placemark.subThoroughfare : #"")
or even shorter:
(placemark.subThoroughfare ?: #"")
This will check if the value is NULL (or nil) - if non-zero, it'll use the string's value, otherwise it'll use a string containing a space.
It's cleaner because my solution does not depend on how NULL strings are printed, i.e. if they get printed in a future OS version as (nil) instead of (null), then my solution will still work, while Brian Tracy's won't. Not that I expect that to be a problem, ever, just pointing out what's a more proper solution to the issue out hand, for those who care what's going on behind the scenes.

CLLocationManager reverseGeocodeLocation Language

I'm using CLLocationManager on iPhone to get the current user location, then I use reverseGeocodeLocation to convert the location to an address.
This works fine but the results are returned in the device language and I need them to always be in english.
Is there a way to get the Address string in a desired language or convert it ?
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
}
I managed to figure it out eventually so this is for those who might need it as well.
Get the "AppleLanguages" key from SUserDefaults standardUserDefaults and save it for later.
Set the "AppleLanguages key to only "en"
Grab the location (Now it's only in english)
Restore the "AppleLanguages" key from before.
//1+2
NSArray *currentLanguageArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:#"en", nil] forKey:#"AppleLanguages"];
//get the location
//4
[[NSUserDefaults standardUserDefaults] setObject:currentLanguageArray forKey:#"AppleLanguages"];
Hope that helps
CLGeocoder provides a handy method for this exact case:
- (void)reverseGeocodeLocation:(CLLocation *)location
preferredLocale:(NSLocale *)locale
completionHandler:(CLGeocodeCompletionHandler)completionHandler;
Available in iOS 11 and above.

ios: Compare NSString to "<null>" not working

I am consuming a web service that returns JSON. One of the values I get is "< null >".
When I run the follwoing code the if statment still get executed when it is not suppposed to.
Any reason why?
NSDictionary *location = [dictionary valueForKey:#"geoLocation"]; //get the product name
NSString *latitude = [location valueForKey:#"latitude"];
NSLog(#"%#", latitude);
NSString *longitude = [location valueForKey:#"longitude"];
if (![latitude isEqual: #"<null>"] && ![longitude isEqual: #"<null>"]) {
NSLog(#"%d", i);
CLLocationCoordinate2D coordinate;
coordinate.longitude = [latitude doubleValue];
coordinate.longitude = [longitude doubleValue];
[self buildMarketsList:coordinate title:title subtitle:nil]; //build the browse list product
}
I am consuming a web service that returns JSON. One of the values I get is "< null >"
Aha. Two possibilities:
I. The JSON doesn't contain latitude and longitude information. In this case, the keys for them aren't present in the dictionary you're getting back, so you are in fact obtaining a nil (or NULL) pointer. As messaging nil returns zero, both conditions will fire (due to the negation applied). Try this instead:
if (latitude != nil && longitude != nil)
and never rely on the description of an object.
II. Probably the JSON contains null values, and the JSON parser you're using turns null into [NSNull null], and in turn you're trying to compare a string against that NSNull. In this case, try this:
if (![latitude isEqual:[NSNull null]] && ![longitude isEqual:[NSNull null]])
I had same Problem, But solved as below,
Replace your if with following,
if (![latitude isKindOfClass:[NSNull class]] && ![longitude isKindOfClass:[NSNull class]])
if ([latitude isEqual:[NSNull null]])
{
//do something
latitude = #"";
}
else
{
//do something else
latitude = json value
}
This is what I will do. This is because I need to store the value even if a null return.
Typically if you get null in a JSON response, you'll need to check against NSNull.
In your case, you should do something like this:
if ( [location valueForKey:#"longitude"] == [NSNull null]) {
// is null object
}

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