2
\$\begingroup\$

I have custom cell of tableview which is used as header as well as normal cell.

In custom cell I have this properties

public var btnReplyTappedClousre:((CommentCell) -> (Void))?
public var btnBulbTappedClousre:((CommentCell) -> (Void))?
public var btnOtherReplyTapped:((CommentCell) -> (Void))?

And Call it with

@IBAction func btnReplyTapped(_ sender: Any) {
    self.btnReplyTappedClousre?(self)
}

//--------------------------------------------------------------------------------

@IBAction func btnBulbTapped(_ sender: Any) {
    self.btnBulbTappedClousre?(self)
}

//--------------------------------------------------------------------------------

@IBAction func btnViewPreviousReplyTapped(_ sender: Any) {
    self.btnOtherReplyTapped?(self)
}

In view Controller i have implemented that closure

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell =  tableView.dequeueReusableCell(withIdentifier: StoryBoard.Cells.CommentCell, for: indexPath) as! CommentCell
    cell.isForHeaderCell = false
    cell.btnReplyTappedClousre = {[weak self] (cell) in
        self?.btnViewAllReplyTapped(cell)
    }
    cell.btnBulbTappedClousre = {[weak self] (cell) in
        self?.btnBulbTapped(cell)
    }
    return cell
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let cell =  tableView.dequeueReusableCell(withIdentifier: StoryBoard.Cells.CommentCell) as! CommentCell
    cell.isForHeaderCell = true

    cell.btnReplyTappedClousre = {[weak self] (cell) in
        self?.btnViewAllReplyTapped(cell)
    }
    cell.btnBulbTappedClousre = {[weak self] (cell) in
        self?.btnBulbTapped(cell)
    }

    cell.btnReplyTappedClousre = {[weak self] (cell) in
        self?.btnReplyTapped(cell)
    }


    return cell

}

Is there any better way to do it ? Or can i improve it ?

\$\endgroup\$
  • \$\begingroup\$ i don't know if the behaviour is changed with isForHeaderCell but in non header you forget to set cell.btnReplyTappedClousre to nil - because you can also get an reused cell from header \$\endgroup\$ – muescha Aug 22 '18 at 20:31
1
\$\begingroup\$

Personally I never mutate the cell inside the cellForRowAt func but I send it a bunch of things that it needs to (re)build itself through a configure function. So:

cell.configure(with: isForHeaderCell, whenA: { /* someblock */ }, whenB: { /* another block */ }) /* etcetera */

You seem to be forwarding a lot of calls that are doing nothing except forwarding. So the only thing you really need to do is to forward the function you want to call in the end. What you need to understand is that functions can be parameters where blocks are asked and vice versa. A block is an anonymous function and if the in/out matches you can use it.

So technically you only need to pass the function that is going to do the required work all the way up and you're good. For example I have a class that fetches some data:

DataFetcher.fetch(for: id, whenDone: { result in /* etc */ })

This could be wrapped in a block:

{ (id: Int) in
    DataFetcher.fetch(for: id, whenDone: { result in /* etc */ })
}

And this block could be passed all the way up the chain, so you call down once.

func configure(with fetchStuff: (Int) -> ())

I also don't understand why you pass the Cell itself as a parameter. Perhaps because the way the @IBAction functions are structured by default but it's better to ignore it unless you have some really pressing reason to got back to exactly that cell.

Last but not least, providing defaults is not that bad when dealing with blocks that almost surely will be set during runtime:

public var btnReplyTappedClousre:((CommentCell) -> (Void))?

Could be:

private var replyAction: (Int) -> () = { _ in
    assertionFailure("This block should always be replaced by the configure function before the cell is shown")
}

Let me explain the changes in detail:

  • It can be private because it's set through your configure function and will only ever be called from within the cell. Better encapsulation.
  • It does not relate to the button but to what actually happens when you tap the button. So the @IBAction func replyButtonTapped() will call replyAction(chatId)
  • It only passes the required data (in this case the chat id). Nobody outside of CommentCell needs to know anything about CommentCell
  • The empty block containing only the assertionFailure will warn any boneheaded colleague that did not call configure during testing but will not crash when released (if it managed to escape during testing)
  • The block will always be set so you're not dealing with optionals anymore
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.