---
title: "Default UIBarButtonItems with protocols"
slug: default-uibarbuttonitems-with-protocols
section: tech
date: 2016-06-17T07:27:00.000Z
canonical: https://roland.leth.ro/blog/tech/default-uibarbuttonitems-with-protocols
---

Having to create the same set of buttons over and over can become cumbersome. We'll try to make use of protocols and implement some default ones. Let's start with that:

```swift
@objc protocol CanGoBack { // Sounds better than Backable :)

  func back()
  
}

extension CanGoBack where Self: UIViewController {
  
  func back() {
    if presentingViewController != nil, navigationController?.childViewControllers.first == self {
      navigationController?.dismiss(animated: true)
    }
    else {
	  navigationController?.popViewController(animated: true)
    }
  }
  
  func setupBackButton() {
    guard navigationController?.childViewControllers.first != self else { return }
	
    let backButton = UIButton.myBackButton()
    backButton.addTarget(self, action: #selector(back), for: .touchUpInside) // ??
    navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
  }
  
}
```

But here we stumble upon the problem: `back` has to be marked as `@objc`, to be able to create the `#selector`, but that's not possible; `@objc` is to be used for _members of classes, @objc protocols, and concrete **extensions of classes**_.

Sadly, there's no way to work around this without some subclassing, but the good news is it's rather straightforward:

```swift
class Button: UIButton {
  // `action` is the internal variable of type `Selector`.
  private let buttonAction: () -> Void
    
  @objc private func performButtonAction() {
    buttonAction()
  }
	
  init(action: () -> Void) {
    buttonAction = action
    let image = UIImage(named: "back")
    super.init(frame: CGRect(origin: .zero, size: image?.size ?? .zero))
    setImage(image, forState: .Normal)
    addTarget(self, action: #selector(performButtonAction), forControlEvents: .TouchUpInside)
  }
}
```

We then modify our protocol, by replacing the `UIButton` creation:

```swift
func setupBackButton() {
  guard
    navigationController?.childViewControllers.first != self
    else { return }

  let backButton = Button(action: { [weak self] in self?.back() } )
  navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
}
```

And from now on, all we have to do for an `UIViewController` to have a back button and action is:

```swift
class SecondMainViewController: UIViewController, CanGoBack {
  override viewDidLoad() {
    super.viewDidLoad()
    setupBackButton()
  }
}
```

We could go a step further and subclass `UIBarButtonItem`, but since the logic is exactly the same, there's no need to include it here.