Reputation: 33471
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
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
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
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