0

I'm trying to understand the MVVM pattern by learning from a tutorial (link) and I can't get an action I've added to a UIButton to work (I'm using Swift 3.0). I've tried debugging for hours, studied this thread (@selector in swift) and others, and I'm still stuck. When I click the button, nothing happens at all. I tried various syntax options with the selector and I think what you see below is correct. I think the protocol I'm using might be contributing to the problem but, given my limited experience with protocols, I could be completely wrong about that.

I'll put all the code here in case anybody wants to drop it into Xcode. The action in question here is in my UIViewController that I add to showGreetingButton:

class GreetingViewController : UIViewController {
  let showGreetingButton = UIButton()
  @IBOutlet weak var greetingLabel: UILabel!

  var viewModel: GreetingViewModelProtocol! {
    didSet {
      self.viewModel.greetingDidChange = { [unowned self] viewModel in
        self.greetingLabel.text = viewModel.greeting
      }
    }
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    showGreetingButton.backgroundColor = UIColor.red
    showGreetingButton.tintColor = UIColor.black
    showGreetingButton.setTitle("Show Greeting", for: .normal)

    // I add the action here:
    showGreetingButton.addTarget(self.viewModel, action: #selector(GreetingViewModel.showGreeting), for: .touchUpInside)

    showGreetingButton.translatesAutoresizingMaskIntoConstraints = false    
    view.addSubview(showGreetingButton)
    let buttonTop = NSLayoutConstraint(item: showGreetingButton, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.greaterThanOrEqual, toItem: view, attribute: NSLayoutAttribute.topMargin, multiplier: 1.0, constant: 20)
    let buttonLeading = NSLayoutConstraint(item: showGreetingButton, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.leadingMargin, multiplier: 1.0, constant: 0.0)
    showGreetingButton.contentEdgeInsets = UIEdgeInsetsMake(2, 2, 2, 2)
    NSLayoutConstraint.activate([buttonTop, buttonLeading])

    let model = Person(firstName: "David", lastName: "Blaine")

    let viewModel = GreetingViewModel(person: model)

    self.viewModel = viewModel
  }
}

My view model is called GreetingViewModel which adopts a protocol:

protocol GreetingViewModelProtocol: class {      
  var greeting: String? { get }
  var greetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set }
  init(person: Person)
  func showGreeting()
}

class GreetingViewModel : GreetingViewModelProtocol {
  let person: Person
  var greeting: String? {
    didSet {
      self.greetingDidChange?(self)
    }
  }

  var greetingDidChange: ((GreetingViewModelProtocol) -> ())?

  required init(person: Person) {
    self.person = person
  }

  @objc func showGreeting() {
    self.greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
  }
}

And my simple model is:

struct Person {
  let firstName: String
  let lastName: String
}

Any help would be really, really appreciated.

Community
  • 1
  • 1
Jim
  • 1,260
  • 15
  • 37

1 Answers1

0

Move these lines to before you set your button's target:

let viewModel = GreetingViewModel(person: model)
self.viewModel = viewModel

Right now you are adding self.viewModel as the target, but self.viewModel is nil

Vinodh
  • 5,262
  • 4
  • 38
  • 68
beyowulf
  • 15,101
  • 2
  • 34
  • 40
  • I also had to move `let model = Person(firstName: "David", lastName: "Blaine") ` with those two lines but it worked! Thanks! – Jim Oct 29 '16 at 02:21
  • @Vinodh, @beyoulf's answer was better before you edited it because it made it clear that the important thing was for those lines to be before `showGreetingButton.addTarget(self.viewModel, action: #selector(GreetingViewModel.showGreeting), for: .touchUpInside)`. Your edit makes it less clear. – Jim Oct 29 '16 at 02:31
  • Will replace the old one – Vinodh Oct 29 '16 at 02:36
  • Thank you, @Vinodh. – Jim Oct 29 '16 at 02:49