Madrid
Swift package for reading your iMessage® database, with support for Apple's typedstream format
Install / Use
/learn @mattt/MadridREADME
Madrid
Madrid is a Swift package that provides read-only access to
your iMessage® chat.db database.
It comprises the following two modules:
- iMessage: Core functionality for querying an iMessage database.
- TypedStream:
A Swift implementation for decoding Apple's
typedstreamformat, adapted from Christopher Sardegna's work on imessage-exporter.
Requirements
- Xcode 16+
- Swift 6.0+
- macOS 13.0+
Installation
Swift Package Manager
Add Madrid as a dependency to your Package.swift:
dependencies: [
.package(url: "https://github.com/mattt/Madrid.git", from: "0.4.0")
]
Then add the modules you need to your target's dependencies:
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "iMessage", package: "Madrid"),
.product(name: "TypedStream", package: "Madrid")
]
)
]
Usage
Fetching Messages
import iMessage
// Create a database (uses `~/Library/Messages/chat.db` by default)
let db = try iMessage.Database()
// Build a predicate-style request
let pastWeek = Date.now.addingTimeInterval(-7 * 24 * 60 * 60) ..< Date.now
let request = FetchRequest<Message>(
predicate: .and([
.participantHandles([
"johnny.appleseed@mac.com",
"+18002752273",
]),
.dateRange(pastWeek),
]),
sortDescriptors: [.date(.descending)],
limit: 10
)
// Execute request
for message in try db.fetch(request) {
print("From: \(message.sender)")
print("Content: \(message.text)")
print("Sent at: \(message.date)")
}
[!TIP] Legacy convenience APIs (
fetchMessages(for:with:in:limit:),fetchChats(with:in:limit:)) are still available, but are deprecated in favor offetch(_:).
Decoding TypedStream Data
import TypedStream
let decoder = TypedStreamDecoder()
let data = // ... your typedstream data ...
let result = try decoder.decode(data)
print(result.stringValue)
FAQ
"Database Disk Image is Malformed"
If you get the error message "database disk image is malformed" when attempting to connect to your iMessage database, it typically indicates corruption in the SQLite file. The error most often occurs when attempting to read the database while another process (like the Messages app) is actively using it.
To check if the database file is corrupt, you can use SQLite's built-in integrity check`:
sqlite3 ~/Library/Messages/chat.db "PRAGMA integrity_check;
If the original database file is corrupt, restore from a Time Machine backup or other backup source.
The most reliable way to prevent this error is to operate on a copy of the iMessage database:
-
Quit Messages: Ensure the Messages app is completely closed.
-
Copy All Database Files:
# Create destination directory mkdir -p ~/imessage_db_copy # Copy main database and supporting files cp -p ~/Library/Messages/chat.db ~/imessage_db_copy/ # Copy WAL and shared memory files if they exist cp -p ~/Library/Messages/chat.db-* ~/imessage_db_copy/ 2>/dev/null || trueAlways include the
-shmand-walfiles when copying a SQLite database using WAL mode -
Use the Copied Database:
let homeURL = FileManager.default.homeDirectoryForCurrentUser let dbURL = homeURL.appendingPathComponent("imessage_db_copy/chat.db") let db = try iMessage.Database(path: dbURL.path)
Acknowledgments
- Christopher Sardegna
(@ReagentX)
for reverse-engineering the
typedstreamformat.
Legal
iMessage® is a registered trademark of Apple Inc. This project is not affiliated with, endorsed, or sponsored by Apple Inc.
License
This project is available under the MIT license. See the LICENSE file for more info.
Related Skills
feishu-drive
345.9k|
things-mac
345.9kManage Things 3 via the `things` CLI on macOS (add/update projects+todos via URL scheme; read/search/list from the local Things database)
clawhub
345.9kUse the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com
postkit
PostgreSQL-native identity, configuration, metering, and job queues. SQL functions that work with any language or driver
