Mark Struzinski
Mark Struzinski

Reputation: 33471

Swift 2 Protocol - Property with a Generic Type?

In my project using Swift 2, I am dealing with 2 protocols, MyViewControllerProtocol and MyViewModelProtocol. I want all View Controllers to conform to MyViewControllerProtocol. This protocol will require a property. The property should conform to the MyViewModel protocol.

What I was thinking would work here would be something like this:

protocol MyViewControllerProtocol {
    var viewModel: <T:MyViewModelProtocol> { get set }
} 

class MyCustomViewModel: MyViewModelProtocol {
     // Implementation here
}

Then for the View Controller:

class ViewController: UIViewController, MyViewControllerProtocol {
    var viewModel: MyCustomViewModel {
        // Getter and Setter implementations here
    }
}

I am most likely thinking about this wrong. This will not compile, and I haven't yet seen this type of implementation in conjunction with a property. Is there some other pattern that could accomplish what I'm trying to do here?

Upvotes: 5

Views: 10174

Answers (3)

davidrynn
davidrynn

Reputation: 2376

typealias for protocols has been deprecated and replaced with associatedtype. The above answers would now be written like:

protocol MyViewController { 
    associatedtype MyViewModelType
    var viewModel: MyViewModelType { get set }
}

Upvotes: 3

Qbyte
Qbyte

Reputation: 13243

Protocols in Swift don't have generic angle brackets. To use generic behavior you have to use typealias:

protocol MyViewControllerProtocol {
    typealias T: MyViewModelProtocol
    var viewModel: T { get set }
}

Upvotes: 1

zrzka
zrzka

Reputation: 21219

If you would like to have dynamic protocol property type, there's typealias.

protocol MyViewModel {
  var title: String { get set }
}

protocol MyViewController {
  typealias MyViewModelType

  var viewModel: MyViewModelType { get set }
}

class BaseViewController<T: MyViewModel>: MyViewController {
  typealias MyViewModelType = T
  var viewModel: T

  init(_ viewModel: T) {
    self.viewModel = viewModel
  }
}

struct CarViewModel: MyViewModel {
  var title: String = "Car"
}

struct BikeViewModel: MyViewModel {
  var title: String = "Bike"
}

let car = BaseViewController(CarViewModel())
let bike = BaseViewController(BikeViewModel())

If you try to use it with model not conforming to your MyViewModel protocol, it will not work:

struct AnotherModel {
  var title: String = "Another"
}

let another = BaseViewController(AnotherModel())

This has some gotchas like you can't pass your view controller via argument of MyViewController type, because of typealias. This is not going to work:

func something(vc: MyViewController) {
}

Why not simpler approach without typealias. If I understood you correctly, you don't need them. Something like:

protocol MyViewModel {
  var title: String { get set }
}

protocol MyViewController {
  var viewModel: MyViewModel { get set }
}

class BaseViewController: MyViewController {
  var viewModel: MyViewModel

  init(_ viewModel: MyViewModel) {
    self.viewModel = viewModel
  }
}

struct CarViewModel: MyViewModel {
  var title: String = "Car"
}

struct BikeViewModel: MyViewModel {
  var title: String = "Bike"
}

And now you can use MyViewController protocol as a variable type:

let bike: MyViewController = BaseViewController(BikeViewModel())
let car: MyViewController = BaseViewController(CarViewModel())

You can pass it to some function as MyViewController:

func something(vc: MyViewController) {
}

And you can't do this as well:

struct AnotherViewModel {
  var title: String = "Another"
}

let another: MyViewController = BaseViewController(AnotherViewModel())

Did I miss something? I mean, about your generics and type constraints? Do you have some use case which forces you to use them?

Upvotes: 4

Related Questions