Trace
Trace

Reputation: 67

Generic perform Request, using Generics

I would like to make a perform request function in swift using Generics. I want to make the call and switch on my enum Result based on what I get back. However, I don't understand the : 'cannot invoke performRequest with an argument list of type (NSURLRequest, (Result<__>) -> ())' Why can't I have an unnamed parameter here? I have also tried something like the following : r<MyStruct> --- but I then get an expected expression error. Any help explaining the above Result<_> error would be greatly appreciated. Thanks.

enum Result<A> {
    case Value
    case Error
}

func performRequest<A>(request:NSURLRequest, callback:(Result<A>) -> ()) {
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
    callback(parseResponse(data, response: response, error: error))
    }
    task.resume()
}

class SampleClass {

let request = NSURLRequest(URL: NSURL(string: "www.google.com")!)

init() {
    performRequest(request) { r in -------- errors out
        switch r {
          case .Value:

          case .Error:
    }
}

}

Upvotes: 1

Views: 280

Answers (1)

Carlos
Carlos

Reputation: 1176

The problem is that when you use performRequest, you have not given the compiler enough information about the generic parameter you intend to use. The critical part that is missing is that parseResponse needs to return a Result that is parameterised in the same way as the callback. However, in the snippet you provided, parseResponse is not generic.

I believe this will do what you intend. In this scenario, I've parameterised the Result with String, but you can substitute any other type.

// multi-purpose (generic) Result type
enum Result<A>
{
    case Value(A) // because you parameterised the enum, you might as well take advantage of the type
    case Error
}

// this is a custom parser, you may substitute your own that returns a different type
func parseString( data:NSData?, response:NSURLResponse?, error:NSError? ) -> Result<String> {
    if let _ = error {
        return Result.Error
    }
    return Result.Value("Success")
}

// this function is completely generic, but the parser and callback need to be compatible
func performRequest<A>( request:NSURLRequest,
                         parser:( NSData?, NSURLResponse?, NSError? ) -> Result<A>,
                       callback:( Result<A> ) -> Void ) {
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
        ( data, response, error ) -> Void in
        callback( parser( data, response, error ) )
    }
    task.resume()
}

let request = NSURLRequest(URL: NSURL(string: "www.google.com")!)

// actual invocation, now I need to pass in a concrete parser and callback with a specific type
performRequest( request, parser: parseString ) { // parseString returns a Result<String>
    r in
    switch r {
    case .Value( let value ):
        // because I passed in a parser that returns a Result<String>, I know that "value" is a String here
        print( "Succeeded with value: \(value)" )
        break;
    case .Error:
        print( "an error occurred" )
        break;
    }
}

Upvotes: 1

Related Questions