Привязка к источнику данных с помощью RxDataSources

#swift #tableview #rx-swift #rxdatasources

Вопрос:

У меня есть список моделей, которые я получаю с сервера, и в основном я получаю массив из них:

 struct ContactModel: Codable, Equatable { static func == (lhs: ContactModel, rhs: ContactModel) -gt; Bool {  return lhs.name == rhs.name amp;amp;  lhs.avatar == rhs.avatar amp;amp;  lhs.job == rhs.job amp;amp;  lhs.follow == rhs.follow }  let id: Int let name: String let avatar:String? let job: JobModel? let follow:Bool  enum CodingKeys: String, CodingKey {  case id, name, avatar, job, follow }  

}

Поэтому я хочу показать список контактов в своем tableview .

Теперь у меня также есть эта структура, которая является оболочкой для этой модели:

 struct ContactCellModel : Equatable, IdentifiableType {    static func == (lhs: ContactCellModel, rhs: ContactCellModel) -gt; Bool {    return lhs.model == rhs.model  }    var identity: Int {  return model.id  }    var model: ContactModel  var cellIdentifier = ContactTableViewCell.identifier }  

То, что я пытаюсь сделать, — это создать источник данных с помощью RxDatasources и привязаться к нему, вот так(ContactsViewController.swift):

 let dataSource = RxTableViewSectionedAnimatedDataSourcelt;ContactsSectionModelgt;(  configureCell: { dataSource, tableView, indexPath, item in  if let cell = tableView.dequeueReusableCell(withIdentifier: item.cellIdentifier, for: indexPath) as? BaseTableViewCell{  cell.setup(data: item.model)  return cell  }    return UITableViewCell()    })  

но я не уверен, что мне следует делать сразу после этого. Я попробовал что-то вроде этого:

 Observable.combineLatest(contactsViewModel.output.contacts, self.contactViewModel.changedStatusForContact)  .map{ (allContacts, changedContact) -gt; ContactsSectionModel in    //what should I return here?  }.bind(to: dataSource)  

Я использую combineLatest , потому что у меня есть еще один наблюдаемый ( self.contactViewModel.changedStatusForContact ), который уведомляет, когда определенный контакт был изменен (это происходит, когда вы нажимаете определенную кнопку в ячейке контакта).

Итак, что я должен вернуть из .map выше, чтобы успешно привязаться к ранее созданному dataSource ?

Ответ №1:

Вы должны заменить изменившиеся контакты; все контакты, которые были изменены, но вы не можете этого сделать, потому что вы отслеживаете не все из них, а только самый последний. Так что вы не можете сделать это на карте. Вам нужно использовать scan вместо этого.

Я сделал много предположений о коде, который вы не публиковали, поэтому ниже приведен компилируемый пример. Если ваши типы отличаются, то вам придется внести некоторые изменения:

 func example(contacts: Observablelt;[ContactModel]gt;, changedStatusForContact: Observablelt;ContactModelgt;, tableView: UITableView, disposeBag: DisposeBag) {  let dataSource = RxTableViewSectionedAnimatedDataSourcelt;ContactsSectionModelgt;(  configureCell: { dataSource, tableView, indexPath, item in  if let cell = tableView.dequeueReusableCell(withIdentifier: item.cellIdentifier, for: indexPath) as? BaseTableViewCell {  cell.setup(data: item.model)  return cell  }  return UITableViewCell() // this is quite dangerious. Better would be to crash IMO.   })   let contactsSectionModels = Observable.combineLatest(contacts, changedStatusForContact) {  (original: $0, changed: $1)  }  .scan([ContactsSectionModel]()) { state, updates in  // `state` is the last array handed to the table view.  // `updates` contains the values from the combineLatest above.  var contactModels = state.flatMap { $0.items.map { $0.model } }  // get all the contactModels out of the state.  if contactModels.isEmpty {  contactModels = updates.original  }  // if there aren't any, then update with the values coming from `contacts`  else {  guard let index = contactModels  .firstIndex(where: { $0.id == updates.changed.id })  else { return state }  contactModels[index] = updates.changed  }  // otherwise find the index of the contact that changed.  // and update the array with the changed contact.  return [ContactsSectionModel(  model: "",  items: updates.original  .map { ContactCellModel(model: $0) }  )]  // rebuild the section model and return it.  }   contactsSectionModels  .bind(to: tableView.rx.items(dataSource: dataSource))  .disposed(by: disposeBag) }  typealias ContactsSectionModel = AnimatableSectionModellt;String, ContactCellModelgt;  struct ContactCellModel: Equatable, IdentifiableType {  var identity: Int { model.id }  let model: ContactModel  let cellIdentifier = ContactTableViewCell.identifier }  struct ContactModel: Equatable {  let id: Int  let name: String  let avatar: String?  let job: JobModel?  let follow: Bool }  struct JobModel: Equatable { }  class BaseTableViewCell: UITableViewCell {  func setup(data: ContactModel) { } }  class ContactTableViewCell: BaseTableViewCell {  static let identifier = "Cell" }  

Комментарии:

1. Да, ты был прав. Мне нужно было только изменить то , что указано items в операторе возврата a scan , from items: updates.original , to items: changedModels , и это сработало.