Reverse-Engineering Apple's Photographic Styles
Every iPhone photo knows which Photographic Style it was shot in. On current iPhones it is in two places, neither documented: a name in the Photos database, and a number in an unnamed maker-note tag. This is the complete mapping for both.
While building Untitled, I wanted the metadata card to name each photo's Photographic Style. That transpired to entail reverse-engineering the metadata of two iPhone generations, and finding that the newer one records the style twice, in two entirely different places.
iPhone 13–15: a plist in the maker note
The original styles — Standard, Vibrant, Rich Contrast, Warm, Cool — are baked into the image at capture and stored in the EXIF {MakerApple} dictionary, tag 0x0040: a binary plist whose _3 key holds the preset number, 1 through 5 in that order. This half is already documented, by ExifTool.
Two traps guard it. {MakerApple} is keyed by decimal strings, so tag 0x0040 lives at makerApple["64"], not "40": a wrong key fails silently. And PhotosPickerItem.loadTransferable(type: Data.self) transcodes HEIC to JPEG, which strips the maker note entirely; you have to load the original file via a FileRepresentation transferable.
iPhone 16/17: the name, in the Photos database
With iPhone 16, styles became non-destructive, and the style's name left EXIF. It moved to the photo's adjustment data: PHAsset → requestContentEditingInput → adjustmentData.data, which is zlib-compressed JSON. Decompressed, the style is sitting there in plaintext:
"settings": {
"cast": "DreamyHues",
"tone": 0.35,
"color": 0.30,
"intensity": 1
}
The path is adjustments[] → settings.cast, on the entry whose identifier is "SemanticStyle". The tone, color, and intensity values are the positions of the sliders in the camera UI.
DreamyHues is an internal codename. As such, the mapping had to be built empirically: set each style as a per-shot override, shoot, decompress, read off the cast. (One of the sixteen, Stark B&W, crashed my dumper on every attempt: a String(format:) bug in the hex printer, not a protection. The decode was fine.)
iPhone 16/17: the number, still in EXIF
The name exited the file, but a number stayed behind, in a maker-note tag ExifTool reports only as unknown: 0x0054 — decimal 84, so makerApple["84"] — holding a small plist of six keys.
Sixteen frames of one scene, shot in a single sitting with the sliders untouched, isolate what it holds. _1, _2, and _3 are tone, color, and intensity: not adjustments, but each style's own built-in position, which is why they carry values at all when nothing was touched. _0 and _5 never move. And _4 runs 1 through 16 across the sixteen styles, with no gaps and no collisions. It is an index.
Two things corroborate this. Luminous reports tone 0.35, color 0.30, intensity 1, the exact triple the adjustment-data JSON gives for DreamyHues, arrived at four months earlier by a completely different route. And a second batch shot on a different day, in different light, at a different focus distance, returns identical tuples for the styles it shares. The numbers belong to the style, not to the photograph.
One caution about what the tag actually means. Shoot a frame as Dramatic, restyle it to Ethereal in Photos, and export: _4 reads 10, Ethereal, with Ethereal's tone and color. The tag describes the rendition in your hand, not the moment of capture. Which also explains a trap that will otherwise waste an afternoon: exporting from Photos as an unmodified original reverts the frame to your default style and faithfully records that, so a folder of differently styled photos comes back reporting one identical value. Export the plain original instead.
Lacuna
Your default style (the one set in Settings → Camera) is invisible to the named path. From iOS's perspective the default is simply what a photo is: it produces no adjustment data at all, adjustmentData is nil, and the styledeltamap auxiliary image that would reveal it is unreachable through public API. Only per-shot overrides leave a trace there. I confirmed this by switching my default from Bright to Vibrant, then shooting with Bright selected as an override. The adjustment data appeared, cast: "BrightPop".
The numeric tag appears not to share the blindness — a frame shot on a default of Bright still reports _4=16 — which would make the older, cruder record the more complete of the two.
In practice
The maker note is the better path, and it covers both generations from the file alone: load the original HEIC via FileRepresentation, then read makerApple["64"] and map _3 for iPhone 13–15, or makerApple["84"] and map _4 for iPhone 16/17. No PHAsset, no requestContentEditingInput, and no photo-library permission prompt.
Take the adjustment-data route when you want the name as Apple spells it: resolve the PHAsset from PhotosPickerItem.itemIdentifier, request content editing input with canHandleAdjustmentData returning true, zlib-decompress, parse, and read settings.cast from the SemanticStyle entry. The full extractor ships in Untitled's repo.
Tested on iPhone 17 Pro Max. The adjustment-data path on iOS 26 beta (23E5234a), March 2026; the maker-note index on iOS 27, July 2026, where 0x0054 holds six keys. Older captures carry eight, with _4 in the same place.
Written while building Untitled, an Instagram publishing client by Moon.
_4 |
Style | Tone | Color | Intensity |
|---|---|---|---|---|
1 | Standard | 0 | 0 | 1 |
4 | Amber | 0.20 | 0.20 | 1 |
14 | Gold | 0.20 | 0.10 | 0.85 |
3 | Rose Gold | -0.20 | -0.20 | 0.75 |
16 | Bright | 0.18 | 0 | 1 |
2 | Neutral | 0.40 | -0.10 | 0.80 |
5 | Cool Rose | 0.35 | -0.10 | 0.75 |
8 | Vibrant | -0.30 | 0.33 | 1 |
6 | Natural | -0.70 | -0.15 | 1 |
12 | Luminous | 0.35 | 0.30 | 1 |
11 | Dramatic | -0.76 | 0.19 | 1 |
7 | Quiet | 0.50 | 0.45 | 1 |
9 | Cozy | -0.75 | -0.20 | 1 |
10 | Ethereal | 0.68 | -0.15 | 1 |
15 | Muted B&W | 0.40 | 0 | 0.50 |
13 | Stark B&W | -1 | 0 | 0.50 |
Rows in camera order; the index is not. Float32, shown rounded. Stark B&W's tone is stored as an integer rather than a float, so a naive reader prints it as 18446744073709551615 — unsigned −1.
| Camera UI name | Internal cast | Category |
|---|---|---|
| Standard | Standard | Undertone |
| Amber | TanWarm | Undertone |
| Gold | GoldWarm | Undertone |
| Rose Gold | BlushWarm | Undertone |
| Bright | BrightPop | Undertone (added iOS 26) |
| Neutral | Neutral | Undertone |
| Cool Rose | Cool | Undertone |
| Vibrant | Colorful | Mood |
| Natural | NoFilter | Mood |
| Luminous | DreamyHues | Mood |
| Dramatic | UrbanCool | Mood |
| Quiet | WarmAuthentic | Mood |
| Cozy | Earthy | Mood |
| Ethereal | CloudCover | Mood |
| Muted B&W | LongGray | Mood |
| Stark B&W | StarkBW | Mood |

