Code Monkey home page Code Monkey logo

rxrealm's Introduction

RxRealm

Carthage Compatible Version Swift Package Manager compatible License Platform

This library is a thin wrapper around RealmSwift ( Realm Docs ).

Table of contents:

  1. Observing object collections
  2. Observing a single object
  3. Write transactions
  4. Automatically binding table and collection views
  5. Example app

Observing object collections

RxRealm can be used to create Observables 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.

Observable.collection(from:synchronousStart:)

Emits an event each time the collection changes:

let realm = try! Realm()
let laps = realm.objects(Lap.self)

Observable.collection(from: laps)
  .map { 
    laps in "\(laps.count) laps"
  }
  .subscribe(onNext: { text  in
    print(text)
  })

The above prints out "X laps" each time a lap 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.

Observable.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 laps = realm.objects(Lap.self)

Observable.array(from: laps)
  .map { array in
    return array.prefix(3) //slice of first 3 items
  }
  .subscribe(onNext: { text  in
    print(text)
  })
Observable.changeset(from:synchronousStart:)

Emits every time the collection changes and provides the exact indexes that has been deleted, inserted or updated:

let realm = try! Realm()
let laps = realm.objects(Lap.self)

Observable.changeset(from: laps)
  .subscribe(onNext: { 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)
    }
  })
Observable.arrayWithChangeset(from:synchronousStart:)

Combines the result of Observable.array(from:) and Observable.changeset(from:) returning an Observable<Array<T>, RealmChangeset?>

let realm = try! Realm()
let laps = realm.objects(Lap.self))

Observable.arrayWithChangeset(from: laps)
  .subscribe(onNext: { array, changes in
    if let changes = changes {
    // it's an update
    print(array.first)
    print("deleted: \(changes.deleted)")
    print("inserted: \(changes.inserted)")
    print("updated: \(changes.updated)")
  } else {
    // it's the initial data
    print(array)
  }
  })

Observing a single object

There's a separate API to make it easier to observe a single object:

Observable.from(object: ticker)
    .map { ticker -> String in
        return "\(ticker.ticks) ticks"
    }
    .bindTo(footer.rx.text)

This API uses the Realm object notifications under the hood to listen for changes.

This method will by default emit the object initial state as its first next event. You can disable this behavior by using the emitInitialValue parameter and setting it to false.

Finally you can set changes to which properties constitute an object change you'd like to observe for:

Observable.from(object: ticker, properties: ["name", "id", "family"]) ...

Write transactions

rx.add()

Writing objects to existing realm reference. You can add newly created objects to a Realm that you already have initialized:

let realm = try! Realm()
let messages = [Message("hello"), Message("world")]

Observable.from(messages)
  .subscribe(realm.rx.add())

Be careful, this will retain your Realm until the Observable completes or errors out.

Realm.rx.add()

Writing to the default Realm. You can leave it to RxRealm to grab the default Realm on any thread your subscribe and write objects to it:

let messages = [Message("hello"), Message("world")]

Observable.from(messages)
  .subscribe(Realm.rx.add())
Realm.rx.add(configuration:)

Writing 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:

var config = Realm.Configuration()
/* custom configuration settings */

let messages = [Message("hello"), Message("world")]
Observable.from(messages)
  .observeOn( /* you can switch threads here */ )     
  .subscribe(Realm.rx.add(configuration: config, onError: {elements, error in
    if let elements = elements {
      print("Error \(error.localizedDescription) while saving objects \(String(describing: elements))")
    } else {
      print("Error \(error.localizedDescription) while opening realm.")
    }
  }))

If you want to create a Realm on a different thread manually, allowing you to handle errors, you can do that too:

let messages = [Message("hello"), Message("world")]

Observable.from(messages)
  .observeOn( /* you can switch threads here */ )
  .subscribe(onNext: {messages in
    let realm = try! Realm()
    try! realm.write {
      realm.add(messages)
    }
  })
rx.delete()

Deleting object(s) from an existing realm reference:

let realm = try! Realm()
let messages = realm.objects(Message.self)
Observable.from(messages)
  .subscribe(realm.rx.delete())

Be careful, this will retain your realm until the Observable completes or errors out.

Realm.rx.delete()

Deleting from the object's realm automatically. You can leave it to RxRealm to grab the Realm from the first object and use it:

Observable.from(someCollectionOfPersistedObjects)
  .subscribe(Realm.rx.delete())

Automatically binding table and collection views

RxRealm does not depend on UIKit/Cocoa and it doesn't provide built-in way to bind Realm collections to UI components.

a) Non-animated binding

You can use the built-in RxCocoa bindTo(_:) method, which will automatically drive your table view from your Realm results:

Observable.from( [Realm collection] )
  .bindTo(tableView.rx.items) {tv, ip, element in
    let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
    cell.textLabel?.text = element.text
    return cell
  }
  .addDisposableTo(bag)

b) Animated binding with RxRealmDataSources

The separate library RxRealmDataSources mimics the default data sources library behavior for RxSwift.

RxRealmDataSources allows you to bind an observable collection of Realm objects directly to a table or collection view:

// create data source
let dataSource = RxTableViewRealmDataSource<Lap>(
  cellIdentifier: "Cell", cellType: PersonCell.self) {cell, ip, lap in
    cell.customLabel.text = "\(ip.row). \(lap.text)"
}

// RxRealm to get Observable<Results>
let realm = try! Realm()
let lapsList = realm.objects(Timer.self).first!.laps
let laps = Observable.changeset(from: lapsList)

// bind to table view
laps
  .bindTo(tableView.rx.realmChanges(dataSource))
  .addDisposableTo(bag)

The data source will reflect all changes via animations to the table view:

RxRealm animated changes

If you want to learn more about the features beyond animating changes, check the RxRealmDataSources README.

Example app

To run the example project, clone the repo, and run pod install from the Example directory first. The app uses RxSwift, RxCocoa using RealmSwift, RxRealm to observe Results from Realm.

Further you're welcome to peak into the RxRealmTests folder of the example app, which features the library's unit tests.

Installation

This library depends on both RxSwift and RealmSwift 1.0+.

CocoaPods

RxRealm requires CocoaPods 1.1.x or higher.

RxRealm is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "RxRealm"

Carthage

To integrate RxRealm into your Xcode project using Carthage, specify it in your Cartfile:

github "RxSwiftCommunity/RxRealm"

Run carthage update to build the framework and drag the built RxRealm.framework into your Xcode project.

Swift Package Manager

In your Package.swift:

let package = Package(
  name: "Example",
  dependencies: [
    .package(url: "https://github.com/RxSwiftCommunity/RxRealm.git", from: "1.0.1")
  ],
  targets: [
    .target(name: "Example", dependencies: ["RxRealm"])
  ]
)

TODO

  • Test add platforms and add compatibility for the pod

License

This library belongs to RxSwiftCommunity. Maintainer is Marin Todorov.

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

rxrealm's People

Contributors

arshiacont avatar ast3150 avatar atlas-rcacheaux avatar catelina777 avatar ewerx avatar fpillet avatar freak4pc avatar icanzilb avatar ikesyo avatar insidegui avatar jhoogstraat avatar joematt avatar lihao6485 avatar lluisgerard avatar loupehope avatar m0rtymerr avatar marcusficner avatar marcuswu0814 avatar moogle19 avatar mrackwitz avatar philippecuvillier avatar rynecheow avatar sendyhalim avatar serejahh avatar sergdort avatar toshi0383 avatar trmquang93 avatar tzm41 avatar ushu avatar wanbok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rxrealm's Issues

Observable.from broken ?

I may have missed something, but it looks like Observable.from is broken: instead of calling the RxRealm method, it is RxSwift's "standard" Observable.from that is called.

To check this bug in action, just open the example app and look at the section

        Observable.from(laps)
            .map {results in "laps: \(results.count)"}
            .subscribe { event in
                self.title = event.element
            }
            .addDisposableTo(bag)

if you insert an onComplete callback in the subscribe block, it will be called immediately.

Also, if you change the name of Observable.from to something else that doesn't
conflict with RxSwift (e.g. Observable.foo then it works as expected.

Observing single object Error

Hey!
I'm getting an error while observing a single object:

Here's my object

class Album : Object{
    dynamic var id = 0
    dynamic var title = ""
    dynamic var artist = ""
    override static func primaryKey() -> String? {
        return "id"
    }
}

Here's the way I call it:

let realm = try! Realm()
  try! realm.write({
      realm.add(album, update: true)
  })
  return Observable.from(album)

There's a EXC_BAD_ACCESS line 344 in RxRealm.swift. type(of:object) fails

Any idea?
Thanks

watchOS support in Podfile

It'd be great to have watchOS support in for Cocoapods. I would do a pull request for it, but since you'll probably want to schedule it with a release, I thought it easiest just to ask here. But here's the line for you to copy pasta if you want!

s.watchos.deployment_target = '2.0'

(Thanks BTW - lovely little library this - helping me test out RxSwift and Realm in a new app!)

[bug] change gets swollen when using synchronous first element

As described here:
realm/realm-swift#4761 (comment)

Demo code:

class Test: Object {
    dynamic var name = ""
    override var description: String {
        return "[name => '\(name)']"
    }
}

class ViewController: UIViewController {

    private let bag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        test()
    }

    var token: NotificationToken?

    func test() {
        let realm = try! Realm()
        let obj = Test()

        try! realm.write {
            realm.deleteAll()
            realm.add(obj)
        }

        let tests = realm.objects(Test.self)

        DispatchQueue.global(qos: .background).async {
            let realm = try! Realm()
            let obj = realm.objects(Test.self).first!

            try! realm.write {
                obj.name = "Test0"
            }
        }

        Observable.array(from: tests)
            .subscribe(onNext: { value in
                print("rx next: \(flatten(value))")
            })
            .addDisposableTo(bag)

        token = tests.addNotificationBlock { changes in
            switch changes {
            case .initial(let tests):
                print("realm initial: \(flatten(tests))")
            case .update(let tests, _, _, _):
                print("realm changed: \(flatten(tests))")
            default: break
            }
        }

    }

}

'carthage update' results in failed build

A Cartfile containing just:
github "RxSwiftCommunity/RxRealm"
Results in a failure when building:

	Task failed with exit code 65:
	/usr/bin/xcrun xcodebuild -project /Users/user/Desktop/Carthage/Carthage/Checkouts/RxRealm/RxRealm.xcodeproj -scheme RxRealm-tvOS -configuration Release -derivedDataPath /Users/user/Library/Caches/org.carthage.CarthageKit/DerivedData/8.3.3_8E3004b/RxRealm/0.7.1 -sdk appletvos ONLY_ACTIVE_ARCH=NO BITCODE_GENERATION_MODE=bitcode CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES build (launched in /Users/user/Desktop/Carthage/Carthage/Checkouts/RxRealm)

This usually indicates that project itself failed to compile. Please check the xcodebuild log for more details: /var/folders/6s/nws8ng4d4wl4mr6jgt_kvzvr0000gn/T/carthage-xcodebuild.Uq3LIJ.log
[carthage-xcodebuild.Uq3LIJ.log]

Logfile: (https://github.com/RxSwiftCommunity/RxRealm/files/1341213/carthage-xcodebuild.Uq3LIJ.log)

XCode CLI tools version: 8.3.3

RLMException in case of binding Observable.arrayFrom() to tableView, RxRealm 0.4.0

Hi, I found problem when using UITableView+Rx extensions or default UITableViewDataSource with Observable.arrayFrom() extension. If we are scrolling tableView, after write transaction to Realm app will crash with exception. Thanks in advance for recommendations and help.

Exception
*** Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'

ViewModel

import Foundation
import RxSwift
import RxCocoa
import RealmSwift
import RxRealm
import Moya
import Moya_ObjectMapper
import RxSwiftUtilities

struct BrandsViewModel {
    
    let bag = DisposeBag()
    let realm = try! Realm()
    let provider = Provider()
    let activityIndicator = ActivityIndicator()
    
    let results: Results<Brand>
    let changeset: Observable<(AnyRealmCollection<Brand>, RealmChangeset?)>
    let brands: Observable<[Brand]>
    let title: Observable<String>
    let state: Observable<State>
    
    
    init() {
        results = realm.objects(Brand.self)
        changeset = Observable.changesetFrom(results)
        brands = Observable.arrayFrom(results)
        
        title = Observable.from(results)
            .map {
                $0.count > 0 ? "Brands (\($0.count))" : "Brands"
            }
        
        state = Observable.combineLatest(brands, activityIndicator.asObservable()) {
            $0.isEmpty ? ($1 ? .loading : .empty) : .content
        }
        
    }
    
    func fetchData() {
        provider.request(.brands)
            .trackActivity(activityIndicator)
            .mapArray(Brand.self)
            .subscribe { event -> Void in
                switch event {
                case .next(let brands):
                    try! self.realm.write {
                        self.realm.delete(self.realm.objects(Brand.self))
                        self.realm.add(brands)
                    }
                case .error(let error):
                    print(error)
                default:
                    break
                }
            }
            .addDisposableTo(bag)
    }
    
}

ViewController, UITableView+Rx -> RLMException

import UIKit
import RxSwift
import RxCocoa

class BrandsViewController: UIViewController {

    @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
    @IBOutlet weak var tableView: UITableView!
    
    let bag = DisposeBag()
    let viewModel = BrandsViewModel()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureTableView()
        bindViewModel()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        viewModel.fetchData()
        
        if let indexPath = tableView.indexPathForSelectedRow {
            tableView.deselectRow(at: indexPath, animated: true)
        }
    }
    
}

extension BrandsViewController {
    
    func configureTableView() {
        tableView.register(BrandsTableViewCell.self)
        tableView.contentInset = UIEdgeInsets(top: 63.5, left: 0, bottom: 48.5, right: 0)
        tableView.sectionHeaderHeight = 32
        tableView.rowHeight = 44
    }
    
    func bindViewModel() {
        viewModel.title
            .bindTo(navigationItem.rx.title)
            .addDisposableTo(bag)
        
        viewModel.state
            .map {
                $0 == .loading
            }
            .bindTo(activityIndicatorView.rx.isAnimating)
            .addDisposableTo(bag)
        
        viewModel.state
            .map {
                $0 != .content
            }
            .bindTo(tableView.rx.isHidden)
            .addDisposableTo(bag)
        
        viewModel.brands
            .bindTo(tableView.rx.items) { (tableView, row, brand) in
                let cell: BrandsTableViewCell = tableView.dequeueReusableCell()
                return cell.configure(with: brand.name)
            }
            .addDisposableTo(bag)
     }
}

ViewController, UITableViewDataSource โ€“> RLMException

var brands: [Brand] = []

...

func bindViewModel() {
        
        ...
        
        viewModel.brands
            .subscribe(onNext: { [unowned self] brands in
                self.brands = brands
                self.tableView.reloadData()
            })
            .addDisposableTo(bag)
}

extension BrandsViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return brands.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: BrandsTableViewCell = tableView.dequeueReusableCell()
        return cell.configure(with: brands[indexPath.row].name)
    }

}

ViewController, UITableViewDataSource and RealmChangeset โ€“> RLMException

func bindViewModel() {
        
        ...
        
        viewModel.changeset
            .subscribe(onNext: { [unowned self] brands in
                self.tableView.reloadData()
            })
            .addDisposableTo(bag)
}

extension BrandsViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.results.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: BrandsTableViewCell = tableView.dequeueReusableCell()
        return cell.configure(with: viewModel.results[indexPath.row].name)
    }

}

General question

Hello, I hope it is OK to ask a question here.
I'm experimenting with RxRealm and in a ViewController, Iโ€™m trying to check if a RealmDB hasnโ€™t been populated yet and if not, populate it with some seed values.

    class CategoriesVC: UIViewController {
      let uiRealm = try! Realm()
      let disposeBag = DisposeBag()

      override func viewDidLoad() {
        super.viewDidLoad()
        seedCategories()
      }

      func seedCategories() {
        let stopSeeding = uiRealm.objects(Category)
          .asObservableArray()
          .map { $0.count > 4 }
          .filter { $0 == true }

        let categorySeeds = ["A", "B", "C", "D", "E"]
          .map { Category(name: $0) }
          .toObservable()

        categorySeeds
          .takeUntil(stopSeeding)
          .doOnNext({ a in
            print(("Create \(a.name)"))
          })
          .subscribe(Realm.rx_add())
          .addDisposableTo(disposeBag)
      }
    }

    class Category: Object {
      dynamic var name = ""

      convenience init(name: String) {
        self.init()
        self.name = name
      }
    }

Q1: I'm having trouble understanding why this is not working ? New objects are inserted at each viewDidLoad.

Q2: the documentation says โ€œYou can add newly created objects to a realm that you already have initializedโ€ but thereโ€™s a compiler error if I replace .subscribe(Realm.rx_add()) with .subscribe(uiRealm.rx_add()) ?

What is the best way to deal with pagination?

Hi, I'm just wondering, what is the best way to deal with pagination?

In this example we are getting all of the laps from Realm DB and then binding it to table

// create data source
let dataSource = RxTableViewRealmDataSource<Lap>(
  cellIdentifier: "Cell", cellType: PersonCell.self) {cell, ip, lap in
    cell.customLabel.text = "\(ip.row). \(lap.text)"
}

// RxRealm to get Observable<Results>
let realm = try! Realm()
let lapsList = realm.objects(Timer.self).first!.laps
let laps = Observable.changeset(from: lapsList)

// bind to table view
laps
  .bindTo(tableView.rx.realmChanges(dataSource))
  .addDisposableTo(bag)

What is the best way if I want to show only 20 first or last records and load older/newer records with pull to refresh or by scrolling to end begin/end of the table? Should I manually handle loading of paginatable data "chunk" and then manually merge this data with table data source? Or maybe there is another better way to achive such behaviour?

TableView crash when Insert a value into a realm database.

Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (13) must be equal to the number of rows contained in that section before the update (13), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).

My Code:-
Datasource contains the realm results.

     Observable.changeset(from: self.datasource)
        .subscribe(onNext: {[unowned self] results, changes in
            if let changes = changes {
                self.contactsTableView.applyChangeset(changes)
            } else {
                self.contactsTableView.reloadData()
            }
        })
        .addDisposableTo(self.disposeBag)

   extension UITableView {
     func applyChangeset(_ changes: RealmChangeset) {
    
    beginUpdates()
    deleteRows(at: changes.deleted.map { IndexPath(row: $0, section: 0) }, with: .automatic)
    insertRows(at: changes.inserted.map { IndexPath(row: $0, section: 0) }, with: .automatic)
    reloadRows(at: changes.updated.map { IndexPath(row: $0, section: 0) }, with: .automatic)
    endUpdates()
  }
}

Error during Carthage build

Building using the Cartfile entry github "RxSwiftCommunity/RxRealm" ~> 1.0 gives the error "Dependency "RxRealm" has no shared framework schemes for any of the platforms: iOS"

Removing the platform specifier does not resolve the error - it just removes the platform identifier.

If I attempt to use the master branch, I get the error "module compiled with Swift 2.3 cannot be imported in Swift 3.0"

I'm almost certainly doing something wrong. Any ideas? Xcode 8, El Capitan

unused scheduler in Observable.changesetFrom

RxRealm 0.4.0 via CocoaPods

changesetFrom takes a scheduler as an optional argument that is never used locally:

public static func changesetFrom(_ collection: E, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<(AnyRealmCollection<E.ElementType>, RealmChangeset?)> {...}

Delay on Observable.from?

Is it just me or is there a big delay when using observables from realm objects?

Using them to try to bind to labels etc leaves me with a flash of the original storyboard value whenever a view appears - or worse if you use a UIPageController, the scrolling stops the main run loop from executing so the labels won't update until you finish scrolling to the new page.

Is there any way around this?

Unable to build RxRealm using Carthage (Swift 2.3 target)

No errors reports by Carthage - just reports a failed build. Using Swift 2.3 toolchain in order to integrate it into a Xcode 8 project. I can't see anything in the code that should cause a problem so the problem might lie elsewhere. Any ideas?

Add bindable sink for write transactions

Create a bindable sink for when the user needs to save objects to the realm. E.g.:

    ... blah fetch json ... map to objects ...
            .subscribeNext {objects in
                let realm = try! Realm()
                try! realm.write {
                    realm.add(objects, update: true)
                }
            }

Not building using Cocoapods with freshest versions of RxSwift and Realm (Swift 3, XCode 8)

Hi. I'm trying to bulid a sample project with RxSwift, RxCocoa, RealmSwift and RxRealm. However, RxRealm is building with lots of errors, even if I switch to swift3 branch. Other pods do not seem to produce any errors. Here is my podfile. Would be very grateful to get any help on this issue.

# Uncomment this line to define a global platform for your project
platform :ios, '9.0'

target โ€˜podtestxcode8โ€™ do
  # Comment this line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for podtestxcode8
  pod 'RxSwift', '~> 3.0.0-beta.1'
  pod 'RxCocoa', '~> 3.0.0-beta.1'
  pod 'Realm', git: 'https://github.com/realm/realm-cocoa.git', branch: 'master', submodules: true
  pod 'RealmSwift', git: 'https://github.com/realm/realm-cocoa.git', branch: 'master', submodules: true
  pod 'RxRealm', git: 'https://github.com/RxSwiftCommunity/RxRealm.git', branch: 'swift3', submodules: true

  target 'podtestxcode8Tests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'podtestxcode8UITests' do
    inherit! :search_paths
    # Pods for testing
  end

end

post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['SWIFT_VERSION'] = '3.0'
            config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.10'
        end
    end
end

screen shot 2016-09-17 at 6 29 48 pm

Observable from optional (not working?)

Hi.

I can not get my observable with binding to work.

My current usage is as follows:

Querying:

func rx_profile(_ userId: String) -> Observable<Profile?> {
    let realm = try! Realm()
    return Observable.from(optional: realm.object(ofType: Profile.self, forPrimaryKey: userId))
}

Using:

rx_profile()
      .map({ $0?.firstName })
      .observeOn(MainScheduler.asyncInstance)
      .bind(to: label.rx.text)
      .addDisposableTo(_bag)

Any consequent updates (realm.write { realm.add(profile, update: true) }) will not update the created binding.

What am I doing wrong? Or this not intended to work?

Swift 4 support

It's early days still, of course, but are there any plans to rewrite the library to support Swift 4?

How to observe the List if its Realm query prerequisite requirement has been changed?

Here is the structure of my database:

class Account: Object {
    dynamic var id = ""
    dynamic var username = ""

    let avatars = List<Avatar>()
    
    override static func primaryKey() -> String? {
        return "id"
    }
}

class Avatar: Object {
    dynamic var id = ""
    dynamic var name = ""
    
    let followingUsers = List<ChatTarget>()
    
    override static func primaryKey() -> String? {
        return "id"
    }
}

class ChatTarget: Object {
    dynamic var id = ""
    dynamic var username = ""
    
    let messages = List<MessageData>()
    
    override static func primaryKey() -> String? {
        return "id"
    }
}

Every user has an Account, and they can create many avatars in the Account. However, only one Avatar is active at the same time.

In my app, there is a tableView to show the current avatar's following users.
And this is simply achieved by binding the search result to the tableView, i.e.

// currentAvatarID will be changed when users changing their current avatar 
Observable.from( realm.objects(Avatar.self).filter("id = '\(currentAvatarID)'").first?.followingUsers)
  .bindTo(tableView.rx.items) {tv, ip, followingUser in
    let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
    cell.textLabel?.text = followingUser.username
    return cell
  }
  .addDisposableTo(bag)

The problem is, as user can have multiple avatars in their account (account.avatars). And there will be only one avatar being active at the same time (found by the property currentAvatarID).
I'll assign new value to the currentAvatarID after users change their avatar.

What I want to achieve is the tableView will automatically updates the followingUsers of the current using avatar. Even after the user changes his avatar, the tableView can show the new followingUsers for the new currentAvatar.

However, the way I did now can only observe the followingUsers of the initial avatar...

How can I achieve this in RxSwift way? I'll be very appreciated if anyone can help. Many thanks.

rx.observe unmanaged object appears broken

When I attempt to observe single properties on an unmanaged object now and then later create a realm I get back an error about duplicate properties in the schema for that object. This was working previously so i'm not sure what changed ?

  • Observing properties for an object that is already managed does not throw the error but this is not what I need.
  • I have tried to remove the observe before creating the realm.
  • the crash happens before I even attempt to add the object to the realm.

Error building RxRealm with Swift 3.

I'm experiencing 29 Swift 3 related compiler errors when attempting to include RxRealm via Cocoapods. What's the correct way to install the library?

screen shot 2017-01-17 at 16 45 46

Support LinkingObjects

LinkingObjects is a full fledged RealmCollectionType in Realm 0.100 - add support to RxRealm too

Support Collection Changes Notifications

After 3 years of poverty and dispair, Realm Finally added Collection Changes Notifications. Basically they can observe weather a collection has inserted, updated, changed, moved etc...

You can find more information here : https://realm.io/docs/swift/latest/#notifications

let myDog = Dog()
        myDog.name = "Rex"
        myDog.age = 1

        realm.add(myDog)


       notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
      guard let tableView = self?.tableView else { return }
      switch changes {
      case .Initial:
        // Results are now populated and can be accessed without blocking the UI
        tableView.reloadData()
        break
      case .Update(_, let deletions, let insertions, let modifications):
        // Query results have changed, so apply them to the UITableView
        tableView.beginUpdates()
        tableView.insertRowsAtIndexPaths(insertions.map { NSIndexPath(forRow: $0, inSection: 0) },
          withRowAnimation: .Automatic)
        tableView.deleteRowsAtIndexPaths(deletions.map { NSIndexPath(forRow: $0, inSection: 0) },
          withRowAnimation: .Automatic)
        tableView.reloadRowsAtIndexPaths(modifications.map { NSIndexPath(forRow: $0, inSection: 0) },
          withRowAnimation: .Automatic)
        tableView.endUpdates()
        break
      case .Error(let error):
        // An error occurred while opening the Realm file on the background worker thread
        fatalError("\(error)")
        break
      }
    }

Question: Drawbacks of using `defer` for situations like this:

I'm asking because I am more curious than suggesting a real change be made here.

https://github.com/RxSwiftCommunity/RxRealm/blob/master/Pod/Classes/RxRealm.swift#L116-L118

    return Observable.create {observer in
            if synchronousStart {
                observer.onNext(collection)
            }

            let token = collection.addNotificationBlock {changeset in

                let value: E

                switch changeset {
                    case .initial(let latestValue):
                        guard !synchronousStart else { return }
                        value = latestValue

                    case .update(let latestValue, _, _, _):
                        value = latestValue

                    case .error(let error):
                        observer.onError(error)
                        return
                }

                observer.onNext(value)
            }

            return Disposables.create {
                token.stop()
                observer.onCompleted()
            }
        }
      return Observable.create {observer in
            let token = collection.addNotificationBlock {changeset in

                let value: E

                switch changeset {
                    case .initial(let latestValue):
                        guard !synchronousStart else { return }
                        value = latestValue

                    case .update(let latestValue, _, _, _):
                        value = latestValue

                    case .error(let error):
                        observer.onError(error)
                        return
                }

                observer.onNext(value)
            }
             
            // does this make sense?
            defer{
                if synchronousStart {
                   observer.onNext(collection)
                }
            }
            return Disposables.create {
                token.stop()
                observer.onCompleted()
            }
        }

Consider support for addProgressNotification

Hi,

thanks for a great framework!

I was wondering if any thought had been given to adding support for progress notification via addProgressNotification

If you have any ideas please post them and I'll consider a [WIP] PR.

thanks,

Daniel

Deleting row => asObservableChangeset

Hello,

I have a strange behaviour, I have a observer on for asObservableChangeset :

realm.objects(Message).asObservableChangeset()
   .subscribeNext {result, changes in
         if let changes = changes {
             print("[๐Ÿต๐Ÿน] deleted: \(changes.deleted) inserted: \(changes.inserted) updated: \(changes.updated)")
         } else {
             print("get results result :\(result.count)")
         }
}.addDisposableTo(self.disposeBag)

when I deleted a row, I got a deleted change offset but also an inserted offset.
Also the index is always = 0
Is it normal to receive a inserted index, when I delete a row ?

RxCoreData demo

I can not update UI when using RxCoreData modify model

Through your project Can you tell me how to use RxCoreData

thank you very much

Update Documentation for Swift 3 API

Update Documentation for Swift 3 API

The new API is all about a static extension method on Observable

Can we update the docs? Let me know if you'd like me to help!

RxRealm Filter object

Hello,
im use rxRealm whit rxSwift in table view .
Initial im load all entries in realm class to a variable of type results and them populate the view.
But when i filter the variable whit the realm results, the table is not reload whit the new data from variable.
there is my class:

import Foundation
import UIKit
#if !RX_NO_MODULE
     import RxSwift
     import RxCocoa
     import RxRealm
#endif
import RealmSwift
import JLRoutes
import AlamofireImage

class MIContentTableViewController: UIViewController, UITableViewDelegate {
     private var disposeBag = DisposeBag()
     @IBOutlet weak var tableView: UITableView!
     let kCellIdentifier = "MIContentTableViewCell"
     var objectRealm: Results<Beacon_getActive>?
     var canUpdate: Bool = false

     override func viewDidLoad() {
          super.viewDidLoad()

          self.title = "Home test RxSwift"

          let buttonBar = UIBarButtonItem(title: "test", style: .Plain, target: self, action:nil)
          self.navigationItem.rightBarButtonItem = buttonBar

          objectRealm = realm.objects(Beacon_getActive).sorted("name", ascending: false)


          self.tableView.registerNib(UINib(nibName: "MIContentTableViewCell", bundle: nil), forCellReuseIdentifier: self.kCellIdentifier)
          self.tableView.estimatedRowHeight = 386


          _ = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: #selector(MIContentTableViewController.update), userInfo: nil, repeats: false)

          tableView.rx_setDelegate(self).addDisposableTo(disposeBag)

          objectRealm!.asObservableArray()
               .bindTo(tableView.rx_itemsWithCellIdentifier(kCellIdentifier, cellType: MIContentTableViewCell.self)) { (row, element, cell) in
                    let disposeBag  = DisposeBag()
                    cell.disposeBagCell = disposeBag
                    cell.title.text = element.name
                    cell.imageField.image = UIImage()

                    cell.imageField.af_setImageWithURL(NSURL(string: "https://unsplash.it/1024/1024/?gravity=east&random&q=\(self.randomStringWithLength(3) as String)")!, placeholderImage: UIImage(), filter: nil, progress: nil, imageTransition: .CrossDissolve(0.2), runImageTransitionIfCached: true, completion: { (response) in
                         cell.imageField.image = response.result.value
                    })
                    cell.button.rx_tap.subscribeNext {
                         if let url: NSURL = NSURL(string: "smiity://terms") {
                              JLRoutes.routeURL(url)
                         }
                         }.addDisposableTo(disposeBag)

                    if row == (self.objectRealm?.count)!-1 {
                         self.canUpdate = true
                    } else {
                         self.canUpdate = false
                    }
               }
               .addDisposableTo(disposeBag)

          objectRealm!.asObservableChangeset()
               .subscribeNext {[weak self] repos, changes in
                    guard let tableView = self?.tableView else { return }
                    if !self!.canUpdate {
                         if let changes = changes {
                              tableView.beginUpdates()
                              tableView.insertRowsAtIndexPaths(changes.inserted.map { NSIndexPath(forRow: $0, inSection: 0) },
                                   withRowAnimation: .Automatic)
                              tableView.deleteRowsAtIndexPaths(changes.deleted.map { NSIndexPath(forRow: $0, inSection: 0) },
                                   withRowAnimation: .Automatic)
                              tableView.reloadRowsAtIndexPaths(changes.updated.map { NSIndexPath(forRow: $0, inSection: 0) },
                                   withRowAnimation: .Automatic)
                              tableView.endUpdates()
                         } else {
                              tableView.reloadData()
                         }
                    }
               }
               .addDisposableTo(disposeBag)



          tableView
               .rx_modelSelected(Object)
               .subscribeNext { value in
                    print("tapped cell")
               }
               .addDisposableTo(disposeBag)


          buttonBar.rx_tap
               .subscribeNext { [weak self] x in
                    self?.filterDummy()
               }
               .addDisposableTo(disposeBag)

     }

     override func didReceiveMemoryWarning() {
          super.didReceiveMemoryWarning()
          // Dispose of any resources that can be recreated.
     }

     func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
          cell.selectionStyle = .None
     }

     func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
          return .None
     }


     //dummy generators
     func update() {
          try! realm.write {
               let objects = realm.objects(Beacon_getActive)
               let randomObject = objects[Int(arc4random_uniform(UInt32(objects.count) - 1))]
               realm.delete(randomObject)

               let resultEdit = objects[Int(arc4random_uniform(UInt32(objects.count) - 1))]

               resultEdit.name = self.randomStringWithLength(3) as String
               realm.add(resultEdit, update: true)

               let object = Beacon_getActive()
               object.name = self.randomStringWithLength(30) as String
               object.beacon_id = self.randomStringWithLength(8) as String
               object.isDummy = true

               realm.add(object, update: false)
          }
     }

     func randomStringWithLength (len: Int) -> NSString {

          let letters: NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

          let randomString: NSMutableString = NSMutableString(capacity: len)

          for _ in 0 ..< len {
               let length = UInt32 (letters.length)
               let rand = arc4random_uniform(length)
               randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
          }

          return randomString
     }

     func filterDummy () {
          print("filter")
          objectRealm = realm.objects(Beacon_getActive).filter(NSPredicate(format: "isDummy == %@", false))
     }
}

'Can only add notification blocks from within runloops.'

I'm getting this exception in my app, not sure how I get it to this state...
Posting an issue/question here to see if anyone has the same issue.

*** Terminating app due to uncaught exception 'RLMException', reason: 'Can only add notification blocks from within runloops.'
*** First throw call stack:
(
0 CoreFoundation 0x00000001112bad85 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x0000000110d2edeb objc_exception_throw + 48
2 Realm 0x000000010fce9627 -[RLMRealm addNotificationBlock:] + 0
3 Realm 0x000000010fcef970 -[RLMResults addNotificationBlock:] + 46
4 RealmSwift 0x00000001100ef4c3 _TFC10RealmSwift7Results20addNotificationBlockfFGOS_21RealmCollectionChangeGS0_x__T_CSo20RLMNotificationToken + 243
5 ApptioMobileSwift 0x000000010f79f962 _TTWuRxC10RealmSwift6ObjectrGCS_7Resultsx_17ApptioMobileSwift19NotificationEmitterS2_FS3_20addNotificationBlockfFGOS_21RealmCollectionChangex_T_CSo20RLMNotificationToken + 130
6 ApptioMobileSwift 0x000000010f7a43a1 TFFe17ApptioMobileSwiftRxS_19NotificationEmitterx10RealmSwift19RealmCollectionTyperS0_12asObservableFT_GC7RxSwift10Observablex_U_FGVS3_11AnyObserverQPS0__PS3_10Disposable + 1137
7 ApptioMobileSwift 0x000000010f7a059d TPA__TFFe17ApptioMobileSwiftRxS_19NotificationEmitterx10RealmSwift19RealmCollectionTyperS0_12asObservableFT_GC7RxSwift10Observablex_U_FGVS3_11AnyObserverQPS0__PS3_10Disposable + 1005

Crashes at 'bool read_only() const { return schema_mode == SchemaMode::ReadOnly; }'

I have this object:

let territory = RoadObject()

which has this LinkingObjects property:

let roadNumbers = LinkingObjects.init(fromType: RoadNumberObject.self, property: "territory")

I create at my ViewModel this property to get an array with the linking objects:

let roadNumbers: Variable<[RoadNumberObject]>

I initialize it (by creating an Array from the LinkingObjects property):

self.roadNumbers = Variable(self.territory.roadNumbers.toArray())

And I am trying to bind an Observer from the LinkingObjects so I can track the changes and access them easily through the Variable (I am using the same realm everywhere by the way, and I do not access it in this case from a thread other than the Main one) :

Observable.array(from: self.territory.roadNumbers)
			.bindTo(roadNumbers)
			.disposed(by: disposeBag)

As soon as my ViewModel is initialized it crashes. I back traced the issue and the problem arises when I try to bind. I checked it as an Observable (not a Variable) and it doesn't crash, but I would like to use the variable for convenience.

The line where it crashes is:

bool read_only() const { return schema_mode == SchemaMode::ReadOnly; }

in the Config method in the shared_realm.hpp file

It doesn't happen though with a ReplaySubject, or a PublishSubject. Only with a Variable and a BehaviorSubject

It also doesn't happen if I initialize the Variable with an empty array (Variable([])). It only happens when it is already initialized as Variable(self.territory.roadNumbers.toArray())

One solution I found is if I initialize the Variable with an empty array and on the subscription I place .startWith(self.territory.roadNumbers.toArray())

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.