Search code examples
iosregexswiftswift3nsdatadetector

Extracting address elements from a String using NSDataDetector in Swift 3.0


I'm attempting to use NSDataDetector to addresses from a string. I've taken a look at NSHipster's article on NSDataDetector as well as Apple's NSDataDetector documentation. I've got the following method to the point where it'll pull addresses out of a string:

func getAddress(from dataString: String) -> [String] {
    let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.address.rawValue)
    let matches = detector.matches(in: dataString, options: [], range: NSRange(location: 0, length: dataString.utf16.count))

    var addressArray = [String]()

    // put matches into array of Strings
    for match in matches {
        let address = (dataString as NSString).substring(with: match.range)
        addressArray.append(address)
    }

    return addressArray
}

I'd like to pull out elements of addresses, not the entire address. In NSHipster's NSDataDetector post in the Data Detector Match Types section, I see address components such as NSTextCheckingCityKey, NSTextCheckingStateKey, and NSTextCheckingZIPKey. I'm unable to use those keys in the NSDataDetector's initialization.

I dug around on GitHub to see if I could find an example to crib from, but the only stuff I'm able to find is Objective-C code or declarative stuff in the master Swift repo.

I'm 99% sure I can pull out the individual components of an address, but I'm too dumb to figure it out. Thank you for reading. I welcome suggestions.


Solution

  • I haven't used this class before, but it looks like it returns objects of type NSTextCheckingResult. If you get a result of type NSTextCheckingTypeAddress then you can ask the result for it's addressComponents, which will be a dictionary containing the different parts of the address.

    EDIT:

    Here is some working playground code I just banged out:

    import UIKit
    
    var string = "Now is the time for all good programmers to babble incoherently.\n" +
    "Now is the time for all good programmers to babble incoherently.\n" +
    "Now is the time for all good programmers to babble incoherently.\n" +
    "123 Elm Street\n" +
    "Daton, OH 45404\n" +
    "Now is the time for all good programmers to babble incoherently.\n" +
    "2152 E Street NE\n" +
    "Washington, DC 20001"
    
    let results = getAddress(from: string)
    
    print("matched \(results.count) addresses")
    for result in results {
      let city = result[NSTextCheckingCityKey] ?? ""
      print("address dict = \(result).")
      print("    City = \"\(city)\"")
    }
    
    func getAddress(from dataString: String) -> [[String: String]] {
      let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.address.rawValue)
      let matches = detector.matches(in: dataString, options: [], range: NSRange(location: 0, length: dataString.utf16.count))
    
      var resultsArray =  [[String: String]]()
      // put matches into array of Strings
      for match in matches {
        if match.resultType == .address,
          let components = match.addressComponents {
          resultsArray.append(components)
        } else {
          print("no components found")
        }
      }
      return resultsArray
    }
    

    This code prints:

    matched 2 addresses

    address dict = ["Street": "123 Elm Street", "ZIP": "45404", "City": "Daton", "State": "OH"]. City = "Daton"

    address dict = ["Street": "2152 E Street NE", "ZIP": "20001", "City": "Washington", "State": "DC"]. City = "Washington"