dub
dub

Reputation: 491

How To Retrieve CLLocation from PhotosPicker

I have noticed when the photo picker is presented, at the bottom it shows "Location Is Included", and the Options menu allows the user to turn that off.

How can we check if CLLocation or Captions as per the inclusion from the options menu are provided when getting the photo from:

PhotosPicker(selection: $selectedItem, matching: .images, photoLibrary: .shared()) {
                    
                    Text("Open Photo Picker")
                    
                }
                .onChange(of: selectedItem) { newItem in
                                
                    if let selected = newItem {
                        // Have PhotosPickerItem, now how to get CLLocation?
                    }
                               
                }

Is the only way to access metadata / gps info? WITHOUT having to access a PHAsset by requesting permission?

Upvotes: 2

Views: 84

Answers (1)

Sweeper
Sweeper

Reputation: 275125

From this answer, all you need to do is

  • load the PhotosPickerItem as Data
  • create a CGImageSource from the data
  • get metadata from the CGImageSource

So

@State var selection: PhotosPickerItem?
var body: some View {
    PhotosPicker("Pick", selection: $selection)
        .task(id: selection) {
            guard let data = try? await selection?.loadTransferable(type: Data.self),
                  let src = CGImageSourceCreateWithData(data as CFData, nil),
                  let metadata = CGImageSourceCopyPropertiesAtIndex(src,0,nil) else {
                return
            }
            print(metadata) // metadata is a CFDictionary
        }
}

The location info and caption are all somewhere in the metadata dictionary. The keys of the dictionaries are all listed in the subsections here.

The location information stored under the kCGImagePropertyGPSDictionary key, which is itself another dictionary. There are different keys for the latitude, longitude, altitude, speed, and so on.

Here I wrote a function that gets the location as a CLLocationCoordinate2D:

func coordinates(from metadata: CFDictionary) -> CLLocationCoordinate2D? {
    guard let dict = metadata as? [AnyHashable: Any],
          let gpsDict = dict[kCGImagePropertyGPSDictionary] as? [AnyHashable: Any],
          let lat = gpsDict[kCGImagePropertyGPSLatitude] as? Double,
          let lon = gpsDict[kCGImagePropertyGPSLongitude] as? Double,
          let latRef = gpsDict[kCGImagePropertyGPSLatitudeRef] as? String,
          let lonRef = gpsDict[kCGImagePropertyGPSLongitudeRef] as? String
        else { return nil }
    return CLLocationCoordinate2D(
        latitude: latRef == "N" ? lat : -lat,
        longitude: lonRef == "E" ? lon : -lon
    )
}

I've found two places where the caption is stored.

  • in the dictionary under the kCGImagePropertyTIFFDictionary key, then the kCGImagePropertyTIFFImageDescription key of that dictionary.
  • in the dictionary under the kCGImagePropertyIPTCDictionary key. then the kCGImagePropertyIPTCCaptionAbstract key of that dictionary.

Upvotes: 0

Related Questions