Atelier Clockwork

Currying to Reduce Duplicated Code

Possibly at the Expense of Readability

I was pulling in the smart deselection function written by one of my co-workers into a project, and as the code isn’t available by Carthage quite yet, I copied and pasted it:

extension UIViewController {
    func rz_smoothlyDeselectRows(tableView tableView: UITableView?) {
        let selectedIndexPaths = tableView?.indexPathsForSelectedRows ?? []

        if let coordinator = transitionCoordinator() {
            coordinator.animateAlongsideTransitionInView(parentViewController?.view, animation: { context in
                selectedIndexPaths.forEach {
                    tableView?.deselectRowAtIndexPath($0, animated: context.isAnimated())
                }
                }, completion: { context in
                    if context.isCancelled() {
                        selectedIndexPaths.forEach {
                            tableView?.selectRowAtIndexPath($0, animated: false, scrollPosition: .None)
                        }
                    }
            })
        }
        else {
            selectedIndexPaths.forEach {
                tableView?.deselectRowAtIndexPath($0, animated: false)
            }
        }
    }
}

After a brief glance, I decided that it wasn’t written in quite the code style that I wanted, in particular I like to avoid multiple inline blocks in an function call. When I was refactoring those bits, I decided to be clever and use currying to avoid semi-duplicated code in the selectedIndexPaths.forEach blocks.

extension UIViewController {

    func rz_smoothlyDeselectRows(tableView tableViewIn: UITableView?) {

        guard let tableView = tableViewIn,
            selectedIndexPaths = tableView.indexPathsForSelectedRows else {
            return
        }

        let deselect = { (tableView: UITableView, animated: Bool) -> ((NSIndexPath) -> ()) in
            return { indexPath in
                tableView.deselectRowAtIndexPath(indexPath, animated: animated)
            }
        }

        let reselect = { (tableView: UITableView, animated: Bool) -> ((NSIndexPath) -> ()) in
            return { indexPath in
                tableView.selectRowAtIndexPath(indexPath, animated: animated, scrollPosition: .None)
            }
        }

        if let coordinator = transitionCoordinator() {
            let animation = { (context: UIViewControllerTransitionCoordinatorContext) in
                selectedIndexPaths.forEach(deselect(tableView, context.isAnimated()))
            }

            let completion = { (context: UIViewControllerTransitionCoordinatorContext) in
                if context.isCancelled() {
                    selectedIndexPaths.forEach(reselect(tableView, false))
                }
            }
            let parentView = parentViewController?.view
            coordinator.animateAlongsideTransitionInView(parentView, animation: animation, completion: completion)
        }
        else {
            selectedIndexPaths.forEach(deselect(tableView, false))
        }
    }

}

The body of my function ended up slightly longer, but if the duplicated functions were longer, and more complex it helps to reduce the chances of missing a change, which can lead to annoying copy / paste bugs.