whisper
whisper

Reputation: 143

RxSwift + UserDefaults

I am new here about RxSwift, In my case, I want to use UserDefaults with RxSwift to simplify my code, so I did this following code

my question is, when I clicked a cell, but the subscribe method submit twice? so what should I do to fix it? thx a lot!

import UIKit

import RxSwift
import RxCocoa
import RxDataSources

class ViewController: UIViewController {
    let disposeBag = DisposeBag()

    @IBOutlet weak var tableView: UITableView! {
        didSet {
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: String(describing: UITableViewCell.self))

            tableView.rx
                .itemSelected
                .subscribe { (indexPath) in
                    UserDefaults.standard.set("\(indexPath)", forKey: "key")
                }
                .disposed(by: disposeBag)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        UserDefaults.standard.rx
            .observe(String.self, "key")
            // .debug()
            .subscribe(onNext: { (value) in
                if let value = value {
                    print(value)
                }
            })
            .disposed(by: disposeBag)

        let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>()

        dataSource.configureCell = { (dataSource, tableView, indexPath, item) in
            let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UITableViewCell.self), for: indexPath)
            cell.textLabel?.text = item

            return cell
        }

        Observable.just([SectionModel(model: "", items: (0..<5).map({ "\($0)" }))])
            .bindTo(tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)
    }

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

Upvotes: 6

Views: 8057

Answers (4)

jjramos
jjramos

Reputation: 2094

If you just want to make sure it emits only once in your scenario, use distinctUntilChanged() as others have suggested. Note that if you tap on the same cell twice, your subscribe closure will only emit once.

If you want to get a better understanding of why it is emitting twice, I would check if the didSet on tableView is called twice. You could try moving this block

tableView.rx
  .itemSelected
  .subscribe { (indexPath) in
    UserDefaults.standard.set("\(indexPath)", forKey: "key")
}
.disposed(by: disposeBag)

to viewDidLoad() and see if you have the same problem.

Upvotes: 0

chatterjee86
chatterjee86

Reputation: 31

You can try using take(n) where 'n' is the number of contiguous elements from an observable sequence.

tableView.rx
  .itemSelected
  .take(1)
  .subscribe { (indexPath) in
    UserDefaults.standard.set("\(indexPath)", forKey: "key")
  }
  .disposed(by: disposeBag)

Upvotes: 0

Timofey Solonin
Timofey Solonin

Reputation: 1403

It's indeed some kind of a bug and I would recommed to use distinctUntilChanged().

Using debounce() as suggested by @wisper might work most of the time but is dangerous because you are relying on speed of events emitted by observable.

Upvotes: 6

whisper
whisper

Reputation: 143

iOS bug, v10.2

UserDefaults.standard.rx
    .observe(String.self, "key")
+   .debounce(0.1, scheduler: MainScheduler.asyncInstance)
    ...

Upvotes: 3

Related Questions