Skip to content

feat(storage): read native iOS and Android app storage - #23

Open
lauridskern wants to merge 4 commits into
mainfrom
feat/native-app-storage
Open

lauridskern wants to merge 4 commits into
mainfrom
feat/native-app-storage

Conversation

@lauridskern

Copy link
Copy Markdown
Collaborator

The Storage tool now works for native iOS and Android apps, not only React Native ones. Two native test apps cover it, and every store type below was checked on a real simulator or emulator.

What changed

Native storage Before Now
SwiftData (default.store) Not found Opens as a SQLite database. When an app has an App Group, SwiftData keeps this store in the group container.
App Group containers (iOS) Not read Read under AppGroup/<id>/, including their shared UserDefaults. Reset empties them too.
Core Data / custom-named SQLite Only common file extensions Any file with a -wal/-shm companion counts as SQLite. Core Data and SwiftData dates show as dates in the table grid.
Files (Codable JSON, plists, NSKeyedArchiver, text) Listed only Clicking a file opens a preview: JSON and plists as a tree, NSKeyedArchiver object graphs resolved, text as is, other data as hex. The new mobile_app_storage_file tool does the same for the model.
EncryptedSharedPreferences Ciphertext shown as values Marked encrypted
Proto DataStore Listed only Decoded without a schema: numbered fields, repeats indexed, nested messages flattened

Also fixed:

  • Database row counts: some showed "?" because one sqlite3 process per table ran in parallel, and the processes failed on the write-ahead log lock. Every table is now described and counted in single queries.
  • Table paging: if you paged quickly, a slower response for an earlier page could replace a newer one. The panel now keeps one open table or file at a time and drops any response for one that's been replaced.

Test apps

  • examples/native-storage-ios: a SwiftUI app writing UserDefaults (every value type), App Group defaults, Core Data, SwiftData, a Codable JSON file, a binary plist, an NSKeyedArchiver archive and text. run.sh <UDID> signs it ad hoc, because the simulator only creates the App Group container when the entitlement is signed.
  • examples/native-storage-android: a Kotlin app (no Compose or code generation) writing SharedPreferences, EncryptedSharedPreferences, SQLite, DataStore Preferences and Proto, MMKV and files. The release build is debuggable so run-as can read it.

Verification

  • Automated: npm test passes (783 tests). New tests use real files taken from the test apps:
    • native store detection
    • Proto DataStore decoding
    • EncryptedSharedPreferences
    • App Group path mapping, including rejection of unknown groups and ..
    • file previews in every format, including NSKeyedArchiver cycles
    • SQLite table description
    • the paging race, with a test that fails under the old check
  • Packaging: test:package, test:codex-plugin and check:mcp-budget pass.
  • Manual:
    • iOS: checked on an iOS 27 simulator with the native app, in the panel too.
    • Android: checked on an Android 36 emulator. Every store reads correctly through the plugin's server code.

Notes

  • Reset and App Groups: Reset now also empties App Group containers, since SwiftData data would otherwise survive a reset. Group containers can be shared with other apps from the same developer.
  • Keychain: not read. It isn't in the app's data directory, and its items are encrypted.
  • MMKV values: an MMKV integer 1 still reads as true, because MMKV stores no types and the bytes are identical.
  • Telemetry: new file operation (app_storage.operation.*) and ui.storage.file timing. File contents are never sent.
  • Version: 0.1.162, one above main.

- Read App Group containers on iOS simulators under AppGroup/<id>/, so shared
  UserDefaults and SwiftData stores in them are found; reset empties them too.
- Recognize SwiftData's default.store and any SQLite file with -wal or -shm
  companions, and show Core Data and SwiftData dates in the table grid.
- Read every table's row count and columns in single queries; parallel sqlite3
  processes failed on the write-ahead log lock.
- Mark EncryptedSharedPreferences as encrypted and decode Proto DataStore files
  as numbered fields without a schema.
- Add mobile_app_storage_file and file previews in the panel, decoding JSON,
  binary and XML property lists, NSKeyedArchiver archives, text, and hex.
A SwiftUI app writes UserDefaults, App Group defaults, Core Data, SwiftData,
and Codable, plist, and archived files. A Kotlin app writes SharedPreferences,
EncryptedSharedPreferences, SQLite, DataStore Preferences and Proto, MMKV, and
files.
- Merge the panel's table and file slots into one detail, loaded by one helper
  that drops a response once a newer detail replaced it; this also fixes a
  slower earlier table page overwriting the page opened after it.
- Describe every SQLite table with one query, replacing the table list and the
  per-table PRAGMA.
- Make Proto DataStore its own store kind instead of re-checking the file name.
- Report files too large to preview as their own format.
- Move varint, UTF-8, and JSON helpers into encoding.ts, share the Apple date
  conversion, and drop a cast, an export, and duplicate operation types.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant