How to get formatted address NSString from AddressDictionary?
-
10-02-2021 - |
Question
Trying to get formatted address from AddressDictionary, that I got from CLGeocoder. Used following code with no result:
subtitle = [NSString stringWithString:[[addressDict objectForKey:@"FormattedAddressLines"]objectAtIndex:0]];
Also tried:
subtitle = [[[ABAddressBook sharedAddressBook] formattedAddressFromDictionary:placemark.addressDictionary] string];
but this code seems working on Mac OS X only.
Compiler asks about ABAdressBook, but I have both header files imported.
#import <AddressBook/ABAddressBook.h>
#import <AddressBook/AddressBook.h>
Solution
The documentation for the addressDictionary
property says:
You can format the contents of this dictionary to get a full address string as opposed to building the address yourself. To format the dictionary, use the ABCreateStringWithAddressDictionary function as described in Address Book UI Functions Reference.
So add and import the AddressBookUI
framework and try:
subtitle =
ABCreateStringWithAddressDictionary(placemark.addressDictionary, NO);
OTHER TIPS
After doing some digging under iOS 6.1 I found out that the CLPlacemark address dictionary contains a pre-formatted address:
CLLocation *location = [[CLLocation alloc]initWithLatitude:37.3175 longitude:-122.041944];
[[[CLGeocoder alloc]init] reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = placemarks[0];
NSArray *lines = placemark.addressDictionary[ @"FormattedAddressLines"];
NSString *addressString = [lines componentsJoinedByString:@"\n"];
NSLog(@"Address: %@", addressString);
}];
I couldn't yet find documentation about this, but it works for all the addresses that I tested.
As highlighted by Martyn Davis, ABCreateStringWithAddressDictionary
is deprecated in iOS 9.
You can use the functions below to convert the addressDictionary
to the newer CNMutablePostalAddress
, then use the CNPostalAddressFormatter
to generate a localised string as long as you import the Contacts
framework.
Swift 3.x
// Convert to the newer CNPostalAddress
func postalAddressFromAddressDictionary(_ addressdictionary: Dictionary<NSObject,AnyObject>) -> CNMutablePostalAddress {
let address = CNMutablePostalAddress()
address.street = addressdictionary["Street" as NSObject] as? String ?? ""
address.state = addressdictionary["State" as NSObject] as? String ?? ""
address.city = addressdictionary["City" as NSObject] as? String ?? ""
address.country = addressdictionary["Country" as NSObject] as? String ?? ""
address.postalCode = addressdictionary["ZIP" as NSObject] as? String ?? ""
return address
}
// Create a localized address string from an Address Dictionary
func localizedStringForAddressDictionary(addressDictionary: Dictionary<NSObject,AnyObject>) -> String {
return CNPostalAddressFormatter.string(from: postalAddressFromAddressDictionary(addressDictionary), style: .mailingAddress)
}
Swift 2.x
import Contacts
// Convert to the newer CNPostalAddress
func postalAddressFromAddressDictionary(addressdictionary: Dictionary<NSObject,AnyObject>) -> CNMutablePostalAddress {
let address = CNMutablePostalAddress()
address.street = addressdictionary["Street"] as? String ?? ""
address.state = addressdictionary["State"] as? String ?? ""
address.city = addressdictionary["City"] as? String ?? ""
address.country = addressdictionary["Country"] as? String ?? ""
address.postalCode = addressdictionary["ZIP"] as? String ?? ""
return address
}
// Create a localized address string from an Address Dictionary
func localizedStringForAddressDictionary(addressDictionary: Dictionary<NSObject,AnyObject>) -> String {
return CNPostalAddressFormatter.stringFromPostalAddress(postalAddressFromAddressDictionary(addressDictionary), style: .MailingAddress)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// get the address
if let location = locations.last {
CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (result: [CLPlacemark]?, err: NSError?) -> Void in
if let placemark = result?.last
, addrList = placemark.addressDictionary?["FormattedAddressLines"] as? [String]
{
let address = addrList.joinWithSeparator(", ")
print(address)
}
})
}
}
Above is the swift version.
I am using Swift 3 / XCode 8
ZYiOS's answer was nice and short but did not compile for me.
The question asks how to get from an existing Address Dictionary to a string address. This is what I did:
import CoreLocation
func getAddressString(placemark: CLPlacemark) -> String? {
var originAddress : String?
if let addrList = placemark.addressDictionary?["FormattedAddressLines"] as? [String]
{
originAddress = addrList.joined(separator: ", ")
}
return originAddress
}
Swift 3 / Xcode 8 Helper Mehtod to get address from CLPlaceMark
class func formattedAddress(fromPlacemark placemark: CLPlacemark) -> String{
var address = ""
if let name = placemark.addressDictionary?["Name"] as? String {
address = constructAddressString(address, newString: name)
}
if let city = placemark.addressDictionary?["City"] as? String {
address = constructAddressString(address, newString: city)
}
if let state = placemark.addressDictionary?["State"] as? String {
address = constructAddressString(address, newString: state)
}
if let country = placemark.country{
address = constructAddressString(address, newString: country)
}
return address
}
Now this is as simple as
func updateUserAddress(coordinates: CLLocationCoordinate2D) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
geoCoder.reverseGeocodeLocation(location) {[weak self] (placemarks, error) in
if error == nil, let placemark = placemarks?.first, let address = placemark.postalAddress {
self?.userLocationLabel.text = CNPostalAddressFormatter.string(from: address, style: .mailingAddress)
}
}
}
Simply create extension for CLLocation
:
typealias AddressDictionaryHandler = ([String: Any]?) -> Void
extension CLLocation {
func addressDictionary(completion: @escaping AddressDictionaryHandler) {
CLGeocoder().reverseGeocodeLocation(self) { placemarks, _ in
completion(placemarks?.first?.addressDictionary as? [String: AnyObject])
}
}
}
Example:
let location = CLLocation()
location.addressDictionary { dictionary in
let city = dictionary?["City"] as? String
let street = dictionary?["Street"] as? String
}
Swift 5 version
CLGeocoder().reverseGeocodeLocation(newLocation!, preferredLocale: nil) { (clPlacemark: [CLPlacemark]?, error: Error?) in
guard let place = clPlacemark?.first else {
print("No placemark from Apple: \(String(describing: error))")
return
}
if let addrList = place.addressDictionary?["FormattedAddressLines"] as? [String] {
let addressString = addrList.joined(separator: ", ")
print(addressString)
}
}