16

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>
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
Shmidt
  • 16,436
  • 18
  • 88
  • 136

10 Answers10

40

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);
  • 2
    This is the right method to use... However it does not always provide a complete address. For instance looking up "wall street" with CLGeocoder will return a placemark and calling ABCreateStringWithAddressDictionary on the placemark dictionary will return "New York, NY, United Stated"... And "Wall Street" is nowhere to be found. – MiKL Oct 03 '13 at 12:20
  • This method also adds weird ascii characters, last checked in iOS8 – jdog Oct 01 '14 at 18:32
  • 14
    fyi - ABCreateStringWithAddressDictionary has been deprecated in iOS 9.0 – Martyn Davis Jun 11 '15 at 08:07
  • 3
    ```addressDictionary``` is deprecated in iOS 11. – Supertecnoboff Nov 29 '17 at 13:36
24

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.

Klaas
  • 22,394
  • 11
  • 96
  • 107
  • It's the same as in question. – Shmidt Aug 12 '13 at 07:36
  • Yes, but you said it was NOT working. My tests on all my devices and in the simulator show that it is actually working. At least with the most recent iOS versions. – Klaas Aug 12 '13 at 11:24
  • 1
    this is a good answer but in order for me to get the FULL address I had to use this instead `NSString *addressString = [lines componentsJoinedByString:@", "];` – sudo Mar 13 '14 at 09:03
  • @DigitalChild you can join the lines by any string. I guess that you didn't see the full address because I used the new line character. – Klaas Mar 13 '14 at 15:00
  • When I search for "Apple" in Palo Alto, I get back two Apple Store results. Neither have the `"FormattedAddressLines"` key in its `addressDictionary`. – ma11hew28 Aug 30 '14 at 19:22
  • @MattDiPasquale how to you search for "Apple" in Palo Alto? Are you using the `CLGeocoder` class? – Klaas Aug 30 '14 at 23:00
  • @Klass no, I'm using `MKLocalSearch`. – ma11hew28 Aug 31 '14 at 17:50
  • @MattDiPasquale ok, might be that `"FormattedAddressLines"` is only there after issuing a reverse geocoding call. – Klaas Aug 31 '14 at 20:41
20

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)
}
tebs1200
  • 1,205
  • 12
  • 19
  • 1
    NB: This API is available from iOS9 upwards. – Matthieu Riegler Jan 06 '16 at 18:13
  • Thanks for this. FYI, the docs for `CNPostalAddressFormatter.stringFromPostalAddress` make no mention of the second `style` parameter but the build fails unless you have it. Take a look at the docs: https://developer.apple.com/library/ios/documentation/Contacts/Reference/CNPostalAddressFormatter_Class/#//apple_ref/occ/instm/CNPostalAddressFormatter/stringFromPostalAddress: – Joshua Pinter May 11 '16 at 14:51
  • Doesn't the use of "ZIP" make this specific to American addresses only? – shim Nov 27 '16 at 04:45
  • The `addressDictionary` including the `ZIP` field is simply the format that the geocoder returns the result in. ZIP is the name of the field, but the geocoder puts the equivalent value (like postal code) into this field where applicable as far as I'm aware. I don't live in America and it works for me. – tebs1200 Dec 02 '16 at 14:28
  • 1
    `CNPostalAddressFormatter` doesn't seem to include delimiters in the string. For instance, there is no comma between city and state for an American address. Is there a way to enable this? – Crashalot Dec 09 '18 at 03:52
13
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.

ZYiOS
  • 5,204
  • 3
  • 39
  • 45
4

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
}
Community
  • 1
  • 1
Nick Graham
  • 575
  • 5
  • 12
2

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
  }
Shmidt
  • 16,436
  • 18
  • 88
  • 136
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
1

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)
        }
    }
}
Yuri Grigoriev
  • 114
  • 2
  • 3
1

iOS 11+

import CoreLocation
import Contacts

public extension CLPlacemark {
    func formattedAddress() -> String? {
        guard let postalAddress = postalAddress else { return nil }
        let formatter = CNPostalAddressFormatter()
        formatter.style = .mailingAddress
        let formatterString = formatter.string(from: postalAddress)
        return formatterString.replacingOccurrences(of: "\n", with: " ")
    }
}
hstdt
  • 5,652
  • 2
  • 34
  • 34
0

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
}
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
0

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)

            }
        }
mini coder
  • 57
  • 1
  • 9