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