Phase 3 focuses on integrating SQLiteNIO observation with existing property wrappers while maintaining full backward compatibility with GRDB.
Added comprehensive transaction support for SQLiteNIO:
// Basic transaction with automatic rollback
try await connection.transaction { conn in
try await conn.query("INSERT INTO users (name) VALUES (?)", [.text("Alice")])
try await conn.query("INSERT INTO posts (title) VALUES (?)", [.text("Post")])
}
// Deferred transaction (locks acquired on first read/write)
try await connection.deferredTransaction { conn in
// ...
}
// Immediate transaction (write lock acquired immediately)
try await connection.immediateTransaction { conn in
// ...
}
// Exclusive transaction (prevents all other access)
try await connection.exclusiveTransaction { conn in
// ...
}
// Savepoints for nested transactions
try await connection.transaction { conn in
try await conn.query("INSERT INTO users (name) VALUES (?)", [.text("Alice")])
try? await conn.savepoint("inner") { conn in
try await conn.query("INSERT INTO bad_table (data) VALUES (?)", [.text("Bad")])
throw SomeError() // Only rolls back inner savepoint
}
// Alice is still inserted
}Goal: Make @FetchAll/@FetchOne work seamlessly with SQLiteNIO while maintaining GRDB compatibility.
Approach: Two options:
- Update FetchKey to detect if database is GRDB or SQLiteNIO
- Use appropriate observation mechanism automatically
- Completely transparent to users
- Add new initializers that accept SQLiteConnection
- Users explicitly choose SQLiteNIO
- Simpler implementation, clear intent
Decision: Start with Option B (explicit opt-in) as it:
- Maintains 100% backward compatibility
- Gives users explicit control
- Is easier to implement and test
- Can be upgraded to Option A later if needed
-
Add SQLiteConnection initializers to @FetchAll
@FetchAll(Item.all, connection: sqliteConnection) var items
-
Add SQLiteConnection initializers to @FetchOne
@FetchOne(Item.count, connection: sqliteConnection) var count = 0
-
Update FetchKeyRequest protocol
- Add method for fetching from SQLiteConnection
- Keep existing GRDB methods
-
Create SQLiteNIOFetchKey
- Similar to existing FetchKey but for SQLiteNIO
- Uses SQLiteNIOObserver for change observation
- Integrates with SharedReader
-
Add examples and tests
@FetchAll
└─> SharedReader
└─> FetchKey
├─> load: DatabaseReader.asyncRead
└─> subscribe: ValueObservation (GRDB)
@FetchAll
├─> (database: DatabaseReader) ─> FetchKey ─> ValueObservation (GRDB)
└─> (connection: SQLiteConnection) ─> SQLiteNIOFetchKey ─> SQLiteNIOObserver
import SQLiteData
import GRDB
// Setup GRDB database
let dbQueue = try DatabaseQueue(path: "db.sqlite")
// Use with property wrappers (works as before)
struct ContentView: View {
@FetchAll(User.all, database: dbQueue) var users
var body: some View {
List(users, id: \.id) { user in
Text(user.name)
}
}
}import SQLiteData
import SQLiteNIO
// Setup SQLiteNIO connection
let connection = try await SQLiteConnection.open(
storage: .file(path: "db.sqlite"),
threadPool: threadPool,
on: eventLoop
).get()
// Use with property wrappers (new initializers)
struct ContentView: View {
@FetchAll(User.all, connection: connection) var users
var body: some View {
List(users, id: \.id) { user in
Text(user.name)
}
}
}Button("Add User") {
Task {
try await connection.transaction { conn in
// Use StructuredQueries with SQLiteNIO
try await User.insert { $0.name; $0.email }
.values { "Alice"; "alice@example.com" }
.execute(conn)
// Property wrappers automatically update!
}
}
}-
Backward Compatibility Tests
- Verify existing @FetchAll/@FetchOne tests still pass
- Ensure GRDB usage is unchanged
-
SQLiteNIO Integration Tests
- Test property wrappers with SQLiteConnection
- Verify automatic updates on database changes
- Test with transactions
-
Mixed Usage Tests
- Use both GRDB and SQLiteNIO in same app
- Verify no conflicts or issues
- Transaction Support: ✅ Complete (2 hours)
- Property Wrapper Integration: 🔄 In Progress (4-6 hours)
- SQLiteConnection initializers (1-2 hours)
- SQLiteNIOFetchKey implementation (2-3 hours)
- Tests and examples (1 hour)
- Documentation: 📝 Pending (1 hour)
Total Estimated: 7-9 hours Completed: 2 hours Remaining: 5-7 hours
- @FetchAll works with SQLiteConnection
- @FetchOne works with SQLiteConnection
- Automatic UI updates when database changes (via SQLiteNIOObserver)
- Transaction support tested and working
- Examples demonstrate usage
- All existing tests still pass
- New tests for SQLiteNIO integration pass
- Documentation updated
- Keep public API consistent between GRDB and SQLiteNIO versions
- Minimize code duplication
- Ensure proper error handling and cleanup
- Consider adding convenience methods for common patterns