JetForMe
(Rick M)
1
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:
protocol RequestDelegate
{
func requestReceived(_ inRequest: Request)
}
class Request
{
init(delegate inDelegate : RequestDelegate)
{
self.delegate = inDelegate
}
func
reqeuestCompletionCallback()
{
self.delegate?.requestReceived(self)
}
var delegate: RequestDelegate?
}
protocol FooRequestDelegate : RequestDelegate
{
func fooRequestHappened(_ inRequest: FooRequest)
}
class FooRequest : Request
{
init(delegate inDelegate : FooRequestDelegate)
{
super.init(delegate: inDelegate)
}
override
func
reqeuestCompletionCallback()
{
super.requestCompletionCallback()
self.delegate?.fooRequestHappened(self)
}
}
protocol FooBarRequestDelegate : FooRequestDelegate
{
func fooBarRequestHappened(_ inRequest: FooRequest)
}
class FooBarRequest : FooRequest
{
init(delegate inDelegate : FooBarRequestDelegate)
{
super.init(delegate: inDelegate)
}
override
func
reqeuestCompletionCallback()
{
super.requestCompletionCallback()
self.delegate?.fooBarRequestHappened(self)
}
}
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.
Thanks!
jechris
(JC)
2
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?
: class is deprecated, use : AnyObject instead.
JetForMe
(Rick M)
4
I've updated the original post with an example of what I want to do.
JetForMe
(Rick M)
5
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).
moiseev
(Max Moiseev)
6
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
}
}
JetForMe
(Rick M)
7
No, it needs to be a delegate (there are multiple callbacks per request type (e.g. progress, completion, failure).
moiseev
(Max Moiseev)
8
This maybe?
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
}
}
JetForMe
(Rick M)
9
Where is the var delegate defined?
moiseev
(Max Moiseev)
10
Ahh.. Sorry. Updated the snippet above.
JetForMe
(Rick M)
11
Ah, yeah, FooDelegate needs to be a protocol. The actual delegates come from elsewhere.
moiseev
(Max Moiseev)
12
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.
JetForMe
(Rick M)
13
I also would rather not do the two-way thing, if I can avoid it.