I want to Decorate UIViewController with the ability to adjust it's interface when setInteractionEnabled
method is called from another class (ex. Network State Manager). All changes (if any) should be provided in the concrete controller by overriding onInteractionChanged
. Here is my code:
import Foundation
typealias InteractionClosure = ((enabled: Bool) -> Void)
protocol Interaction: class {
var onInteractionChanged: InteractionClosure? { get set }
func setInteractionEnabled(enabled: Bool)
}
extension Interaction where Self: UIViewController {
// Default: Do nothing
// Throws: - Extensions may not contain stored properties
var onInteractionChanged: InteractionClosure? = nil
func setInteractionEnabled(enabled: Bool) {
onInteractionChanged?(enabled: enabled)
}
}
extension UIViewController : Interaction {}
How to add default implementation for onInteractionChanged
?
The modern-swift compact way to use a closure in the protocol is as follows
typealias InteractionClosure = (Bool) -> Void
protocol Interaction {
var interactionClosure: InteractionClosure? { get set }
}
class SomeViewController: UIViewController { }
extension SomeViewController: Interaction {
private struct AssociatedKeys {
static var interactionClosure: UInt8 = 0
}
var interactionClosure: InteractionClosure? {
get {
let wrapper =
objc_getAssociatedObject(self, &AssociatedKeys.interactionClosure) as? InteractionClosure
return wrapper
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.interactionClosure, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Here we have no any global vars and supplementary functions.
And we don't use protocol extension with default implementation because we need to set this closure to different UIViewController
instances.
If we used global var as key for associated value then we'd override closure setter for the first instance by the second one.
Also the default implementation is not such obvious and the most times has to be associated with the self
context (only global abstract defaults are good).