I'd like to implement some kind of Decorator
pattern, which allows to write reusable decorators
So I defined 2 protocols. The first one defines a type for a decorator:
protocol ViewDecorator {
associatedtype View: Decoratable
func decorate(view: View)
}
// this is how I expect to use decorator
class GreenViewDecorator: ViewDecorator {
typealias View = GreenView
func decorate(view: GreenView) {
view.backgroundColor = UIColor.green
}
}
The second one defines a type which decoratable view should conform to.
protocol Decoratable {
func decorate<T: ViewDecorator>(with decorator: T)
}
extension Decoratable where Self: UIView {
func decorate<T : ViewDecorator>(with decorator: T) {
decorator.decorate(view: self)
}
}
// exampled of 'decoratable' view
class GreenView: UIView, Decoratable { }
I defined default implementation of function func decorate<T : ViewDecorator>(with decorator: T)
in the protocol extension. I see it's useful if my view will implement the method by default. I just need it only inherit Decoratable
protocol. And then I can use it like:
// example of using decorator with view
let decorator = GreenViewDecorator()
greenView.decorate(with: decorator)
But Swift5 compiler raises error at the line decorator.decorate(view: self)
Cannot invoke 'decorate' with an argument list of type '(view: Self)'
============== TOTAL LISTING ==========
protocol ViewDecorator {
associatedtype View: Decoratable
func decorate(view: View)
}
protocol Decoratable {
func decorate<T: ViewDecorator>(with decorator: T)
}
extension Decoratable where Self: UIView {
func decorate<T : ViewDecorator>(with decorator: T) {
decorator.decorate(view: self)
}
}
class GreenView: UIView, Decoratable { }
class GreenViewDecorator: ViewDecorator {
typealias View = GreenView
func decorate(view: GreenView) {
view.backgroundColor = UIColor.green
}
}
class ViewController: UIViewController {
@IBOutlet var greenView: GreenView!
override func viewDidLoad() {
super.viewDidLoad()
let decorator = GreenViewDecorator()
greenView.decorate(with: decorator)
}
}
The type of the argument is the associated type View
, but it has not been established anywhere that Self
is that type, hence you cannot pass an argument of type Self
without establishing that it is compatible. For example:
extension Decoratable where Self: UIView {
func decorate<T: ViewDecorator>(with decorator: T) where T.View == Self {
decorator.decorate(view: self)
}
}
(However, if you do this, you will have trouble conforming to the Decoratable
protocol because it requires this method for any ViewDecorator
. You can change the protocol to also have the same T.View == Self
limitation.)