Привязка к UIRefreshControl после обновления с помощью RxSwift

У меня есть TableView уведомления. Я хочу обновить, потянув, чтобы обновить с помощью UIRefreshControl. Как это сделать с помощью rx-swift? Это мой код. Почему tableView не обновляется после того, как я установил значение для переменных данных

var refreshControl = UIRefreshControl()
var disposeBag = DisposeBag()

let loadingData = ActivityIndicator()

var data: Observable<[Notification]>!

override func viewDidLoad() {
    super.viewDidLoad()

    self.view = v

    v.tableView.registerClass(NotificationsViewCell.self, forCellReuseIdentifier: "Cell")
    v.tableView.addSubview(refreshControl)
    data = getNotifications()

    configureTableDataSource()
    configureActivityIndicatorsShow()

    refreshControl.rx_controlEvent(.ValueChanged)
        .flatMapLatest{ [unowned self] _ in
            return self.getNotifications()
            .trackActivity(self.loadingData)
        }.subscribe(
            onNext: {notification in
                print("success")
                self.data = Observable.just(notification) // NOT REFRESH TABLEVIEW
            },
            onError: { error in
                print("Error \(error)")
            },
            onCompleted: {() in
                print("complete")
            },
            onDisposed: {() in
                print("disposed")
            })
        .addDisposableTo(disposeBag)
}


func configureTableDataSource(){
    data
        .retry(3)
        .doOnError{ [weak self] error in
            self?.v.emptyLabel.hidden = false
            self?.v.retryButton.hidden = false
        }
        .doOnNext{ [weak self] result in
            if result.count == 0 {
                self?.v.emptyLabel.hidden = false
                self?.v.emptyLabel.text = "Tidak ada bisnis favorit"
            } else {
                self?.v.emptyLabel.hidden = true
                self?.v.retryButton.hidden = true
            }
        }
        .trackActivity(loadingData)
        .retryWhen{ _ in
            self.v.retryButton.rx_tap
        }
        .asDriver(onErrorJustReturn: [])
        .map{ results in
            results.map(NotificationsViewModel.init)
        }
        .drive(v.tableView.rx_itemsWithCellIdentifier("Cell", cellType: NotificationsViewCell.self)) { (index, viewModel, cell) in
            cell.viewModel = viewModel

            let tap = UITapGestureRecognizer(target: self, action: #selector(self.goToProfile(_:)))
            tap.numberOfTapsRequired = 1
            cell.photo.tag = index
            cell.photo.addGestureRecognizer(tap)
        }
        .addDisposableTo(disposeBag)
}

func configureActivityIndicatorsShow(){
    loadingData
        .driveNext{ isLoading in
            if !isLoading {
                self.v.indicatorView.stopAnimating()
            } else {
                self.v.indicatorView.startAnimating()
                self.v.retryButton.hidden = true
                self.v.emptyLabel.hidden = true
            }
        }
        .addDisposableTo(disposeBag)

    loadingData.asObservable()
        .bindTo(refreshControl.rx_refreshing)
        .addDisposableTo(disposeBag)
}

func getNotifications() -> Observable<[Notification]> {
    let parameters = [
        "token": NSUserDefaults.standardUserDefaults().objectForKey("token")! as! String
    ]
    return string(.POST, NOTIFICATION_LIST, parameters: parameters)
        .map { json in
            return Notification.parseJSON(JSON.parse(json)["notifications"])
        }
        .observeOn(MainScheduler.instance)
}

РЕДАКТИРОВАТЬ::

var data = Variable<[Notification]>([])

override func viewDidLoad() {
    getNotifications()
        .retry(3)
        .doOnError{ [weak self] error in
            self?.v.emptyLabel.hidden = false
            self?.v.retryButton.hidden = false
        }
        .doOnNext{ [weak self] result in
            if result.count == 0 {
                self?.v.emptyLabel.hidden = false
                self?.v.emptyLabel.text = "Tidak ada notifikasi"
            } else {
                self?.v.emptyLabel.hidden = true
                self?.v.retryButton.hidden = true
            }
        }
        .trackActivity(loadingData)
        .retryWhen{ _ in
            self.v.retryButton.rx_tap
        }
        .bindTo(data)
        .addDisposableTo(disposeBag)


    refreshControl.rx_controlEvent(.ValueChanged)
        .flatMapLatest{ [unowned self] _ in
            return self.getNotifications()
                .doOnError{ [weak self] error in 
                    // This not call after the second pull to refresh if No network connection, so refresh control still appear
                    self?.refreshControl.endRefreshing()  
                }
                .doOnCompleted{ [weak self] result in
                    self?.refreshControl.endRefreshing()
                }
        }.bindTo(data)
        .addDisposableTo(disposeBag)
}

func configureTableDataSource(){
    datas.asObservable()
        .asDriver(onErrorJustReturn: [])
        .map{ results in
            results.map(NotificationsViewModel.init)
        }
        .drive(v.tableView.rx_itemsWithCellIdentifier("Cell", cellType: NotificationsViewCell.self)) { (index, viewModel, cell) in
            cell.viewModel = viewModel
        }
        .addDisposableTo(disposeBag)
}


func configureActivityIndicatorsShow(){
    loadingData
        .driveNext{ isLoading in
            if !isLoading {
                self.v.indicatorView.stopAnimating()
            } else {
                self.v.indicatorView.startAnimating()
                self.v.retryButton.hidden = true
                self.v.emptyLabel.hidden = true
            }
        }
        .addDisposableTo(disposeBag)
}

person Willyanto Wijaya Sulaiman    schedule 30.05.2016    source источник


Ответы (1)


self.data = Observable.just(notification) создает новый Observable и отправляет новый элемент [Notification] на том Observable, на который никто не подписан.

Вы должны использовать Subject, например Variable.

// instead of `var data: Observable<[Notification]>!`
let data = Variable<[Notification]>([])

// and then later, when you want to send out a new element:
self.data.value = notification

РЕДАКТИРОВАТЬ: чтобы показать вам, как использовать это в сочетании с тем, что у вас уже есть.

// this will update `data` upon `refreshControl` value change
refreshControl.rx_controlEvent(.ValueChanged)
    .flatMapLatest{ [unowned self] _ in
        return self.getNotifications()
    }
    .bindTo(data)
    .addDisposableTo(disposeBag)

// this will update `loadingData` when `data` gets a new element
data.asDriver().trackActivity(self.loadingData)

// bind to your table view
data.asDriver().drive(//.....

Кроме того, подумайте о том, чтобы переместить retry и retryWhen, чтобы они происходили раньше, вместо того, чтобы происходить ниже по течению, где они у вас есть в настоящее время (в привязке представления таблицы). Вместо этого я думаю, что это должно быть в getNotifications.

person solidcell    schedule 30.05.2016
comment
Вы имеете в виду let data = Variable ‹[Уведомление]› ([])? Но как назначить данные из этого data = getNotifications (). Следует ли мне изменить тип возвращаемого значения getNotifications ()? - person Willyanto Wijaya Sulaiman; 31.05.2016
comment
Хороший улов. Я также обновил ответ, чтобы показать вам, как вы можете немного реорганизовать свой код, чтобы работать с моим предложением. - person solidcell; 31.05.2016
comment
Спасибо, это работа. Я обновил свой код. Но у меня небольшая проблема, если нет соединения, тогда я тяну, чтобы запросить tableView. Первое действие, refreshControl, будет скрыто, второе действие останется. Почему? - person Willyanto Wijaya Sulaiman; 31.05.2016