Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Photos taken with the camera and images picked from the gallery carry additional

Keys are omitted when the value is unavailable, so existing consumers are unaffected.

`takenAt` is always read from the image's EXIF data and needs no extra permissions. GPS coordinates are only present if they are already embedded in the file. By default the plugin does **not** ask for any new permissions.
`takenAt` is always read from the image's EXIF data and needs no extra permissions. For gallery images, GPS coordinates are only present if they are already embedded in the file. On iOS, photos taken with the camera never carry location, in the file or the payload, unless you opt in below. By default the plugin does **not** ask for any new permissions.

### Opting into location

Expand Down
31 changes: 22 additions & 9 deletions resources/ios/CameraFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,8 @@ final class CameraPhotoDelegate: NSObject, UIImagePickerControllerDelegate, UINa

// Grab the best location fix we have at capture time (may be nil). Only consulted when
// the caller opted in; otherwise the provider was never started and no prompt was shown.
let captureLocation = pendingIncludeLocation ? CameraLocationProvider.shared.currentLocation : nil
let includeLocation = pendingIncludeLocation
let captureLocation = includeLocation ? CameraLocationProvider.shared.currentLocation : nil
CameraLocationProvider.shared.stop()
pendingIncludeLocation = false

Expand Down Expand Up @@ -641,9 +642,15 @@ final class CameraPhotoDelegate: NSObject, UIImagePickerControllerDelegate, UINa
}

// Build the metadata dictionary to embed, starting from the original capture
// metadata so Exif (incl. DateTimeOriginal) and any existing GPS are preserved.
// metadata so Exif (incl. DateTimeOriginal) is preserved.
var imageProperties = mediaMetadata

// The capture metadata can already contain GPS when the app holds location
// authorization for some other reason. Only keep it if the caller opted in.
if !includeLocation {
imageProperties.removeValue(forKey: kCGImagePropertyGPSDictionary as String)
}

// If the capture metadata lacks GPS (e.g. no location authorization at capture
// time), synthesise one from our CLLocation fix so the file stays geotagged.
let hasGPS = imageProperties[kCGImagePropertyGPSDictionary as String] != nil
Expand Down Expand Up @@ -673,6 +680,7 @@ final class CameraPhotoDelegate: NSObject, UIImagePickerControllerDelegate, UINa
fileURL: fileURL,
eventClass: eventClass,
mediaMetadata: mediaMetadata,
includeLocation: includeLocation,
captureLocation: captureLocation
)
return
Expand All @@ -697,6 +705,7 @@ final class CameraPhotoDelegate: NSObject, UIImagePickerControllerDelegate, UINa
fileURL: fileURL,
eventClass: eventClass,
mediaMetadata: mediaMetadata,
includeLocation: includeLocation,
captureLocation: captureLocation
)

Expand Down Expand Up @@ -724,6 +733,7 @@ final class CameraPhotoDelegate: NSObject, UIImagePickerControllerDelegate, UINa
fileURL: URL,
eventClass: String,
mediaMetadata: [String: Any],
includeLocation: Bool,
captureLocation: CLLocation?
) {
var payload: [String: Any] = [
Expand All @@ -739,13 +749,16 @@ final class CameraPhotoDelegate: NSObject, UIImagePickerControllerDelegate, UINa
let takenAtDate = CameraMetadata.dateTimeOriginal(from: mediaMetadata) ?? Date()
payload["takenAt"] = CameraMetadata.isoString(from: takenAtDate)

// latitude/longitude: prefer GPS embedded in the capture metadata, then our CLLocation.
if let coordinate = CameraMetadata.coordinate(from: mediaMetadata) {
payload["latitude"] = coordinate.latitude
payload["longitude"] = coordinate.longitude
} else if let location = captureLocation {
payload["latitude"] = location.coordinate.latitude
payload["longitude"] = location.coordinate.longitude
// latitude/longitude: only when the caller opted in. Prefer GPS embedded in the capture
// metadata, then our CLLocation.
if includeLocation {
if let coordinate = CameraMetadata.coordinate(from: mediaMetadata) {
payload["latitude"] = coordinate.latitude
payload["longitude"] = coordinate.longitude
} else if let location = captureLocation {
payload["latitude"] = location.coordinate.latitude
payload["longitude"] = location.coordinate.longitude
}
}

// Dispatch event with slight delay to ensure UI is ready
Expand Down
Loading