Nick
Nick

Reputation: 236

Swift Generic URLRequest

I am trying to create a createRequest function that I can re-use for all my networking calls, some require posting JSON and other don't so I was thinking of creating a function that takes an optional generic object; something in theory like this:

struct Person: Codable {

var fName: String
var lName: String

}

struct Location: Codable {

 var city: String
 var state: String

}

let data = Person(fName: "John", lName: "Smith")
let location = Location(city: "Atlanta", state: "Georgia")

createRequest(withData: data)
createRequest(withData: location)

 private func createRequest(withData:T) throws -> URLRequest { 

        var newRequest = URLRequest(url: URL(string: "\(withUrl)")!)

        newRequest.httpMethod = method.rawValue

       if let data = withData {

            newRequest.setBody = data

         }

        if withAPIKey {

            newRequest.setValue(apiKey, forHTTPHeaderField: "APIKEY")

        }

        return newRequest

    }

I would like to return the URLRequest with the option of passing different JSON objects this function. I read that you cant do it this way unless you define the type on the return function but I can't define my object in the return.

Upvotes: 0

Views: 278

Answers (1)

Alexander
Alexander

Reputation: 63157

Preface: This code's a mess of inconsistent indentation and unnecessary whitespace (it reads like a double-spaced essay lol), I cleaned it up.

Looks like your function needs to take a T, but not just any T, but one that is constrained to being Encodable. This is a common observation: more general generic parameters are compatible with more types, but can do we less with them. By containing T to Encodable, we can use it with JSONEncoder.encode.

The label withData: is a misnomer, because that parameter won't have type Data. Something like withBody: would work better.

import Foundation

struct Person: Codable {
    var fName: String
    var lName: String
}

struct Location: Codable {
    var city: String
    var state: String
}

// stubs for making compilation succeed
let apiKey = "dummy"
let withAPIKey = true
enum HTTPMethod: String { case GET } 

private func createRequest<Body: Encodable>(method: HTTPMethod, url: URL, withBody body: Body) throws -> URLRequest { 

    var newRequest = URLRequest(url: url)
    newRequest.httpMethod = method.rawValue

    newRequest.httpBody = try JSONEncoder().encode(body)

    if withAPIKey {
        newRequest.setValue(apiKey, forHTTPHeaderField: "APIKEY")
    }

    return newRequest
}

let data = Person(fName: "John", lName: "Smith")
let location = Location(city: "Atlanta", state: "Georgia")

Upvotes: 1

Related Questions