I make a protocol:
protocol SomeProtocol {
func getData() -> String
}
I make a struct that conforms to it:
struct SomeStruct: SomeProtocol {
func getData() -> String {
return "Hello"
}
}
Now I want every UIViewController to have a property called source, so I can do something like…
class MyViewController : UIViewController {
override func viewDidLoad() {
self.title = source.getData()
}
}
To accomplish this, I create a protocol to define the property:
protocol SomeProtocolInjectable {
var source: SomeProtocol! { get set }
}
Now I just need to extend the view controller with this property:
extension UIViewController: SomeProtocolInjectable {
// ???
}
How can I hack together a stored property that will work with a protocol type?
What hasn't worked:
var source: SomeProtocol! obviously doesn't work because extensions don't have stored propertiesAny other suggestions?
Any protocol object can be converted into a type-erased class. Build an AnySomeProtocol and store that.
private var sourceKey: UInt8 = 0
final class AnySomeProtocol: SomeProtocol {
func getData() -> String { return _getData() }
init(_ someProtocol: SomeProtocol) { _getData = someProtocol.getData }
private let _getData: () -> String
}
extension UIViewController: SomeProtocolInjectable {
var source: SomeProtocol! {
get {
return objc_getAssociatedObject(self, &sourceKey) as? SomeProtocol
}
set(newValue) {
objc_setAssociatedObject(self, &sourceKey, AnySomeProtocol(newValue), .OBJC_ASSOCIATION_RETAIN)
}
}
}
class MyViewController : UIViewController {
override func viewDidLoad() {
self.title = source.getData()
}
}
The caller can only use this to access the protocol methods. You can't force it back into its original type with as, but you should avoid that anyway.
As a side note, I'd really recommend making source return SomeProtocol? rather than SomeProtocol!. There's nothing here that promises that source will be set. You don't even set it until viewDidLoad.
You can hack around with a static and the view controllers hash:
struct SomeProtocol {/*....*/}
struct DataProxy {
var data: [Int: SomeProtocol]
}
protocol SomeProtocolInjectable {
var source: SomeProtocol! { get set }
}
extension UIViewController: SomeProtocolInjectable {
static var dataProxy = DataProxy(data: [:])
var source: SomeProtocol! {
get{
return UIViewController.dataProxy.data[self.hashValue]
}
set{
UIViewController.dataProxy.data[self.hashValue] = newValue
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With