Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Swift Generic Function Without Explicit Declaration

I am facing problem in creating a generic function that will handle all the requests I receive in Vapor. I hope someone can help me to fix this issue

func boot(routes: RoutesBuilder) throws {
        routes.get("localsetting", ":param") { request in
            let response: OutboundWrapper<LocalSettingsDO> = self.handleIncomingRequest(request: request, entity: .localSetting)
            return response
        }
        routes.get("client", ":param:") { request in
            let response: OutboundWrapper<ClientDO> = self.handleIncomingRequest(request: request, entity: .client)
            return response
        }
}
fileprivate func handleIncomingRequest<T: Content>(request: Request, entity: Entity) -> OutboundWrapper<T> {
        var outboundWrapper = OutboundWrapper<T>()
        
        switch entity {
        case .client:
            var domainObject = ClientDO()
            if let param = request.parameters.get("param") {
                outboundWrapper = OutboundWrapper(data: domainObject as! T)
            }
        case .localSetting:
            var domainObject = ApplicationLocalSettingsDO()
            if let param = request.parameters.get("param") {
                outboundWrapper = OutboundWrapper(data: domainObject as! T)
            }
        }
        return outboundWrapper
}
struct OutboundWrapper<T: Content>: Content {
    public dynamic var data: T? = nil
    
    public init() {}
    public init(data: T) {
        self.data = data
    }
}

I feel that in this implementation, I missed out the purpose of generic as i need to explicitly cast my function return with let response: OutboundWrapper<ClientDO> for every entity function.

Is there a better way to improve this so that I can just call self.handleIncomingRequest ?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

Instead of having an Entity parameter that you check, have the caller pass in a factory () -> T, that handleIncomingRequest uses to construct a T. This avoids all the as! T stuff.

fileprivate func handleIncomingRequest<T: Content>(request: Request, contentFactory: () -> T) -> OutboundWrapper<T> {

    var domainObject = contentFactory()
    if let param = request.parameters.get("param") {
        let predicate = NSPredicate(format: "ID == %i", param.convertToInt())

        // do something with predicate, perhaps (?)

        return OutboundWrapper(data: domainObject)
    }
    return OutboundWrapper()
}

Usage:

let response = self.handleIncomingRequest(request: request, contentFactory: LocalSettingsDO.init)

Alternatively, have a protocol that requires a parameterless initialiser:

protocol DomainObbject {
    init()

    // perhaps also:
    // func predicate(for param: String) -> NSPredicate
}

extension LocalSettingsDO: DomainObject {}
extension ClientDO: DomainObject {}

Then handleIncomingRequest can take a T.Type, and you can directly call the initialiser in the method:

fileprivate func handleIncomingRequest<T: Content & DomainObject>(request: Request, domainObjectType: T.Type) -> OutboundWrapper<T> {

    var domainObject = T()
    ...
}

Instead of passing LocalSettingsDO.init, you would pass LocalSettingsDO.self.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading