Hi I prepare ios developer
Sorry, because do not speak english not well
I would like to know how to implement it in general.
subscribe array data and make custom view
doTableViewModel.doItems
.observe(on: MainScheduler.instance)
.subscribe(onNext: { viewModelItems in
self.doTableView.reloadData(viewModelItems: viewModelItems)
// make and draw view
// after view create event
self.doTableView.doItemViews.forEach{ doItemView in
let item = doItemView.doItem
doItemView.rx.tapGesture()
.when(.recognized)
.subscribe(onNext: { element in
print(item.doID)
}
).disposed(by: self.disposeBag)
doItemView.checkButton.rx.tapGesture()
.when(.recognized)
.subscribe(onNext: { _ in
self.tappedDoItemCheck(viewDoItem: item)
}
).disposed(by: self.disposeBag)
doItemView.deleteButton.rx.tapGesture()
.when(.recognized)
.subscribe(onNext: { _ in
self.tappedDoItemRemove(doItem: item)
}
).disposed(by: self.disposeBag)
doItemView.editButton.rx.tapGesture()
.when(.recognized)
.subscribe(onNext: { element in
self.tappedDoItemEdit(doItem: item)
}
).disposed(by: self.disposeBag)
}
self.doTableView.configureLayer()
})
.disposed(by: disposeBag)
subscribe in subscirbe ... I don't know if this way is correct.
CodePudding user response:
This is not correct.
It has retain cycles. Do not capture
self
strongly in subscribe when self owns the disposeBag.Having subscribe calls inside a subscribe call is a code smell. Try to avoid it. (Notice that in the code below, there is only one
subscribe(onNext:)
instead of many.)
I would rather see code that looks like this:
doTableViewModel.doItems
.observe(on: MainScheduler.instance)
.flatMapLatest { [doTableView] items in
doTableView.configure(viewModelItems: items)
}
.subscribe(onNext: { [weak self] action in
guard let self else { return }
switch action {
case let .check(doItem):
self.tappedDoItemCheck(viewDoItem: doItem)
case let .edit(doItem):
self.tappedDoItemEdit(doItem: doItem)
case let .remove(doItem):
self.tappedDoItemEdit(doItem: doItem)
}
})
.disposed(by: disposeBag)
To make the above work, you will need to move the other code into DoTableView and DoItemView like this:
extension DoTableView {
enum Action {
case check(DoItem)
case remove(DoItem)
case edit(DoItem)
}
func configure(viewModelItems: [DoItem]) -> Observable<Action> {
reloadData(viewModelItems: viewModelItems)
configureLayer()
return Observable.merge(zip(doItemViews, viewModelItems).map { doItemView, doItem in
doItemView.configure()
.compactMap { viewAction in
switch viewAction {
case .print:
print(doItem.doID)
return nil
case .check:
return .check(doItem)
case .remove:
return .remove(doItem)
case .edit:
return .edit(doItem)
}
}
})
}
}
extension DoItemView {
enum Action {
case print
case check
case remove
case edit
}
func configure() -> Observable<Action> {
Observable.merge(
rx.tapGesture().when(.recognized).map { _ in Action.print },
checkButton.rx.tapGesture().when(.recognized).map { _ in Action.check },
deleteButton.rx.tapGesture().when(.recognized).map { _ in Action.remove },
editButton.rx.tapGesture().when(.recognized).map { _ in Action.edit }
)
}
}
Note that with the above code, DoItemView
does not need a doItem
property any more. That can be removed.
One problem about this that has nothing to do with RxSwift is that it assumes the DoTableView completely destroys and rebuilds its DoItemViews every time the reloadData(viewModelItems:)
method is called. That is incredibly inefficient. It would be better to use a normal UITableView or build your DoTableView so that it can reuse DoTableViews like UITableView reuses its cells.