I'm struggling a bit to find the Swiftiest way to do this.
I have a hierarchy of Request classes. Each Request is an asynchronous operation of a specific type, with different inputs and results. Each Request also has a delegate that gets called when the request finishes. The delegate is made up of a protocol hierarchy that parallels the Request hierarchy.
What I'd like to do is have one delegate member variable in the top-level Request type, such that each subclassed Request automatically gets a delegate variable of the corresponding sub-protocol.
I feel like this is a problem that protocols (and associated types?) were intended to solve, but I can't quite figure out what it should look like. Am I misunderstanding?
Here's a contrived example of what I'd like to be able to express:
Obviously, this won't compile. But it expresses the basic spirit of what I want to do: extend the delegate functionality in parallel with the request functionality.
protocol Delegate: class {
}
protocol Request : class {
associatedtype DelegateType: Delegate
weak var delegate: DelegateType? { get }
}
class MyRequest : Request {
var delegate: MyDelegate?
}
Is that what you want?
Otherwise can you provide some code to explain a little more?
I'm able to get close to what I want, but in the end my concrete Request subtype isn't able to have a delegate of the protocol root type, because a protocol does not conform to itself (something I find puzzling).
I don't see a reason to have delegate a separate type. Perhaps you have other things that affect your decision, but here is what I'd do:
protocol Request {
typealias Callback = (_ request: Self) -> ()
init(didFinish callback: @escaping Callback?)
}
class FooRequest : Request {
init(didFinish callback: @escaping Callback?) {
// thanks to the Self associated type, callback here will be `((FooRequest) -> ())?`
// which is what you wanted, unless I misunderstand the problem statement
}
}
protocol DelegateP {
associatedtype Request: RequestP
}
protocol RequestP {
// The trick is to tie the request type and its delegate type with this constraint on the associated type
associatedtype Delegate: DelegateP where Delegate.Request == Self
init(delegate: Delegate?)
var delegate: Delegate? { get }
}
class FooDelegate: DelegateP {
typealias Request = FooRequest
}
class FooRequest : RequestP {
typealias Delegate = FooDelegate
var _delegate: Delegate?
required init(delegate: Delegate?) {
self._delegate = delegate
}
var delegate: Delegate? {
return _delegate
}
}
My imagination fails me here. But it still feels that a proper combination of associated types and constraints applied to them on concrete types, should do the trick.