|
| 1 | +# Scry |
| 2 | + |
| 3 | +A pure Swift EXIF metadata parser. No dependencies, no Apple frameworks. Fully macOS and Linux compatible. |
| 4 | + |
| 5 | +Supports **JPEG**, **PNG**, **WebP**, and **TIFF**. |
| 6 | + |
| 7 | +## Usage |
| 8 | + |
| 9 | +```swift |
| 10 | +import Scry |
| 11 | + |
| 12 | +// From a file path |
| 13 | +if let metadata = try Scry.metadata(fromFileAt: "/path/to/photo.jpg") { |
| 14 | + print(metadata["Make"]) // "Apple" |
| 15 | + print(metadata["Model"]) // "iPhone 14 Pro" |
| 16 | + print(metadata["FNumber"]) // 1.78 |
| 17 | +} |
| 18 | + |
| 19 | +// From Data |
| 20 | +let data = Data(contentsOf: url) |
| 21 | +if let metadata = try Scry.metadata(from: data) { |
| 22 | + // format is auto-detected |
| 23 | +} |
| 24 | +``` |
| 25 | + |
| 26 | +Returns `nil` when the image has no EXIF data or the format is unsupported. |
| 27 | + |
| 28 | +## Installation |
| 29 | + |
| 30 | +Add to your `Package.swift`: |
| 31 | + |
| 32 | +```swift |
| 33 | +dependencies: [ |
| 34 | + .package(url: "https://github.com/loopwerk/Scry.git", from: "1.0.0"), |
| 35 | +] |
| 36 | +``` |
| 37 | + |
| 38 | +## Metadata properties |
| 39 | + |
| 40 | +`Scry.metadata` returns a flat `[String: Any]` dictionary. The keys and value types are: |
| 41 | + |
| 42 | +### Image info (from TIFF IFD0) |
| 43 | + |
| 44 | +| Key | Type | Example | |
| 45 | +|-----|------|---------| |
| 46 | +| `ImageWidth` | `Int` | `4032` | |
| 47 | +| `ImageLength` | `Int` | `3024` | |
| 48 | +| `ImageDescription` | `String` | `"Sunset photo"` | |
| 49 | +| `Make` | `String` | `"Apple"` | |
| 50 | +| `Model` | `String` | `"iPhone 14 Pro"` | |
| 51 | +| `Orientation` | `Int` | `1` | |
| 52 | +| `XResolution` | `Double` | `72.0` | |
| 53 | +| `YResolution` | `Double` | `72.0` | |
| 54 | +| `ResolutionUnit` | `Int` | `2` | |
| 55 | +| `Software` | `String` | `"18.7.3"` | |
| 56 | +| `DateTime` | `String` | `"2026:01:05 17:11:42"` | |
| 57 | +| `Artist` | `String` | `"Kevin Renskers"` | |
| 58 | +| `HostComputer` | `String` | `"iPhone 14 Pro"` | |
| 59 | +| `Copyright` | `String` | `"2026 Kevin Renskers"` | |
| 60 | + |
| 61 | +### Exposure and camera (from EXIF sub-IFD) |
| 62 | + |
| 63 | +| Key | Type | Example | |
| 64 | +|-----|------|---------| |
| 65 | +| `ExposureTime` | `Double` | `0.04` (seconds) | |
| 66 | +| `FNumber` | `Double` | `1.78` | |
| 67 | +| `ExposureProgram` | `Int` | `2` | |
| 68 | +| `ISOSpeedRatings` | `Int` | `800` | |
| 69 | +| `ExifVersion` | `[UInt8]` | `[2, 3, 2]` | |
| 70 | +| `DateTimeOriginal` | `String` | `"2026:01:05 17:11:42"` | |
| 71 | +| `DateTimeDigitized` | `String` | `"2026:01:05 17:11:42"` | |
| 72 | +| `ShutterSpeedValue` | `Double` | `4.64` (APEX) | |
| 73 | +| `ApertureValue` | `Double` | `1.66` (APEX) | |
| 74 | +| `BrightnessValue` | `Double` | `-2.50` (APEX) | |
| 75 | +| `ExposureBiasValue` | `Double` | `0.0` (EV) | |
| 76 | +| `MeteringMode` | `Int` | `5` | |
| 77 | +| `Flash` | `Int` | `16` | |
| 78 | +| `FocalLength` | `Double` | `6.86` (mm) | |
| 79 | +| `OffsetTime` | `String` | `"+01:00"` | |
| 80 | +| `OffsetTimeOriginal` | `String` | `"+01:00"` | |
| 81 | +| `OffsetTimeDigitized` | `String` | `"+01:00"` | |
| 82 | +| `PixelXDimension` | `Int` | `4032` | |
| 83 | +| `PixelYDimension` | `Int` | `3024` | |
| 84 | +| `CustomRendered` | `Int` | `0` | |
| 85 | +| `ExposureMode` | `Int` | `0` | |
| 86 | +| `WhiteBalance` | `Int` | `0` | |
| 87 | +| `FocalLenIn35mmFilm` | `Int` | `24` | |
| 88 | +| `SceneCaptureType` | `Int` | `0` | |
| 89 | +| `BodySerialNumber` | `String` | `"DNXXXXXXXXXXXX"` | |
| 90 | +| `LensSpecification` | `[Double]` | `[2.22, 9.0, 1.78, 2.8]` | |
| 91 | +| `LensMake` | `String` | `"Apple"` | |
| 92 | +| `LensModel` | `String` | `"iPhone 14 Pro back triple camera 6.86mm f/1.78"` | |
| 93 | +| `LensSerialNumber` | `String` | `"XXXXXXXXXX"` | |
| 94 | + |
| 95 | +### GPS (from GPS sub-IFD) |
| 96 | + |
| 97 | +| Key | Type | Example | |
| 98 | +|-----|------|---------| |
| 99 | +| `GPSVersionID` | `[Int]` | `[2, 3, 0, 0]` | |
| 100 | +| `GPSLatitudeRef` | `String` | `"N"` | |
| 101 | +| `GPSLatitude` | `[Double]` | `[52.0, 31.0, 12.74]` (deg, min, sec) | |
| 102 | +| `GPSLongitudeRef` | `String` | `"E"` | |
| 103 | +| `GPSLongitude` | `[Double]` | `[13.0, 24.0, 36.33]` (deg, min, sec) | |
| 104 | +| `GPSAltitudeRef` | `Int` | `0` (0 = above sea level) | |
| 105 | +| `GPSAltitude` | `Double` | `38.39` (meters) | |
| 106 | +| `GPSTimeStamp` | `[Double]` | `[16.0, 11.0, 42.0]` (hrs, min, sec) | |
| 107 | +| `GPSSpeedRef` | `String` | `"K"` (km/h) | |
| 108 | +| `GPSSpeed` | `Double` | `0.0` | |
| 109 | +| `GPSImgDirectionRef` | `String` | `"T"` (true north) | |
| 110 | +| `GPSImgDirection` | `Double` | `139.56` (degrees) | |
| 111 | +| `GPSDestBearingRef` | `String` | `"T"` | |
| 112 | +| `GPSDestBearing` | `Double` | `139.56` (degrees) | |
| 113 | +| `GPSDateStamp` | `String` | `"2026:01:05"` | |
| 114 | + |
| 115 | +Only keys present in the image are included in the dictionary. Not every image will have every property. |
0 commit comments