Swift UIKit Combine - 如何在处理发布者事件时重新加载 tableview?

Swift UIKit Combine - How to reload tableview when handling publisher events?

使用 Combine 和 UIKit,我试图在 viewModel 中包含的数组发生变化时更新我的​​ tableview。我的tableview的数据源是分开的,因为我想重用这个文件(不同的对象都有一个名字var,所以tableviews会显示一个名字列表)。

视图模型:

class ViewModel {
  @Published var items = [ItemViewModel]()
  let service = NetworkService()

  init() {
    fetchItems()
  }
    
  func fetchItems() {
    service.fetchItems { items in {
    self.items = items.map { ItemViewModel([=11=]) }
  }
}

数据源:

class GenericDataSource: UITableViewDataSource {
   var list = [String]()

   func setList(_ list: [String]) {
       self.list = list
   }

  // then the usual tableView datasource boilerplate
}

viewController:

class ViewController: UIViewController {
  let viewModel = ViewModel()
  let tableDataSouce = GenericDataSource()
  var subscriptions = Set<AnyCancellable>()

  func setupBinding() {
    // I want to: 1) map over viewModel.$items to get [item.name]
    //            2) assign the list of names to tableDataSource.list
    //            3) reload the tableview so the new values are displayed
  }

我不确定要使用什么 Combine 函数。我当前的(工作)代码如下:

func setupBinding() {
     viewModel.$items.handleEvents(receiveOutput: { [weak self] items in
         self?.dataSource.setList(items.map { [=14=].name })
         
            // dispatch called because I get yelled at for not being on main thread
            DispatchQueue.main.async { 
                 self?.itemsTableView.reloadData()
            }
        })
           .sink { _ in }
           .store(in: &subscriptions)
 }

它有效,但我觉得我在强行使用它。有没有更简洁的方法来执行与当前设置的绑定?

是否有更简洁的方法来设置代码以便绑定更容易?

“更清洁”是主观的,但更“Combine-y”的方式是:

viewModel.$items
   .receive(on: DispatchQueue.main)
   .sink { [weak self] items in
      self?.dataSource.setList(items.map(\.name))
      self?.itemsTableView.reloadData()
   }
   .store(in: &subscriptions)

您还可以使用 .map 运算符来获取名称数组:

.map { [=11=].map(\.name) }
// ...
.sink { [weak self] names in
// ...
}

但我认为这是一个品味问题。