Introduction to Maps with Swift
Tags: swift, mobile, beginners
Published: 2023-11-20 15:54:30

In this blog, I will be using two frameworks for locations, **CoreLocation** and **MapKit**.
- CoreLocation: used to manage operations related to coordinates and address
- MapKit: used to manage any operations related to _drawing a map_ on the screen

**Forward Geocoding**
- translate a human-readable address -> coordinates (latitude + longitude)
- submit the address to Apple's geocoding server as a _background task_

```swift
import UIKit

// MARK: 1. import the CoreLocation Library
// - has methods and properties that allow you to manage addresses and coordinates
import CoreLocation

class ForwardGeoVC: UIViewController {
  
  // MARK: Outlets
  @IBOutlet weak var streetTF: UITextField!
  @IBOutlet weak var cityTF: UITextField!
  @IBOutlet weak var countryTF: UITextField!
  @IBOutlet weak var coordinatesLabel: UILabel!
  
  // MARK: 2. Geocoder object
  let geocoder = CLGeocoder()
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }
  
  @IBAction func fetchCoordinates(_ sender: Any) {
    guard let street = streetTF.text,
          let city = cityTF.text,
          let country = countryTF.text
    else {return}
    
    print("\(country), \(city), \(street)")
    
    getLocation(address: "\(country), \(city), \(street)")
  }
  
  // helper function
  func getLocation(address: String) {
    // MARK: 3. Forward geocoding request
    // - Taking a human readable addresss and converting it to a coordinate
    
    geocoder.geocodeAddressString(address) { results, error in
      print("Sent request to geocoding service, waiting for response")
      
      // unsuccessful
      if (error != nil) {
        print("Error occured during geocoding request:")
        print(error ?? "")
        self.coordinatesLabel.text = "An error occured, try again later."
        return
      }
      
      // success
      if(results != nil) {
        if(results?.count == 0) {
          // not able to find a coordinate
          print("No coordinates found for this address")
          self.coordinatesLabel.text = "No coordinates found for this address"
        } else {
          // able to find a coordinate
          print("Coordinate found")
          let placemark: CLPlacemark = (results?.first)!
          print(placemark)
          
          // extract the relevant data (lat, long)
          if let lat = placemark.location?.coordinate.latitude,
             let long = placemark.location?.coordinate.longitude {
            print("Coordinate: \(lat), \(long)")
            self.coordinatesLabel.text = "\(lat), \(long)"
          } else {
            print("Could not extract coordinates")
            self.coordinatesLabel.text = "Could not extract coordinates"
          }
        }
      }
    }
  }
  
}
```

**Screen**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e49pa0q78918yeuia610.png)

**Reverse Geocoding**
- translate a set of coordinates (latitude + longitude) -> human readable address

```swift
import UIKit

// MARK: 1. import CoreLocation
import CoreLocation

class ReverseGeoVC: UIViewController {
  
  // MARK: Outlets
  @IBOutlet weak var latitudeTF: UITextField!
  @IBOutlet weak var longitudeTF: UITextField!
  @IBOutlet weak var addressLabel: UILabel!
  
  // MARK: 2. Geocoder object
  let geocoder = CLGeocoder()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
  }
  
  @IBAction func fetchAddress(_ sender: Any) {
    guard let lat = latitudeTF.text,
          let long = longitudeTF.text
    else {return}
    
    print("Got coordinates: (\(lat), \(long))")
    
    getAddress(latitude:lat, longitude: long)
  }
  
  // helper function
  func getAddress(latitude:String, longitude:String) {
    // MARK: 3. Reverse geocoding request
    // - Taking a coordinate and converting it to a human readable addresss
    guard let latDouble = Double(latitude),
          let longDouble = Double(longitude)
    else {return}
    
    let location = CLLocation(latitude: latDouble, longitude: longDouble)
    
    geocoder.reverseGeocodeLocation(location) { results, error in
      print("Sent request to geocoding service, waiting for response")
      
      if let err = error {
        print("Error occured during geocoding request: \(err)")
        self.addressLabel.text = "An error occured, try again later."
      }
      
      if let res = results {
        if (res.count == 0) {
          // not able to find a coordinate
          print("No addresses found for this coordinate")
        } else {
          // able to find a coordinate
          print("Address found")
          let placemark:CLPlacemark = res.first!
          print("Placemark: \(placemark)")
          
          //compose an actual address
          let name = placemark.name ?? ""
          let street = "\(placemark.subThoroughfare ?? "") \(placemark.thoroughfare ?? "")"
          let city = placemark.locality ?? ""
          let province = placemark.administrativeArea ?? ""
          let country = placemark.country ?? ""
          
          let address = "\(name) \(street), \(city), \(province), \(country)"
          print("Final address: \(address)")
          self.addressLabel.text = address
        }
      }
    }
  }
  
}
```

**Screen**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qao2q604dtqxuuxrlxli.png)

**Main Map View Controller**

```swift
import UIKit

// MARK: 1. import MapKit
import MapKit

class MapVC: UIViewController {
  
  // MARK: 2. Create an outlet for your map
  @IBOutlet weak var mapView: MKMapView!
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    // MARK: 3. Code to configure the map
    // See:  https://guides.codepath.com/ios/Maps#set-up-initial-properties
    
    // span  = describes the zoom level of your map
    // smaller numbers = more zoomed in
    // larger numbers = more zoomed out
    // 0.01 = street level zoom
    let zoomLevel = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
    
    // region = visible area of the map --> center of your map
    let centerOfMap = CLLocationCoordinate2D(latitude: 43.7957894, longitude: -79.3489909)
    let visibleRegion = MKCoordinateRegion(center: centerOfMap, span: zoomLevel)
    
    // setup the map to show this region
    mapView.setRegion(visibleRegion, animated: true)
    
    // MARK: 4. Add a pin on the map
    // See: https://guides.codepath.com/ios/Maps#mkpointannotation
    let pin = MKPointAnnotation()
    pin.coordinate = centerOfMap
    pin.title = "Seneca College - Newnham Campus"
    mapView.addAnnotation(pin)
  }
  
}
```

**Screen**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0m4ugmg30t0osz9wkooj.png)