sergdort / CleanArchitectureRxSwift

Example of Clean Architecture of iOS app using RxSwift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

UITableViewCell/UICollectionViewCell input example

ipaboy opened this issue · comments

Hi Guys, I'm very exited with this architecture and want to thank you. While working with it I've got some questions. Could you please provide an example for the following case: I have a cell with a textfield placed on it. How should I create an Input for controller's ViewModel in this case to make it work properly with cells reusability?

Thanks.

Hi @bryzinski,

Have a look at the AllPosts scene in this repository. It does what you're describing. Ping me if you need help in understanding anything in particular.

Cheers

Hi @nmdias thank you for your feedback. But unfortunately PostTableViewCell doesn't contain any UITextFields. To clarify I'm interested in a table view based form screen and looking for an elegant solution to pass rx streams of textfields (which are placed on cells) through view models's -transform method.

Probably the easiest way is to add Variable's to view model (like username, password) and bind them directly after cells are dequeued. But I love this transform function and still looking for the solution :)

Hey @bryzinski,

Ah! A UITextfield! Yeah, that's different. I use a Base class. Consider the following:

class BaseTableViewCell<ViewModelType>: UITableViewCell, Reusable {
    
    private var disposeBag: DisposeBag?
    
    var viewModel: ViewModelType? {
        didSet {
            guard let viewModel = viewModel else {
                return
            }
            let disposeBag = DisposeBag()
            self.configure(viewModel, disposedBy: disposeBag)
            self.disposeBag = disposeBag
        }
    }
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        configureDefaults()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func prepareForReuse() {
        super.prepareForReuse()
        self.disposeBag = nil
        self.viewModel = nil
    }
    
    func configureDefaults() {
        fatalError("Always override \(#function) to provide default view configurations.")
    }
    
    func configure(_ viewModel: ViewModelType, disposedBy disposeBag: DisposeBag) {
        fatalError("Always override \(#function) to provide view model configurations.")
    }
    
}

Now, with your own subclass, you could do this, maybe?

final class SomeTableViewCell: BaseTableViewCell<SomeTableViewCellViewModel> {
    
    let textfield: UITextField = {
        let textfield = UITextField()
        textfield.translatesAutoresizingMaskIntoConstraints = false
        // Do your thing
        return textfield
    }()
    
    override func configureDefaults() {
        // Do your thing
    }
    
    override func configure(_ viewModel: SomeTableViewCellViewModel, disposedBy disposeBag: DisposeBag) {
        // I suspect there might be a cleaner way to `drive` the subject...
        textfield.rx.text.asDriver().drive(onNext: { (string) in
            viewModel.text.on(.next(string))
        }).disposed(by: disposeBag)
    }
    
}

The cell has this corresponding ViewModelType:

struct SomeTableViewCellViewModel {
    let text = PublishSubject<String?>()
}

Now, when you instantiate your Cell's View Model, you can subscribe to it's text: PublishSubject<String?>

Something like:

// The Cell's View Model
viewModel.text.subscribe(onNext: { (string) in
    print(string)
}).disposed(by: disposeBag)

Then assign the Cell's viewModel

let cell = tableView.reusableCell() as SomeTableViewCell
cell.viewModel = viewModel
return cell

Does this help? Cheers

@nmdias thank you! It looks what I expected for.