Skip to content

CombineCommunity/CombineRealm

Repository files navigation

CombineRealm

CombineRealm Logo

Version License Platform

This library is a thin wrapper around RealmSwift (Realm Docs), inspired by the RxSwift Community's RxRealm library.

Usage

Table of contents:

  1. Observing object collections
  2. Observing a single object
  3. Observing a realm instance
  4. Write transactions
  5. Delete transactions
  6. Example app

Observing object collections

CombineRealm can be used to create Publishers from objects of type Results, List, LinkingObjects or AnyRealmCollection. These types are typically used to load and observe object collections from the Realm Mobile Database.

RealmPublishers.collection(from:synchronousStart:)

Emits an event each time the collection changes:

let realm = try! Realm()
let colors = realm.objects(Color.self)

RealmPublishers.collection(from: colors)
    .map { colors in "colors: \(colors.count)" }
    .sink(receiveCompletion: { _ in
        print("Completed")
    }, receiveValue: { result in
        print(result)
    })

The above prints out "colors: X" each time a Color instance is added or removed from the database. If you set synchronousStart to true (the default value), the first element will be emitted synchronously - e.g. when you're binding UI it might not be possible for an asynchronous notification to come through.

RealmPublishers.array(from:synchronousStart:)

Upon each change fetches a snapshot of the Realm collection and converts it to an array value (for example if you want to use array methods on the collection):

let realm = try! Realm()
let colors = realm.objects(Color.self)

RealmPublishers.array(from: colors)
    .map { colors in colors.prefix(3) }
    .sink(receiveCompletion: { _ in
        print("Completed")
    }, receiveValue: { colors in
        print(colors)
    })

RealmPublishers.changeset(from:synchronousStart:)

Emits every time the collection changes and provides the exact indexes that have been deleted, inserted or updated along with the appropriate AnyRealmCollection<T> value:

let realm = try! Realm()
let colors = realm.objects(Color.self)

RealmPublishers.changeset(from: colors)
    .sink(receiveCompletion: { _ in
        print("Completed")
    }, receiveValue: { results, changes in
        if let changes = changes {
            // it's an update
            print(results)
            print("deleted: \(changes.deleted)")
            print("inserted: \(changes.inserted)")
            print("updated: \(changes.updated)")
        } else {
            // it's the initial data
            print(results)
        }
    })

RealmPublishers.arrayWithChangeset(from:synchronousStart:)

Emits every time the collection changes and provides the exact indexes that have been deleted, inserted or updated along with the Array<T> value:

let realm = try! Realm()
let colors = realm.objects(Color.self))

RealmPublishers.arrayWithChangeset(from: colors)
    .sink(receiveCompletion: { _ in
        print("Completed")
    }, receiveValue: { array, changes in
        if let changes = changes {
            // it's an update
            print(array)
            print("deleted: \(changes.deleted)")
            print("inserted: \(changes.inserted)")
            print("updated: \(changes.updated)")
        } else {
            // it's the initial data
            print(array)
        }
    })

Observing a single object

RealmPublishers.from(object:emitInitialValue:properties:)

Emits every time any of the properties of the observed object change.

It will by default emit the object's initial state as its first value. You can disable this behavior by using the emitInitialValue parameter and setting it to false.

RealmPublishers.from(object: color)
    .sink(receiveCompletion: { _ in
        print("Completed")
    }) { color in
        print(color)
    }

You can set which property changes you'd like to observe:

Observable.from(object: ticker, properties: ["red", "green", "blue"])

Observing a realm instance

RealmPublishers.from(realm:)

Emits every time the realm changes: any create & update & delete operation happens in it. It provides the realm instance along with the realm change notification.

let realm = try! Realm()

RealmPublishers.from(realm: realm)
    .sink(receiveCompletion: { _ in
        print("Completed")
    }) { realm, notification in
        print("Something happened!")
    }

Write transactions

addToRealm()

Writes object(s) to the default Realm: Realm(configuration: .defaultConfiguration).

let realm = try! Realm()
let colors = realm.objects(Color.self))

RealmPublishers.array(from: colors)
  .addToRealm()

addToRealm(configuration:updatePolicy:onError:)

Writes object(s) to a custom Realm. If you want to switch threads and not use the default Realm, provide a Realm.Configuration. You an also provide an error handler for the observer to be called if either creating the realm reference or the write transaction raise an error:

NOTE: All 3 arguments are optional, check the function definition for the default values

let realm = try! Realm()
let colors = realm.objects(Color.self))

RealmPublishers.array(from: colors)
  .addToRealm(configuration: .defaultCOnfiguration, updatePolicy: .error, onError: {
      print($0)
  })

Delete transactions

deleteFromRealm()

Deletes object(s) from the object(s)'s realm:

let realm = try! Realm()
let colors = realm.objects(Color.self))

RealmPublishers.array(from: colors)
  .deleteFromRealm()

deleteFromRealm(onError:)

Deletes object(s) from the object(s)'s realm. You an also provide an error handler for the observer to be called if either creating the realm reference or the write transaction raise an error:

let realm = try! Realm()
let colors = realm.objects(Color.self))

RealmPublishers.array(from: colors)
  .deleteFromRealm(onError: {
      print($0)
  })

Example app

To run the example project, clone the repo, navigate to the Example folder and open the Example.xcodeproj file.

To ensure that you're using the latest version of CombineRealm, in Xcode select Update to Latest Package Versions in the File/Swift Packages/Add Package Dependency... menu.

The app uses CombineRealm to observe changes in and write to Realm.

Testing

To inspect the library's Unit tests, check out the files in Tests/CombineRealmTests. To run the tests, go to the root directory of the repo and run the command:

swift test

Installation

CocoaPods

Add the following line to your Podfile and run pod install:

pod 'Combine-Realm'

Since import statements in Xcode can't contain dashes, the correct way to import the library is:

import Combine_Realm

Swift Package Manager

  • In Xcode select File/Swift Packages/Add Package Dependency...
  • Paste https://github.com/istvan-kreisz/CombineRealm.git into the repository URL textfield.

Future ideas

  • Add CI tests
  • Add Carthage support
  • Your ideas?

Author

Istvan Kreisz

[email protected]

@IKreisz

License

CombineReachability is available under the MIT license. See the LICENSE file for more info.