Issue With __block And CLGecoder in ios? - objective-c

i write code of CLGecoder and the code are
-(NSString *)GetCurrentAddress:(CLLocation *)Location
{
__block NSString *locatedaddress;
CLGeocoder *Gecoder=[[CLGeocoder alloc]init];
[Gecoder reverseGeocodeLocation: Location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"Placemark array: %#",placemark.addressDictionary );
//String to address
locatedaddress = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//Print the location in the console
NSLog(#"Currently address is: %#",locatedaddress);
}];
return locatedaddress;
}
Problem is locatedaddress inside The completionHandler Has Value But Outside Is Not ?
Any One Can Help Me ?
Thanks In Advance.

The geocoder is asynchronous -- which by definition means you cannot get the result immediately. You must use the completion handler to do whatever you want to do with the result.

Related

How to get the time zone name for a CLLocation

I have an application that uses a CLGeocoder to forwardGeocode a placemark from an address string. The CLPlacemark response contains a CLLocation which gives me GPS coordinates.
The only way to create an NSTimeZone seems to be by using the correct Time Zone Name. It is important to point out that I am not using the current location of the device, so [NSTimeZone localTimeZone] will not work for me.
Is there a way to get the timezone name for the CLLocation so that I can create an NSTimeZone correctly?
NOTE: I have been using timeZoneForSecondsFromGMT but that never contains correct DST data, so it is not helpful for me.
You should use https://github.com/Alterplay/APTimeZones to get NSTimeZone from CLLocation. It also works with CLGeocoder.
since iOS9 it should be possible direclty using CLGeocoder as specified here: https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html
Search results for MapKit and CLGeocoder can provide a time zone for the result.
I found an interesting approach using CLGeocoder, which I put into a category on CLLocation. The interesting part looks like this:
-(void)timeZoneWithBlock:(void (^)(NSTimeZone *timezone))block {
[[[CLGeocoder alloc] init] reverseGeocodeLocation:self completionHandler:^(NSArray *placemarks, NSError *error) {
NSTimeZone *timezone = nil;
if (error == nil && [placemarks count] > 0) {
CLPlacemark *placeMark = [placemarks firstObject];
NSString *desc = [placeMark description];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"identifier = \"([a-z]*\\/[a-z]*_*[a-z]*)\"" options:NSRegularExpressionCaseInsensitive error:nil];
NSTextCheckingResult *result = [regex firstMatchInString:desc options:0 range:NSMakeRange(0, [desc length])];
NSString *timezoneString = [desc substringWithRange:[result rangeAtIndex:1]];
timezone = [NSTimeZone timeZoneWithName:timezoneString];
}
block(timezone);
}];
}
Usage is like this:
CLLocation *myLocation = ...
[myLocation timeZoneWithBlock:^(NSTimeZone *timezone) {
if (timezone != nil) {
// do something with timezone
} else {
// error determining timezone
}
}];
Despite requiring a network connection and working asynchronously, I have found this to be the most reliable way of getting the time zone for a location.
EDITED YEARS LATER
This answer hasn't aged well since the timeZone property was added to CLPlacemark in iOS9 (thank you Ortwin Genz). Here's an updated category method:
-(void)timeZoneWithBlock:(void (^)(NSTimeZone *timezone))block {
[[[CLGeocoder alloc] init] reverseGeocodeLocation:self completionHandler:^(NSArray *placemarks, NSError *error) {
NSTimeZone *timeZone = nil;
if (error == nil && [placemarks count] > 0) {
CLPlacemark *placeMark = [placemarks firstObject];
timeZone = placeMark.timeZone;
}
block(timeZone);
}];
}

reverse Geolocation : Postal Code Issues

This code I am using to get the entire Address of the user using Reverse Geolocation. Can someone suggest me the way to just get the Postal Code or Zip code out of it .
//Geocoding Block
[self.geoCoder reverseGeocodeLocation: locationManager.location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
//Set the label text to current location
[locationLabel setText:locatedAt];
}];
Use google maps to convert the geo location to a zip adress.
https://developers.google.com/maps/documentation/geocoding/

Reverse Geocode using CLGeocoder

I'd like to ask for a second opinion for my solution in reverse Geocoding in getting a user's current location:
- (void)reverseGeocodeLocation:(CLLocation *)location
{
CLGeocoder* reverseGeocoder = [[CLGeocoder alloc] init];
if (reverseGeocoder) {
[reverseGeocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark* placemark = [placemarks firstObject];
if (placemark && [placemark count] > 0) {
//Using blocks, get zip code
NSString *zipCode = [placemark.addressDictionary objectForKey:(NSString*)kABPersonAddressZIPKey];
}
}];
}
else{
MKReverseGeocoder* revGeo = [[MKReverseGeocoder alloc] initWithCoordinate:location.coordinate];
revGeo.delegate = self;//using delegate
[revGeo start];
[revGeo release];
}
[reverseGeocoder release];
}
however, there seemed to be a bit of a problem...I encountered an EXC_BAD_ACCESS error pointing at:
[reverseGeocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark* placemark = ...
}];
Could you please tell me what went wrong? I received an EXC_BAD_ACCESS error.
You're not checking the error sent to the block. You're presuming that you get at least one placemark returned, but the array may be empty for some reason. That certainly could be the source of your EXC_BAD_ACCESS error.

Issue forward-geocoding multiple addresses

I am connecting to a remote web service which basically returns an XML back. I am then parsing that XML into a Property object (think real state sort of thing)
But now, the web service returns a postal code for each property alone. It does not provide a coordinate which is what I need to place an annotation in the map. I am able to geocode an address provided a postal code. However, my problem is it is not allowing me to do multiple requests
Here's my code
- (void)processProperties:(Property *)property {
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
}];
}
- (void)addAnnotations:(NSArray *)objects {
CLLocationDegrees lat;
CLLocationDegrees longitude;
CLLocationCoordinate2D mCoords;
NSString *fullAddress;
// Add the annotations found nearby
for (Property *property in objects) {
[self processProperties:property];
lat = property.latitude;
longitude = property.longitude;
fullAddress = [NSString stringWithFormat:#"%# %# %#", property.houseNumber, #" ", property.streetName];
[self createAnnotationWithCoords:mCoords :fullAddress :[NSString stringWithFormat:#"$%.2f", property.rent]];
}
zoomLevel = 0.1;
mCoords = CLLocationCoordinate2DMake(lat,longitude);
MKCoordinateRegion region = MKCoordinateRegionMake(mCoords,MKCoordinateSpanMake(zoomLevel,zoomLevel));
[self.mapView setRegion:region animated:YES];
}
For some reason it's just geocoding 1 property. Is not going through the loop accordingly.
Any ideas folks?
Use this on your Forward Geo Function. geocoder needs to be release and initialized again to start a new address, hope this helps.
- (void)processProperties:(Property *)property {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:property.postalCode
completionHandler:^(NSArray* placemarks, NSError* error){
placemark = [placemarks lastObject];
for (CLPlacemark* aPlacemark in placemarks)
{
[sublet setLatitude:aPlacemark.location.coordinate.latitude];
[sublet setLongitude:aPlacemark.location.coordinate.longitude];
}
[geocoder release];
}];
}

IP Address? - Cocoa

How would I make a GUI program that displays your Ip address with a click of a button? Please, no difficult explanations, I just started Cocoa not long ago.
Thanks,
Kevin
You can get IP address through two ways:
1- if you want to get the local ip address on the current used netwrok, you can use the following method to retrive it:
-(NSString *)getIPAddress
{
NSString *address = #"error";
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 0;
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(&interfaces);
if (success == 0)
{
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL)
{
if(temp_addr->ifa_addr->sa_family == AF_INET)
{
// Get NSString from C String
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
return address;
}
2- if you want to get the external IP address then you need to use the following method:
-(NSString*)getIP
{
NSUInteger an_Integer;
NSArray * ipItemsArray;
NSString *externalIP;
NSURL *iPURL = [NSURL URLWithString:#"http://www.dyndns.org/cgi-bin/check_ip.cgi"];
if (iPURL) {
NSError *error = nil;
NSString *theIpHtml = [NSString stringWithContentsOfURL:iPURL encoding:NSUTF8StringEncoding error:&error];
if (!error) {
NSScanner *theScanner;
NSString *text = nil;
theScanner = [NSScanner scannerWithString:theIpHtml];
while ([theScanner isAtEnd] == NO) {
// find start of tag
[theScanner scanUpToString:#"<" intoString:NULL] ;
// find end of tag
[theScanner scanUpToString:#">" intoString:&text] ;
// replace the found tag with a space
//(you can filter multi-spaces out later if you wish)
theIpHtml = [theIpHtml stringByReplacingOccurrencesOfString:
[ NSString stringWithFormat:#"%#>", text]
withString:#" "] ;
ipItemsArray =[theIpHtml componentsSeparatedByString:#" "];
an_Integer=[ipItemsArray indexOfObject:#"Address:"];
externalIP =[ipItemsArray objectAtIndex: ++an_Integer];
}
NSLog(#"%#",externalIP);
} else {
NSLog(#"Oops... g %d, %#", [error code], [error localizedDescription]);
}
}
return externalIP;
}
For determining the IP address, I found
this.
As for making it into a Cocoa app, add an NSTextField (label) to your main window in Interface Builder, put in a button, add in an application controller (a subclass of NSObject that you make), put in the outlet and the action, do the proper connenctions, and in the "get IP" method, put in that code and set the value for the label's stringValue.
You can use [[NSHost currentHost] address], but it won't always display what you like. On my system, for example, it gives my IPv6 address.
EDIT: On my system, [[[NSHost currentHost] addresses] objectAtIndex:0] has my IPv4 address.
[[NSHost currentHost] addresses] will get you an array of IPs. Read the documentation for NSHost.
As for displaying that in a GUI, I recommend getting Aaron Hillegass' book Cocoa Programming for Mac OS X, or any Cocoa beginners book should teach that.
I just wrote this, may need some work but seems to work well on my machine...
- (NSString *)getLocalIPAddress
{
NSArray *ipAddresses = [[NSHost currentHost] addresses];
NSArray *sortedIPAddresses = [ipAddresses sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.allowsFloats = NO;
for (NSString *potentialIPAddress in sortedIPAddresses)
{
if ([potentialIPAddress isEqualToString:#"127.0.0.1"]) {
continue;
}
NSArray *ipParts = [potentialIPAddress componentsSeparatedByString:#"."];
BOOL isMatch = YES;
for (NSString *ipPart in ipParts) {
if (![numberFormatter numberFromString:ipPart]) {
isMatch = NO;
break;
}
}
if (isMatch) {
return potentialIPAddress;
}
}
// No IP found
return #"?.?.?.?";
}
We can use hostWithName: method with current host name. This will return only single local IPv4 and IPv6 IP, which we can filter easily.
We can get the current system host name using [[NSHost currentHost] name].
+(NSString *)getLocalIPAddress{
NSArray *ipAddresses = [[NSHost hostWithName:[[NSHost currentHost] name]] addresses];
for (NSString *ipAddress in ipAddresses) {
if ([ipAddress componentsSeparatedByString:#"."].count == 4) {
return ipAddress;
}
}
return #"Not Connected.";
}
So, this will solve all the problems mentions in comments of other answers. Also, this significantly work more faster than other solution mention here.