RxGRDB Swift 5.7 Platforms License Build Status

用于 SQLiteGRDB.swiftRxSwift 的一组扩展

最新版本: 2022 年 9 月 9 日 • 版本 3.0.0发行说明

要求: iOS 11.0+ / macOS 10.13+ / tvOS 11.0+ / watchOS 4.0+ • Swift 5.7+ / Xcode 14+

Swift 版本 RxGRDB 版本
Swift 5.7 v3.0.0
Swift 5.3 v2.1.0
Swift 5.2 v2.0.0
Swift 5.1 v0.18.0
Swift 5.0 v0.18.0
Swift 4.2 v0.13.0
Swift 4.1 v0.11.0
Swift 4 v0.10.0
Swift 3.2 v0.6.0
Swift 3.1 v0.6.0
Swift 3 v0.3.0

用法

要连接到数据库,请参考支持 RxGRDB 的数据库库 GRDB

异步从数据库读取

此可观察对象读取单个值并传递它。

// Single<[Player]>
let players = dbQueue.rx.read { db in
    try Player.fetchAll(db)
}

players.subscribe(
    onSuccess: { (players: [Player]) in
        print("Players: \(players)")
    },
    onError: { error in ... })
异步写入数据库

此可观察对象在数据库更新后完成。

// Single<Void>
let write = dbQueue.rx.write { db in 
    try Player(...).insert(db)
}

write.subscribe(
    onSuccess: { _ in
        print("Updates completed")
    },
    onError: { error in ... })

// Single<Int>
let newPlayerCount = dbQueue.rx.write { db -> Int in
    try Player(...).insert(db)
    return try Player.fetchCount(db)
}

newPlayerCount.subscribe(
    onSuccess: { (playerCount: Int) in
        print("New players count: \(playerCount)")
    },
    onError: { error in ... })
观察数据库值的变化

每当数据库发生变化时,此可观察对象都会传递新的值

// Observable<[Player]>
let observable = ValueObservation
    .tracking { db in try Player.fetchAll(db) }
    .rx.observe(in: dbQueue)

observable.subscribe(
    onNext: { (players: [Player]) in
        print("Fresh players: \(players)")
    },
    onError: { error in ... })

// Observable<Int?>
let observable = ValueObservation
    .tracking { db in try Int.fetchOne(db, sql: "SELECT MAX(score) FROM player") }
    .rx.observe(in: dbQueue)

observable.subscribe(
    onNext: { (maxScore: Int?) in
        print("Fresh maximum score: \(maxScore)")
    },
    onError: { error in ... })
观察数据库事务

每当数据库事务影响到被观察的区域时,此可观察对象都会传递数据库连接

// Observable<Database>
let observable = DatabaseRegionObservation
    .tracking(Player.all())
    .rx.changes(in: dbQueue)

observable.subscribe(
    onNext: { (db: Database) in
        print("Exclusive write access to the database after players have been impacted")
    },
    onError: { error in ... })

// Observable<Database>
let observable = DatabaseRegionObservation
    .tracking(SQLRequest<Int>(sql: "SELECT MAX(score) FROM player"))
    .rx.changes(in: dbQueue)

observable.subscribe(
    onNext: { (db: Database) in
        print("Exclusive write access to the database after maximum score has been impacted")
    },
    onError: { error in ... })

文档

安装

要将 RxGRDB 与 Swift Package Manager 一起使用,请将依赖项添加到您的 Package.swift 文件中

let package = Package(
    dependencies: [
        .package(url: "https://github.com/RxSwiftCommunity/RxGRDB.git", ...)
    ]
)

要将 RxGRDB 与 CocoaPods 一起使用,请在您的 Podfile 中指定

# Pick only one
pod 'RxGRDB'
pod 'RxGRDB/SQLCipher'

异步数据库访问

RxGRDB 提供了执行异步数据库访问的可观察对象。

DatabaseReader.rx.read(observeOn:value:)

此方法返回一个 Single,它在异步获取数据库值后完成。

// Single<[Player]>
let players = dbQueue.rx.read { db in
    try Player.fetchAll(db)
}

任何修改数据库的尝试都会导致订阅以错误完成。

当您使用数据库队列数据库快照时,读取操作必须等待此队列或快照执行的任何可能的并发数据库访问完成。

当您使用数据库连接池时,除非已达到最大并发读取数,否则读取通常是非阻塞的。在这种情况下,读取操作必须等待另一个读取操作完成。最大数量可以配置

此可观察对象可以从任何线程订阅。每次订阅都会启动新的数据库访问。

除非您为 observeOn 参数提供特定的调度器,否则获取的值将在主队列上发布。

DatabaseWriter.rx.write(observeOn:updates:)

此方法返回一个 Single,它在数据库更新在数据库事务中成功执行后完成。

// Single<Void>
let write = dbQueue.rx.write { db in
    try Player(...).insert(db)
}

// Single<Int>
let newPlayerCount = dbQueue.rx.write { db -> Int in
    try Player(...).insert(db)
    return try Player.fetchCount(db)
}

此可观察对象可以从任何线程订阅。每次订阅都会启动新的数据库访问。

它在主队列上完成,除非您为 observeOn 参数提供特定的 调度器

您可以忽略它的值,并使用 asCompletable 运算符将其转换为 Completable

// Completable
let write = dbQueue.rx
    .write { db in try Player(...).insert(db) }
    .asCompletable()

当您使用 数据库连接池,并且您的应用程序执行了一些数据库更新,然后执行了一些缓慢的获取操作,您可以通过 rx.write(observeOn:updates:thenRead:) 来优化调度。请参见下文。

DatabaseWriter.rx.write(observeOn:updates:thenRead:)

此方法返回一个 Single,它在数据库更新在数据库事务中成功执行后完成,并且随后获取了值

// Single<Int>
let newPlayerCount = dbQueue.rx.write(
    updates: { db in try Player(...).insert(db) }
    thenRead: { db, _ in try Player.fetchCount(db) })
}

它发布的值与 rx.write(observeOn:updates:) 完全相同

// Single<Int>
let newPlayerCount = dbQueue.rx.write { db -> Int in
    try Player(...).insert(db)
    return try Player.fetchCount(db)
}

区别在于最后一次获取是在 thenRead 函数中执行的。此函数接受两个参数:只读数据库连接和 updates 函数的结果。这允许您将信息从一个函数传递到另一个函数(在上面的示例代码中被忽略)。

当您使用 数据库连接池时,此方法应用了调度优化:thenRead 函数看到的是 updates 函数留下的数据库状态,但不会阻止任何并发写入。这可以减少数据库写入争用。有关更多信息,请参见 高级 DatabasePool

当您使用 数据库队列时,结果保证是相同的,但不会应用任何调度优化。

此可观察对象可以从任何线程订阅。每次订阅都会启动新的数据库访问。

它在主队列上完成,除非您为 observeOn 参数提供特定的 调度器

数据库观察

数据库观察可观察对象基于 GRDB 的 ValueObservationDatabaseRegionObservation。有关更多信息,请参考它们的文档。如果您的应用程序需要 RxGRDB 中未内置的更改通知,请查看通用的 数据库更改观察章节。

ValueObservation.rx.observe(in:scheduling:)

GRDB 的 ValueObservation 跟踪数据库值的变化。您可以将其转换为 RxSwift 可观察对象

let observation = ValueObservation.tracking { db in
    try Player.fetchAll(db)
}

// Observable<[Player]>
let observable = observation.rx.observe(in: dbQueue)

此可观察对象的行为与 ValueObservation 相同

有关更多信息,请参见 ValueObservation 调度

⚠️ ValueObservation 和数据一致性

当您使用 combineLatest 运算符将 ValueObservation 可观察对象组合在一起时,您将失去所有 数据一致性保证。

相反,将请求组合成一个 ValueObservation,如下所示

// DATA CONSISTENCY GUARANTEED
let hallOfFameObservable = ValueObservation
    .tracking { db -> HallOfFame in
        let playerCount = try Player.fetchCount(db)
        let bestPlayers = try Player.limit(10).orderedByScore().fetchAll(db)
        return HallOfFame(playerCount:playerCount, bestPlayers:bestPlayers)
    }
    .rx.observe(in: dbQueue)

有关更多信息,请参见 ValueObservation

DatabaseRegionObservation.rx.changes(in:)

GRDB 的 DatabaseRegionObservation 通知所有影响跟踪数据库区域的事务。您可以将其转换为 RxSwift 可观察对象

let request = Player.all()
let observation = DatabaseRegionObservation.tracking(request)

// Observable<Database>
let observable = observation.rx.changes(in: dbQueue)

此可观察对象可以从任何线程创建和订阅。它在“受保护的调度队列”中传递数据库连接,与所有数据库更新序列化。它仅在发生数据库错误时完成。

let request = Player.all()
let disposable = DatabaseRegionObservation
    .tracking(request)
    .rx.changes(in: dbQueue)
    .subscribe(
        onNext: { (db: Database) in
            print("Players have changed.")
        },
        onError: { error in ... })

try dbQueue.write { db in
    try Player(name: "Arthur").insert(db)
    try Player(name: "Barbara").insert(db)
} 
// Prints "Players have changed."

try dbQueue.write { db in
    try Player.deleteAll(db)
}
// Prints "Players have changed."

有关更多信息,请参见 DatabaseRegionObservation