Create a Geocoder class with these imports and variables.

import CoreLocation
import Contacts

class Geocoder {
    static var shared = Geocoder()
    
    private var locationTable: [CNPostalAddress: CLLocation]!
    private var queue: DispatchQueue!

 

Apple throttles the number of geocoding requests you can send, so it’s a good idea to check for a cached lookup first to prevent redundant calls. If no cached version exists will submit a foreward geocoding request. Returns a CLLocation for a CNPostalAddress.

public func locationForPostalAddress(_ postalAddress: CNPostalAddress, callback: @escaping (CLLocation?) -> ()) {
    
    if locationTable == nil {
        locationTable = [CNPostalAddress: CLLocation]()
    }
    
    if queue == nil {
        queue = DispatchQueue(label: "com.bundle.geocoder")
    }
    
    queue.sync {
        if let location = locationTable[postalAddress] {
            callback(location)
            return
        }
        
        geocodeLocationForAddress(postalAddress) { (location) in
            self.locationTable[postalAddress] = location
            callback(location)
            return
        }
    }
}

 

Submits a forward-geocoding request using a CNPostalAddress.

private func geocodeLocationForAddress(_ address: CNPostalAddress, callback: @escaping (CLLocation?) -> ()) {
    let coder = CLGeocoder()

    coder.geocodeAddressString(address.addressString) { (placemarks, error) in
        guard placemarks != nil && error == nil, let placemark = placemarks!.first else {
            callback(nil)
            return
        }
        
        callback(placemark.location)
    }
}

 

Add this extension.

extension CNPostalAddress {
    
    /// Street, city, state and zip string combined with commas.
    var addressString: String {
        var addressParts = [String]()
        
        if !street.isEmpty {
            addressParts.append(street)
        }
        
        if !city.isEmpty {
            addressParts.append(city)
        }
        
        if !state.isEmpty {
            addressParts.append(state)
        }
        
        if !postalCode.isEmpty {
            addressParts.append(postalCode)
        }
        
        return addressParts.joined(separator: ", ")
    }
}

 

In your ViewControllers import Contacts and then use the Geocoder:

let address = CNMutablePostalAddress()
address.street = ""
address.city = ""
address.state = ""
address.postalCode = ""

Geocoder.shared.locationForPostalAddress(address) { (location) in
    // use the CLLocation
}

Download Sample Project on GitHub