---
title: "Extracting the location from a photo"
slug: extracting-the-location-from-a-photo
section: tech
date: 2018-08-06T18:51:00.000Z
canonical: https://roland.leth.ro/blog/tech/extracting-the-location-from-a-photo
---

I’d like to quickly explain how to let the user pick a photo and automatically extract the location for them. The post is targeted at iOS 11+, because starting with this version, to use an `UIImagePickerController` we don’t need to ask the user’s permission to access their photo library, because the controller runs as a separate process from the app, which means the app gets read-only access _only_ to the image the user selected, and just to the image — no metadata included.

For this, we will need the `UIImagePickerController`:

```swift
func pickPhotoFromLibrary() {
   // 1
   guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { return }

   let imagePicker = UIImagePickerController()
   imagePicker.delegate = self // 2
   imagePicker.sourceType = .photoLibrary // 3
   imagePicker.allowsEditing = false // 4

   present(imagePicker, animated: true, completion: nil)
}
```

First, we need to make sure the type of source we want is available (1), in our case `.photoLibrary` (2). We’ll then set a delegate where the picker will give us the picked image, set the `sourceType` (3) and set if the user is allowed to edit the image, or not (4).

Next, the bread and butter:

```swift
// 1
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
   defer { dismiss(animated: true, completion: nil) } // 2
   guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else { return } // 3

   if let asset = info[UIImagePickerControllerPHAsset] as? PHAsset, // 4
      let location = asset.location { // 5
      CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in // 6
         guard let placemark = placemarks?.first else { return } // 7

         /*
         Do something with
         placemark.name
         placemark.locality
         placemark.country ...
         */
      }
   }

   // Do something with the image.
}
```

This is the method the picker will call after the user has selected his image (1). First thing we do is defer the dismissal of the picker, since the system doesn’t do that by itself (2). Next, we try to extract the image from the info dictionary we have received (3).

Now, to extract the location, an `UIImage` is not enough, we need a `PHAsset`. To receive a `PHAsset` in the info dictionary, we _do_ need access to the user’s library, which they may decline, so that’s why we’re using an `if let` here, instead of trying to extract the asset (4) and its location (5) in the `guard` above (at 3).

After we have the `CLLocation` (5), we need a `CLGeocoder` that can reverse-geocode the location via an async request (6), which will return an array of `CLPlacemark`s. Most of the time, this array will contain only one element, unless the address couldn’t be resolved to a single location, which is why we mostly care about the first element in the array (7).

There’s a final catch: as we saw above, to actually receive a `PHAsset`, we need access to the user’s library, so we need to ask for it first. In order to be allowed to ask this, we need to add a message in our `Info.plist` under the `NSPhotoLibraryUsageDescription` key.

Let’s update our `pickPhotoFromLibrary` method to do that:

```swift
func pickPhotoFromLibrary() {
   if PHPhotoLibrary.authorizationStatus() == .notDetermined { // 1
      DispatchQueue.main.async { // 2
         PHPhotoLibrary.requestAuthorization { _ in // 3
            DispatchQueue.main.async { // 4
               self.pickPhotoFromLibrary() // 5
            }
         }
      }

      return
   }

   guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { return }

   let imagePicker = UIImagePickerController()
   imagePicker.delegate = self
   imagePicker.sourceType = .photoLibrary
   imagePicker.allowsEditing = false

   present(imagePicker, animated: true, completion: nil)
}
```

We first check the current status and if it’s `.notDetermined` — which means we haven’t asked yet — we ask for authorisation (3). Since this will present an alert, we can be extra safe and do it on the main thread (2).

At this point, the alert asking for access to their library will be show to the user. Since the user can still pick photos _even if they decline_, we need an explicit message of what’s happening, otherwise the user might get confused, or worse, panic. I went for something along the lines of: _”To automatically determine a photo's location, we need access to your library. You can still pick photos even if you don't allow, but you'll have to manually enter locations.”_

After the user taps _Allow_ or _Don’t allow_, the request’s handler is called, at which point we’ll call `pickPhotoFromLibrary` again (5), also on the main thread (4). At this point, the `authorizationStatus` will be determined (`restricted`, `denied` or `authorized`) and the `UIImagePickerController` will be presented.

There are a few extra steps for offering this small feature, but I think it’s worth it if you need the location, as it brings a nice touch; for us it was.