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 ?
>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.