A WWDC 2015 session video describes the idea of Protocol-Oriented Programming, and I want to adopt this technique in my future apps. I've been playing around with Swift 2.0 for the last couple of days in order to understand this new approach, and am stuck at trying to make it work with the Delegate Pattern.
I have two protocols that define the basic structure of the interesting part of my project (the example code is nonsense but describes the problem):
1) A delegation protocol that makes accessible some information, similar to UITableViewController's dataSource protocol:
protocol ValueProvider {
var value: Int { get }
}
2) An interface protocol of the entity that does something with the information from above (here's where the idea of a "Protocol-First" approach comes into play):
protocol DataProcessor {
var provider: ValueProvider { get }
func process() -> Int
}
Regarding the actual implementation of the data processor, I can now choose between enums, structs, and classes. There are several different abstraction levels of how I want to process the information, therefore classes appear to fit best (however I don't want to make this an ultimate decision, as it might change in future use cases). I can define a base processor class, on top of which I can build several case-specific processors (not possible with structs and enums):
class BaseDataProcessor: DataProcessor {
let provider: ValueProvider
init(provider: ValueProvider) {
self.provider = provider
}
func process() -> Int {
return provider.value + 100
}
}
class SpecificDataProcessor: BaseDataProcessor {
override func process() -> Int {
return super.process() + 200
}
}
Up to here everything works like a charm. However, in reality the specific data processors are tightly bound to the values that are processed (as opposed to the base processor, for which this is not true), such that I want to integrate the ValueProvider directly into the subclass (for comparison: often, UITableViewControllers are their own dataSource and delegate).
First I thought of adding a protocol extension with a default implementation:
extension DataProcessor where Self: ValueProvider {
var provider: ValueProvider { return self }
}
This would probably work if I did not have the BaseDataProcessor class that I don't want to make value-bound. However, subclasses that inherit from BaseDataProcessor and adopt ValueProvider seem to override that implementation internally, so this is not an option.
I continued experimenting and ended up with this:
class BaseDataProcessor: DataProcessor {
// Yes, that's ugly, but I need this 'var' construct so I can override it later
private var _provider: ValueProvider!
var provider: ValueProvider { return _provider }
func process() -> Int {
return provider.value + 10
}
}
class SpecificDataProcessor: BaseDataProcessor, ValueProvider {
let value = 1234
override var provider: ValueProvider { return self }
override func process() -> Int {
return super.process() + 100
}
}
Which compiles and at first glance appears to do what I want. However, this is not a solution as it produces a reference cycle, which can be seen in a Swift playground:
weak var p: SpecificDataProcessor!
autoreleasepool {
p = SpecificDataProcessor()
p.process()
}
p // <-- not nil, hence reference cycle!
Another option might be to add class constraints to the protocol definitions. However, this would break the POP approach as I understand it.
Concluding, I think my question boils down to the following: How do you make Protocol Oriented Programming and the Delegate Pattern work together without restricting yourself to class constraints during protocol design?