jwarris91
jwarris91

Reputation: 952

Nesting JSON Dictionary Inside a Key?

I need to send a request inside a key, expecting it to look like so:

 {
  "user": {
    "email": String,
    "password": String
  }
}

I am trying to do this via creating a UserSignupRequest that has email and password properties and conforms to codable:

struct UserSignupRequest: Codable {
    let email: String
    let password: String

    enum CodingKeys: String, CodingKey {
        case email
        case password
    }
}

Then creating the parameters for alamofire via:

case .signup(let request):
        return ["user": request]
}

My logic was this will create the child key value pairs inside a user parent key, however my app fatal errors with errors when trying:

urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])

Im sure the solution is something simple, but I cant quite get it to work! many thanks

Upvotes: 1

Views: 147

Answers (1)

ManWithBear
ManWithBear

Reputation: 2875

JSONSerialization

JSONSerialization works only with simple types as arrays, dictionaries, strings, numbers, etc.

If you want to use it, then you don't need Codable, instead you need function func toDict() -> [String: Any] that will convert your UserSignupRequest to dictionary.
Then your switch will be look like:

case .signup(let request):
    return ["user": request.toDict()]
}

JSONEncoder

JSONSerialization is old api and if you planning to use Codable you need to use:

urlRequest.httpBody = try JSONEncoder().encode(parameters)

Generic parameter 'T' could not be inferred

I assume you getting parameters like:

func parameters(for request: RequestType) -> [String: Any] {
    switch request {
    case .signup(let request):
        return ["user": request]
    }
}

So when you pass your parameters in encoder he doesn't know what type to encode.

TO solve we can return not just Dictionary, but specific protocol / type aka:

protocol Request {
    func encode(by encoder: JSONEncoder) throws -> Data
}
extension Request where Self: Encodable {
    func encode(by encoder: JSONEncoder) throws -> Data {
        /// since it will be method on specific type, JSONEncoder will know what type is encoding
        return try encoder.encode(self) 
    }
}

struct UserSignupRequest: Codable, Request {
    struct RequestData: Codable {
        let email: String
        let password: String
    }
    let data: RequestData

    enum CodingKeys: String, CodingKey {
        case data = "user"
    }
}

func parameters(for request: RequestType) -> Request {
    switch request {
    case .signup(let requestData):
        return UserSignupRequest(data: requestData)
    }
}

So now you can do

urlRequest.httpBody = try parameters.encode(by: JSONEncoder())

Upvotes: 2

Related Questions