Search code examples
iosrx-swift

How to fix IBOutlet "unexpectedly found nil" error when binding data with RxSwift?


I want to bind my data of BehaviorRelay<[Data]> from my View Model class into a UITableView in my UIViewController class, but unfortunately I keep getting this error:

Unexpectedly found nil while implicitly unwrapping an Optional value: file project/ResultCell.swift, line 27 2021-04-17 15:06:32.497411+0700 project[5189:936745] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file project/ResultCell.swift, line 27

Here's what I did (In my View Controller class):

    private func setupUI() { // Called in viewDidLoad()
        resultsTv.register(ResultCell.self, forCellReuseIdentifier: ResultCell.IDENTIFIER)
    }
    
    private func setupRxBindings() { // Called in viewDidLoad()
        viewModel.results.asObservable().bind(to: resultsTv.rx.items(cellIdentifier: ResultCell.IDENTIFIER, cellType: ResultCell.self)) { row, element, cell in
            cell.configureData(with: element)
        }.disposed(by: disposeBag)
        
        let query = searchTf.rx.text.observe(on: MainScheduler.asyncInstance).distinctUntilChanged().throttle(.seconds(1), scheduler: MainScheduler.instance).map { $0 }
        query.subscribe(onNext: { [unowned self] query in
            self.viewModel.search(query ?? "") // Everytime I search something, it gives me the error
        }).disposed(by: disposeBag)
        
    }

My View Model class:

fileprivate final class SearchVM {
    var results = BehaviorRelay<[ModelData]>(value: [ModelData]())
    
    init() { }
    
    func search(_ query: String) {
        // Get the data from a server and store it in the results property
    }
}

My ResultCell.swift class:

class ResultCell: UITableViewCell {
    static let IDENTIFIER = "ResultCell"
    
    @IBOutlet weak var photoIv: UIImageView!
    @IBOutlet weak var idLbl: UILabel!
    @IBOutlet weak var nameLbl: UILabel!
    @IBOutlet weak var miscLbl: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    func configureData(with data: ModelData) {
        idLbl.text = "ID: \(data.id ?? "")" // The line that causes the error
        nameLbl.text = data.name
        miscLbl.text = "\(data.gender), \(data.height), \(data.phone)"
    }
}

In more detail, I'm making a search page that can show data based on the search results (I'm using an .xib files for both of my UIViewController and UITableViewCell files). Since I'm learning RxSwift I don't want to use any delegates and datasources for my UITableView. I'm guessing the error is because the cell was not loaded properly so the IBOutlets is not yet initialized. But I'm not sure how to solve the error. Is there anyway to solve this?


Solution

  • You have registered the cell class against your reuse identifier. This just instantiates your cell instances without any reference to your XIB file, and so the outlets are not connected.

    You need to register the XIB file against the reuse identifier.

    
    private func setupUI() { // Called in viewDidLoad()
        resultsTv.register(UINib(nibName: "yourNib", bundle: nil), forCellReuseIdentifier: ResultCell.IDENTIFIER) 
    }