Я хочу напрямую связать UITableView с атрибутом @Published без использования DiffableDataSouce.
Если я сделаю человека
struct Person {
let name: String
}
и создадим массив данных:
@Published
var people = [Person(name: "Kim"), Person(name: "Charles")]
Итак, я хочу напрямую привязать свой UITableView, например:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return $people.count()
}
Но это дает ошибку
Cannot convert return expression of type 'Publishers.Count<Published[Person]>.Publisher>' to return type 'Int'
Это будет во вьюмодели.





Проблема здесь в том, что UITableViewDataSource основан на вытягивании (фреймворк извлекает данные из вашего кода), но издатели основаны на выталкивании (они отправляют данные во что-то). Это означает, что для того, чтобы заставить его работать, вам нужен посредник (а-ля Образец посредника.)
Один из вариантов - использовать RxSwift / RxCocoa и проект RxCombine для перевода между Combine и RxSwift и использовать функциональность там, где она уже существует. Это много для этого вопроса, но, возможно, у вас есть другие области, где RxCocoa также может оптимизировать ваш код.
Для этого вопроса вот посредник, который, я думаю, подойдет:
final class ViewController: UIViewController {
var tableView: UITableView!
@Published var people = [Person(name: "Kim"), Person(name: "Charles")]
var cancellable: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
cancellable = $people.sink(receiveValue: tableView.items { tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = item.name
return cell
})
}
}
extension UITableView {
func items<Element>(_ builder: @escaping (UITableView, IndexPath, Element) -> UITableViewCell) -> ([Element]) -> Void {
let dataSource = CombineTableViewDataSource(builder: builder)
return { items in
dataSource.pushElements(items, to: self)
}
}
}
class CombineTableViewDataSource<Element>: NSObject, UITableViewDataSource {
let build: (UITableView, IndexPath, Element) -> UITableViewCell
var elements: [Element] = []
init(builder: @escaping (UITableView, IndexPath, Element) -> UITableViewCell) {
build = builder
super.init()
}
func pushElements(_ elements: [Element], to tableView: UITableView) {
self.elements = elements
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
elements.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
build(tableView, indexPath, elements[indexPath.row])
}
}
Если люди являются атрибутом того же класса, то @Published не является подходящей оболочкой свойства. Каков ваш настоящий источник данных?