Parallel inheritance hierarchies

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!

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.

I’ve updated the original post with an example of what I want to do.

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
    }
}

No, it needs to be a delegate (there are multiple callbacks per request type (e.g. progress, completion, failure).

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
    }
}

Where is the var delegate defined?

Ahh… Sorry. Updated the snippet above.

Ah, yeah, FooDelegate needs to be a protocol. The actual delegates come from elsewhere.

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.

I also would rather not do the two-way thing, if I can avoid it.