Issue #72

The other day I was refactor my code. I have

extension MainController: TabBarViewDelegate {

  func buttonDidPress index: Int) {
    let initialIndex = tabBarView.selectedIndex
    let wholeAppContentView = updateWholeAppContentView()
    view.addSubview(wholeAppContentView)
  }
}

The delegate method does not look right, as it’s hard to tell between required delegate method, or just instance method. Also it lacks a subject. I like this post API Design, you can read section Rule 19: Always say who’s talking

This is a simple rule, and an equally simple mistake to make. In your delegate methods, always pass the sender as a parameter. Always. Even for singletons. Even for things you cannot conceive would ever be used more than once simultaneously. No exceptions.

So I refactor the delegate, and conform to it.

extension MainController: TabBarViewDelegate {

  func tabBarView(_ view: TabBarView, buttonDidPress index: Int) {
    let initialIndex = tabBarView.selectedIndex
    let wholeAppContentView = updateWholeAppContentView()
    view.addSubview(wholeAppContentView) // This is the culprit ⚠️
  }
}

Even with just 1 line change in MainController.swift, the whole UI breaks, as all the views were added to the tab bar. Strange 😡 .

It didn’t take long until I remember that parameter takes precedence over instance property if they have same name. So in this case, the compiler, without warning, assume you’re dealing with view from TabBarView ⚠️

That’s why you often use self to disambiguate.

struct User: Codable, Equatable {
  let firstName: String
  let lastName: String

  init(firstName: String, lastName: String) {
    self.firstName = firstName
    self.lastName = lastName
  }
}

Back to our code. The workaround is to specify self to specify view of MainController

self.view.addSubview(wholeAppContentView)

Well, you may say, who should add view again in case of tab bar changes 😬 This is a bad example, but the lesson is learned 😇