0

Say I have the following API Service:

enum HttpMethod: Equatable {
    case get([URLQueryItem])
    case put(Data?)
    case post(Data?)

    var name: String {
        switch self {
        case .get: return "GET"
        case .put: return "PUT"
        case .post: return "POST"
        }
    }
}

struct Request<Response> {
    let url: URL
    let method: HttpMethod
    var headers: [String: String] = [:]
}

extension Request {
    var urlRequest: URLRequest {
        var request = URLRequest(url: url)

        switch method {
        case .post(let data), .put(let data):
            request.httpBody = data
        case let .get(queryItems):
            var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
            components?.queryItems = queryItems
            guard let url = components?.url else {
                preconditionFailure("Couldn't create a url from components...")
            }
            request = URLRequest(url: url)
        default:
            break
        }

        request.allHTTPHeaderFields = headers
        request.httpMethod = method.name
        return request
    }
}

How can I compose a Data representation from a list of parameters that I can POST?

extension Request {
    static func postEvent(type: String, title:String, tableId:Int? = nil, params: [String:Any]?) -> Self {
        Request(
            url: URL(string: NetworkingConstants.EVENTS)!,
            method: .post(
                // WHAT GOES HERE?
                // How to compose Data representation JSON?
            )
        )
    }
}
soleil
  • 12,133
  • 33
  • 112
  • 183
  • 1
    That depends on what the server accepts, doesn't it? What format does the server need the data to be in? JSON? XML? Something else? – Sweeper Jul 09 '23 at 12:57
  • Let's assume JSON. – soleil Jul 09 '23 at 13:30
  • Well then it's a duplicate of [this](https://stackoverflow.com/q/29625133/5133585). – Sweeper Jul 09 '23 at 13:32
  • That answer focuses on `JSONSerialization`. Generally [`JSONEncoder`](https://developer.apple.com/documentation/foundation/jsonencoder) with [custom types](https://developer.apple.com/documentation/foundation/archives_and_serialization/using_json_with_custom_types) that are [`Encodable`](https://developer.apple.com/documentation/swift/encodable) might be preferable. See [Encoding and Decoding Custom Types](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types). – Rob Jul 09 '23 at 17:00
  • 1
    As an aside, it would be best practice to set the `Content-Type` header for your `POST` requests. E.g., for `POST` with JSON body, we might specify a `Content-Type` of `application/json`. Depending upon the framework your backend is using, it might accept different types of payloads, so it is prudent to set the `Content-Type` header. FWIW, some web services would expect `application/x-www-form-urlencoded` requests (which obviously require client to encode the `httpBody` differently). – Rob Jul 09 '23 at 17:13
  • @Rob I tried adding `headers["Content-Type"] = "application/json"` before the `request.allHTTPHeaderFields = headers` line, but `Cannot assign through subscript: 'self' is immutable`. Where should this go? – soleil Jul 09 '23 at 22:09
  • 1
    I’d probably do it right were I encode the values, e.g., perhaps something like https://gist.github.com/robertmryan/31daba0c0bf3d46ed26a17e3603b5eb8. – Rob Jul 10 '23 at 06:30
  • @Rob that's helpful, thanks. I'm still struggling with the original question though. My latest attempt is `method: .post( try? JSONEncoder().encode(params) )` but `Type 'Any' cannot conform to 'Encodable'` – soleil Jul 10 '23 at 14:41
  • In `postEvent`, rather than `[String: Any]`, the `params` should be declared as `Encodable`. – Rob Jul 10 '23 at 15:43
  • I just want to be able to send any dictionary of params in my API calls though. There's got to be a way to do this without creating a new `Encodable` object for every new endpoint, right? – soleil Jul 10 '23 at 19:08
  • Using `Any` is a bit of an anti-pattern, and we generally would use `Encodable` types so that you enjoy compile-time validation of our parameters. But if you don’t want to tackle that right now, feel free to use `JSONSerialization` instead of `JSONEncoder`. – Rob Jul 11 '23 at 21:19

0 Answers0