'Generic delegate response handlers

I've got a class currently something like this

class Client {

    var responseOneDelegate: ResponseOneDelegate?
    var responseTwoDelegate: ResponseTwoDelegate?
    ...

    func onData(forMessageType messageType: MessageType, data: Data) {
        
        switch messageType {
        case .responseOne:
            let response = JSONDecoder().decode(Response<ResultForResponseOne>.self, from: data)
            responseOneDelegate?.received(response: response)
        case .responseTwo:
            let response = JSONDecoder().decode(Response<ResultForResponseTwo>.self, from: data)
            responseTwoDelegate?.received(response: response)
        }
    }


}

protocol ResponseOneDelegate {

    func received(response: Response<ResultForResponseOne>)

}

protocol ResponseTwoDelegate {

    func received(response: Response<ResultForResponseTwo>)

}

With the idea that a class can be one or multiple delegates

class Handler: ResponseOneDelegate, ResponseTwoDelegate {

    func received(response: Response<ResultForResponseOne>) { }
    func received(response: Response<ResultForResponseTwo>) { }

}

This seems to be screaming out to be generalised as there will be quite a lot of responses in this format, but I can't quite figure out how to do it

I've tried using a generic type to make just a single delegate

protocol ResponseDelegate: AnyObject {
    
    associatedtype T
    func received(response: Response<T>)

}

It doesn't seem possible to store the delegates in Client in [MessageType: ResponseDelegate] so with the idea of the generic delegate I'm not sure how I'd store the references of the delegates? Maybe I'd just have to cast them before calling?

How would you generalise this?



Solution 1:[1]

Functions may be helpful here, in cases where you have a protocol with just a single function in it, and few types that implement the protocol. Here's an example of the idea:

class Client {
    var handle: ((Data) -> Bool)

    init(handle: @escaping ((Data) -> Bool)) {
        self.handle = handle
    }

    func received(data: Data) {
        handle(data)
    }
}

let ints = { (data: Data) -> Bool in
    guard let i = try? JSONDecoder().decode(Int.self, from: data) else {
        return false
    }
    print("handle \(i)")

    return true // if handled
}

let strings = { (data: Data) -> Bool in
    guard let str = try? JSONDecoder().decode(String.self, from: data) else {
        return false
    }
    // handle Strings
    print("handle \(str)")
    return true
}

let intOrString = { (data: Data) -> Bool in
    ints(data) ||
    strings(data)
}

func handleMany(handlers: ((Data) -> Bool)...) -> (Data) -> Bool {
    return { data in
        for handle in handlers {
            if handle(data) {
                return true
            }
        }
        return false
    }
}

let intsOrStrings = handleMany(handlers: ints, strings)

let aOrBClient = Client(handle: intsOrStrings)
let aClient = Client(handle: ints)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Shadowrun