Most .plist files on a Mac or inside an app bundle are binary: they start with bplist00 and look like noise in a text editor. On a Mac, plutil -convert xml1 turns them back into XML. On Windows or Linux, or with a plist pulled out of an iOS backup, a crash log or a database blob, you usually need a script. Archives written by NSKeyedArchiver are worse: even as XML they are a flat $objects array full of CF$UID numbers.
This viewer does both in your browser. Drop the file and read it as XML, as JSON and, for keyed archives, as the object graph the app actually stored.
How to use
- Get the plist file: copy it out of the app bundle (
Contents/Info.plist,~/Library/Preferences/*.plist), an iOS backup or an IPA, or save the bytes of a database column that starts withbplist00. - Drop it onto the drop zone above, or press Choose a file. Binary and XML plists with any extension are accepted.
- Read the Summary first: binary or XML, object count, and whether it is an NSKeyedArchiver archive. Then open the object graph (keyed archives), the XML plist or the JSON, and download the view you need.
Real example output
We dropped the keyed archive into this page and copied what it displayed. Layout condensed (label and value on one line, buttons left out); every value is verbatim. The Decoded in time varies per run.
Sample: test/plist/keyed.bplist, an NSKeyedArchiver archive that Foundation wrote from the Swift script test/plist/keyed.swift on macOS 27 (fixture built for this site, 2026-11-02)
Summary
Format Binary plist (bplist00)
Size 1.0 KB
Object table 135 objects · 2-byte offsets · 1-byte references
Top-level object dict · 4 entries
Keyed archive NSKeyedArchiver, $version 100000 · 44 archived objects · $top: root
Decoded in 6 ms
NSKeyedArchiver object graph (JSON)
{
"root": {
"id": "6F9619FF-8B86-D011-B42D-00C04FC964FF",
"blob": {
"data": "3q2+7w==",
"bytes": 4
},
"count": 3,
"url": "https://example.com/notes?id=1",
"ratio": 0.5,
"tags": [
"alpha",
"beta",
"shared value"
],
"title": "Fixture Notes",
"nested": {
"again": "shared value",
…
},
"set": [
"only"
],
"enabled": true,
"created": "2026-11-02T08:30:00.000Z",
"nothing": null
}
}
Archived classes (8)
NSDictionary 2
NSArray 1
NSDate 1
NSMutableArray 1
NSNull 1
NSSet 1
NSURL 1
NSUUID 1What it reads
- Binary plists (
bplist00): every object type of Apple’s format (dictionaries, arrays, ASCII and UTF-16 strings, integers up to 128 bit, reals, booleans, dates, data, UIDs). Reference cycles and out-of-range offsets are rejected instead of looping. - XML plists, with or without the DOCTYPE. External entities and DTDs are never resolved.
- Any extension:
.plist,.bplist, binary.strings,PrivacyInfo.xcprivacy, a.savedStatefile, or bytes you exported from a database column.
What you see
- Summary: binary or XML, the size, the bplist object table (object count, offset and reference sizes), the top-level type and its entry count, and whether the file is a keyed archive.
- XML plist: written the way
plutil -convert xml1writes it (tab indent, dictionary keys sorted, base64 data in 68-column lines, dates in UTC without fractions), so you can diff it against a Mac’s output. - JSON: dates as ISO 8601 strings, data as
{ "data": base64, "bytes": n }, integers above 2^53 as strings.plutil -convert jsonrefuses any plist that contains a date or data; this view does not. - NSKeyedArchiver object graph: when
$archiverisNSKeyedArchiver, everyCF$UIDreference is followed from$top.NSArray,NSSetandNSOrderedSetbecome arrays,NSDictionarya dictionary,NSStringa string,NSDatadata,NSDatea date,NSUUIDa UUID string,NSURLits string,NSNullnull. Any other class (your own model objects,NSAttributedString,NSColor) is kept as a dictionary with its$classname and its keys, references resolved. An object that refers back to one still being decoded becomes{ "$ref": n }, the index in$objects. A table lists how many objects of each class the archive holds.
Each view has Download and Copy buttons. Very large outputs show their first 1 MiB on the page; Download saves all of it.
How we checked it
The fixtures are in test/plist/ and rebuilt by test/plist/build.sh on macOS 27: a hand-written XML plist with every type converted to binary by plutil -convert binary1, and an NSKeyedArchiver archive written by Foundation’s NSKeyedArchiver.archivedData from a Swift script, with an NSString shared by two containers. The unit tests check that this page’s XML equals plutil -convert xml1 byte for byte, and that the decoded graph matches the values the Swift script archived.
How your data stays private
- The viewer runs in a separate frame served from
tools.rea-reverse.com, and decoding runs in a Web Worker inside it, on your device. Nothing you drop is uploaded. - This page shows one ad, below the article. The ad script runs on
rea-reverse.com; the frame is a different origin, so the browser does not let the ad script, or any other script on this page, read the file you drop or the results. - The frame’s Content Security Policy blocks every request its scripts could make (
connect-src 'none', no third-party resources at all). The frame cannot open windows or navigate this page by itself. The only things it passes to this page are its height and the analytics events below. - Keys and values are shown as plain text. URLs inside the plist are not turned into links and are never requested.
- Analytics events contain only coarse values: success or an error code, binary or XML, whether it was a keyed archive, a size bucket and a duration bucket. No keys, values or class names. The frame hands them to this page, which sends them. The full list is on the privacy page.
- Prefer no ads and no analytics at all? Open the viewer on its own page.
How to verify this yourself
- Open your browser’s developer tools on the Network tab and load this page. Requests to
tools.rea-reverse.comare the frame: its HTML, styles, fonts and scripts, and the worker script/_astro/plist-worker-*.js. Requests to Google domains are the ad on this page. - Clear the log, then drop a plist. Type
domain:tools.rea-reverse.comin the filter box. At most the worker script/_astro/plist-worker-*.jsappears again (a fresh worker is prepared for the next run); no request carries your data. Ad requests may continue, but they come from this page and carry nothing from the frame. - Click the
plist-viewer/document fromtools.rea-reverse.comand open Headers: itsContent-Security-Policyhasconnect-src 'none', andframe-ancestors https://rea-reverse.com. In Chrome, Application > Frames also shows the frame’s origin and policy. The worker runs underdefault-src 'none'; script-src 'self'; connect-src 'none'. - Check the isolation: in the Console, with the context set to
top, rundocument.getElementById('tool-frame').contentDocument. It returnsnull.frames[0].documentthrows aSecurityError. That is what the ad script gets too. - Or load the page, go offline, and drop the file. It still works.
- On a Mac, compare with
plutil -convert xml1 -o - file.plist.
Limitations
- Read only: it does not write binary plists or edit values.
- Only
bplist00. Thebplist15/bplist16formats some Apple frameworks use internally are rejected with a clear message. - A real number with no fractional part (an
NSDatestored as815301000.0) is written as<integer>in the XML view, because the parser keeps one number type.plutilwrites<real>. Values are the same. - Classes outside Foundation’s basic set are not interpreted: you see their keys, not what the class does with them.
Command-line equivalent
On a Mac, plutil turns the same archive into XML, but the object graph stays a flat $objects array linked by CF$UID numbers (macOS 27, trimmed):
$ plutil -convert xml1 -o - keyed.bplist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>$archiver</key>
<string>NSKeyedArchiver</string>
<key>$objects</key>
<array>
<string>$null</string>
…
<key>NS.time</key>
<real>815301000</real>
</dict>
<dict>
<key>$classes</key>
<array>
<string>NSDate</string>
<string>NSObject</string>
</array>
<key>$classname</key>
<string>NSDate</string>
</dict>
…
<key>$top</key>
<dict>
<key>root</key>
<dict>
<key>CF$UID</key>
<integer>1</integer>
</dict>
</dict>
<key>$version</key>
<integer>100000</integer>
</dict>
</plist>For a plain binary plist, plutil -p prints a readable dump (the test/plist/settings.bplist fixture, trimmed):
$ plutil -p settings.bplist
{
"Big" => 9007199254740993
"Blob" => {length = 81, bytes = 0x000102030405060708090a0b0c0d0e0f ... 494a4b4c4d4e4f50}
"CFBundleIdentifier" => "com.example.fixture"
"CFBundleShortVersionString" => "2.5.0"
"Count" => 42
"Created" => 2026-11-02 08:30:00 +0000
…
"Greeting" => "Grüße, 你好 & <tags>"
…
}JSON output from plutil stops at the first date or data value:
$ plutil -convert json -o - settings.bplist
settings.bplist: Invalid object in plist for JSON formatFAQ
How do I view a binary plist online without uploading it?
Drop it on this page. The file is decoded in a Web Worker in your browser that has no network access, so it is never sent anywhere. On a Mac, plutil -p file.plist prints the same values.
How do I convert a bplist to XML?
Drop the file and use the XML plist view: Download saves it as .xml.plist. The output follows plutil -convert xml1 (sorted keys, tab indent, 68-column base64), so it diffs cleanly against a Mac's output.
How do I decode an NSKeyedArchiver archive?
Drop it here. When $archiver is NSKeyedArchiver, the page follows every CF$UID from $top and shows the result as JSON: arrays, dictionaries, strings, dates, UUIDs and URLs instead of $objects indexes. Unknown classes keep their $class name and keys.
Why does plutil -convert json fail on my plist?
JSON has no date or binary data type, so plutil refuses any plist that contains <date> or <data> with "Invalid object in plist for JSON format". This page writes dates as ISO 8601 strings and data as base64 instead.
Does it support bplist15 or bplist16?
No. Only bplist00, the format plutil and NSPropertyListSerialization write, is read. Files starting with another bplist version get an error message rather than a wrong result.
Updated
Related tools and guides
- Reverse engineer a macOS app with REA: where Info.plist, launchd plists and other property lists sit in an app bundle, and what to read in them.
- iOS provisioning profile and IPA viewer: reads the plists inside a
.mobileprovisionor an.ipaand checks them against each other. - File type identifier: when you are not sure the file is a plist at all.