4

Can anyone suggest a cleaner was to create a address string from a CLPlacemark.

At present I am using this extension

extension CLPlacemark {

    func makeAddressString() -> String {
        var address = ""
        if subThoroughfare != nil { address = address + " " + subThoroughfare! }
        if thoroughfare != nil { address = address + " " + thoroughfare! }
        if locality != nil { address = address + " " + locality! }
        if administrativeArea != nil { address = address + " " + administrativeArea! }
        if postalCode != nil { address = address + " " + postalCode! }
        if country != nil { address = address + " " + country! }
        return address
    }
}

All the instance variables are optionals hence the checking for nil, and I want in the same order of street number, to street, etc.

DogCoffee
  • 19,820
  • 10
  • 87
  • 120

4 Answers4

10

CLPlacemark has a postalAddress property of type CNPostalAddress. You can format that into a locale-aware string using the CNPostalAddressFormatter.

let formatter = CNPostalAddressFormatter()
let addressString = formatter.string(from: placemark.postalAddress)
alekop
  • 2,838
  • 2
  • 29
  • 47
5

You can now flatMap on an Array of Optionals in order to filter out nil values (I think this works since Swift 2). Your example is now basically a one-liner (if you delete the line breaks I inserted for readability):

extension CLPlacemark {

    func makeAddressString() -> String {
        return [subThoroughfare, thoroughfare, locality, administrativeArea, postalCode, country]
            .flatMap({ $0 })
            .joined(separator: " ")
    }
}

You can take this further, and use nested arrays to achieve more complex styles. Here is an example of a German style shortened address (MyStreet 1, 1030 City):

extension CLPlacemark {

    var customAddress: String {
        get {
            return [[thoroughfare, subThoroughfare], [postalCode, locality]]
                .map { (subComponents) -> String in
                    // Combine subcomponents with spaces (e.g. 1030 + City),
                    subComponents.flatMap({ $0 }).joined(separator: " ")
                }
                .filter({ return !$0.isEmpty }) // e.g. no street available
                .joined(separator: ", ") // e.g. "MyStreet 1" + ", " + "1030 City"
        }
    }
}
manmal
  • 3,900
  • 2
  • 30
  • 42
0

For some reason, neither CNPostalAddressFormatter(), nor iterating over properties like thoroughfare helped me: the addresses returned were not "full", i.e. they were missing things like "province" (in those countries that have such a concept).

So, I had to come up with a bit hakish, but working (for my usecase) workaround:

extension CLPlacemark {

    func address() -> String {
        var address: String = description.components(separatedBy: "@").first ?? description
        address.trimPrefix("\(name ?? ""), ")
        return address
    }

}

Basically, I grab description, which contains all the details I need (including things like "province"), and remove all the details I don't need (they come after @, so I use it as a separator for splitting).

I know that it might be error-prone if Apple changes the way description is generated, but so far it works flawlessly.

Dmytro Titov
  • 2,802
  • 6
  • 38
  • 60
-1

This should work too.

extension CLPlacemark {

  func makeAddressString() -> String {
    // Unwrapping the optionals using switch statement
    switch (self.subThoroughfare, self.thoroughfare, self.locality, self.administrativeArea, self.postalCode, self.country) {
    case let (.Some(subThoroughfare), .Some(thoroughfare), .Some(locality), .Some(administrativeArea), .Some(postalCode), .Some(country)):
        return "\(subThoroughfare), \(thoroughfare), \(locality), \(administrativeArea), \(postalCode), \(country)"
    default:
        return ""
    }
  }
}

Check out this post for reference: http://natashatherobot.com/swift-unwrap-multiple-optionals/

EDIT - This will only work if none of the optionals are nil, if one is nil the case will not match. Check the reference link to see how you can detect cases where one might be nil.

gabo
  • 1,133
  • 8
  • 7
  • 1
    I don't think this will work, even if one or two are nil, i need the remaining parts still to be combined to form a string. – DogCoffee Oct 29 '15 at 20:31