All Projects → RxSwiftCommunity → Rxgrdb

RxSwiftCommunity / Rxgrdb

Licence: mit
Reactive extensions for SQLite

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to Rxgrdb

Rxrealm
RxSwift extension for RealmSwift's types
Stars: ✭ 1,007 (+472.16%)
Mutual labels:  reactive, rxswift, database
Grdbcombine
GRDB ❤️ Combine
Stars: ✭ 220 (+25%)
Mutual labels:  reactive, database, sqlite
Reduxmoviedb
🎥 See the upcoming movies! ReSwift + RxSwift 💖 Hacktoberfest 🎃
Stars: ✭ 137 (-22.16%)
Mutual labels:  reactive, rxswift
Roomasset
A helper library to help using Room with existing pre-populated database [DEPRECATED].
Stars: ✭ 138 (-21.59%)
Mutual labels:  database, sqlite
Rxiglistkit
IGListKit with RxSwift🚀
Stars: ✭ 174 (-1.14%)
Mutual labels:  reactive, rxswift
Kangaroo
SQL client and admin tool for popular databases
Stars: ✭ 127 (-27.84%)
Mutual labels:  database, sqlite
Goose
A database migration tool. Supports SQL migrations and Go functions.
Stars: ✭ 2,112 (+1100%)
Mutual labels:  database, sqlite
Sqlite Jdbc
SQLite JDBC Driver
Stars: ✭ 1,961 (+1014.2%)
Mutual labels:  database, sqlite
R2dbc H2
R2DBC H2 Implementation
Stars: ✭ 118 (-32.95%)
Mutual labels:  reactive, database
Vue Materialize Datatable
A fancy Materialize CSS datatable VueJS component.
Stars: ✭ 162 (-7.95%)
Mutual labels:  reactive, database
Sqlitestudio
A free, open source, multi-platform SQLite database manager.
Stars: ✭ 2,337 (+1227.84%)
Mutual labels:  database, sqlite
Esp32 arduino sqlite3 lib
Sqlite3 Arduino library for ESP32
Stars: ✭ 167 (-5.11%)
Mutual labels:  database, sqlite
Directus
Open-Source Data Platform 🐰 — Directus wraps any SQL database with a real-time GraphQL+REST API and an intuitive app for non-technical users.
Stars: ✭ 13,190 (+7394.32%)
Mutual labels:  database, sqlite
Myblog
python写的博客,支持3种数据库,现在挂在evilbinary.org
Stars: ✭ 121 (-31.25%)
Mutual labels:  database, sqlite
Easyreact
Are you confused by the functors, applicatives, and monads in RxSwift and ReactiveCocoa? It doesn't matter, the concepts are so complicated that not many developers actually use them in normal projects. Is there an easy-to-use way to use reactive programming? EasyReact is born for this reason.
Stars: ✭ 1,616 (+818.18%)
Mutual labels:  reactive, rxswift
Sqift
Powerful Swift wrapper for SQLite
Stars: ✭ 119 (-32.39%)
Mutual labels:  database, sqlite
Sql Fundamentals
👨‍🏫 Mike's SQL Fundamentals and Professional SQL Courses
Stars: ✭ 140 (-20.45%)
Mutual labels:  database, sqlite
Linq2db
Linq to database provider.
Stars: ✭ 2,211 (+1156.25%)
Mutual labels:  database, sqlite
Bible Database
Bible databases as XML, JSON, SQL & SQLITE3 Database format for various languages. Developers can download it freely for their development works. Freely received, freely give.
Stars: ✭ 111 (-36.93%)
Mutual labels:  database, sqlite
Rxasdatasources
RxDataSource for AsyncDisplayKit/Texture
Stars: ✭ 114 (-35.23%)
Mutual labels:  reactive, rxswift

RxGRDB Swift 5.2 Platforms License Build Status

A set of extensions for SQLite, GRDB.swift, and RxSwift

Latest release: January 3, 2021 • version 2.0.0 • Release Notes

Requirements: iOS 10.0+ / OSX 10.10+ / tvOS 9.0+ / watchOS 2.0+ • Swift 5.2+ / Xcode 11.4+

Swift version RxGRDB version
Swift 5.2+ v2.0.0, 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

Usage

To connect to the database, please refer to GRDB, the database library that supports RxGRDB.

Asynchronously read from the database

This observable reads a single value and delivers it.

// 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 ... })
Asynchronously write in the database

This observable completes after the database has been updated.

// 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 ... })
Observe changes in database values

This observable delivers fresh values whenever the database changes:

// 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 ... })
Observe database transactions

This observable delivers database connections whenever a database transaction has impacted an observed region:

// 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 ... })

Documentation

Installation

To use RxGRDB with the Swift Package Manager, add a dependency to your Package.swift file:

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

To use RxGRDB with CocoaPods, specify in your Podfile:

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

Asynchronous Database Access

RxGRDB provide observables that perform asynchronous database accesses.

DatabaseReader.rx.read(observeOn:value:)

This methods returns a Single that completes after database values have been asynchronously fetched.

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

Any attempt at modifying the database completes subscriptions with an error.

When you use a database queue or a database snapshot, the read has to wait for any eventual concurrent database access performed by this queue or snapshot to complete.

When you use a database pool, reads are generally non-blocking, unless the maximum number of concurrent reads has been reached. In this case, a read has to wait for another read to complete. That maximum number can be configured.

This observable can be subscribed from any thread. A new database access starts on every subscription.

The fetched value is published on the main queue, unless you provide a specific scheduler to the observeOn argument.

DatabaseWriter.rx.write(observeOn:updates:)

This method returns a Single that completes after database updates have been successfully executed inside a database transaction.

// 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)
}

This observable can be subscribed from any thread. A new database access starts on every subscription.

It completes on the main queue, unless you provide a specific scheduler to the observeOn argument.

You can ignore its value and turn it into a Completable with the asCompletable operator:

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

When you use a database pool, and your app executes some database updates followed by some slow fetches, you may profit from optimized scheduling with rx.write(observeOn:updates:thenRead:). See below.

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

This method returns a Single that completes after database updates have been successfully executed inside a database transaction, and values have been subsequently fetched:

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

It publishes exactly the same values as rx.write(observeOn:updates:):

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

The difference is that the last fetches are performed in the thenRead function. This function accepts two arguments: a readonly database connection, and the result of the updates function. This allows you to pass information from a function to the other (it is ignored in the sample code above).

When you use a database pool, this method applies a scheduling optimization: the thenRead function sees the database in the state left by the updates function, and yet does not block any concurrent writes. This can reduce database write contention. See Advanced DatabasePool for more information.

When you use a database queue, the results are guaranteed to be identical, but no scheduling optimization is applied.

This observable can be subscribed from any thread. A new database access starts on every subscription.

It completes on the main queue, unless you provide a specific scheduler to the observeOn argument.

Database Observation

Database Observation observables are based on GRDB's ValueObservation and DatabaseRegionObservation. Please refer to their documentation for more information. If your application needs change notifications that are not built in RxGRDB, check the general Database Changes Observation chapter.

ValueObservation.rx.observe(in:scheduling:)

GRDB's ValueObservation tracks changes in database values. You can turn it into an RxSwift observable:

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

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

This observable has the same behavior as ValueObservation:

  • It notifies an initial value before the eventual changes.

  • It may coalesce subsequent changes into a single notification.

  • It may notify consecutive identical values. You can filter out the undesired duplicates with the distinctUntilChanged() RxSwift operator, but we suggest you have a look at the removeDuplicates() GRDB operator also.

  • It stops emitting any value after the database connection is closed. But it never completes.

  • By default, it notifies the initial value, as well as eventual changes and errors, on the main thread, asynchronously.

    This can be configured with the scheduling argument. It does not accept an RxSwift scheduler, but a GRDB scheduler.

    For example, the .immediate scheduler makes sure the initial value is notified immediately when the observable is subscribed. It can help your application update the user interface without having to wait for any asynchronous notifications:

    // Immediate notification of the initial value
    let disposable = observation.rx
        .observe(
            in: dbQueue,
            scheduling: .immediate) // <-
        .subscribe(
            onNext: { players: [Player] in print("fresh players: \(players)") },
            onError: { error in ... })
    // <- here "fresh players" is already printed.
    

    Note that the .immediate scheduler requires that the observable is subscribed from the main thread. It raises a fatal error otherwise.

See ValueObservation Scheduling for more information.

⚠️ ValueObservation and Data Consistency

When you compose ValueObservation observables together with the combineLatest operator, you lose all guarantees of data consistency.

Instead, compose requests together into one single ValueObservation, as below:

// 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)

See ValueObservation for more information.

DatabaseRegionObservation.rx.changes(in:)

GRDB's DatabaseRegionObservation notifies all transactions that impact a tracked database region. You can turn it into an RxSwift observable:

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

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

This observable can be created and subscribed from any thread. It delivers database connections in a "protected dispatch queue", serialized with all database updates. It only completes when a database error happens.

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."

See DatabaseRegionObservation for more information.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].