0

I am trying to parse json data in decodable in swift 4. It prints nil value. i could not find what issue is this?

here is model class:

public struct TaskID: Decodable {

let embedded: Embedded?
let count: Int?

enum CodingKeys: String, CodingKey {
    case count = "count"
    case embedded = "_embedded"
  }
}

 public struct Embedded: Decodable {


let task: [Task]?
enum CodingKeys: String, CodingKey {
       case task = "task"
   }

 }

public struct Task : Decodable {

let id : String?
let name: String?
let assignee: String?
let created: String?
let processDefinitionId: String?
enum CodingKeys: String, CodingKey {
    case id = "id"
    case name = "name"
    case assignee = "assignee"
    case created = "created"
    case processDefinitionId = "processDefinitionId"


 }

}

Here is json:

{
"_links": {
    "self": {
        "href": "/task"
    }
},
"_embedded": {

    "task": [
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": []
            },
            "id": "1b64f688-0616-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-13T13:04:20.687+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        },
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": [
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/loanAmount"
                            }
                        },
                        "_embedded": null,
                        "name": "loanAmount",
                        "value": "650000",
                        "type": "String",
                        "valueInfo": {}
                    },
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/firstName"
                            }
                        },
                        "_embedded": null,
                        "name": "firstName",
                        "value": "Kamesh",
                        "type": "String",
                        "valueInfo": {}
                    }
                ]
            },
            "id": "412a2aca-06ae-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-14T07:13:27.558+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        }

    ]
},
"count": 13
}

Here is urlrequest:

 // MARK: - URLRequestConvertible
func asURLRequest() throws -> URLRequest {
    let url = try K.ProductionServer.baseURL.asURL()

    var urlRequest = URLRequest(url: url.appendingPathComponent(path))
    print(urlRequest)
    // HTTP Method
    urlRequest.httpMethod = method.rawValue

    let authToken = UserDefaults.standard.string(forKey: "authToken")
    let bearerToken: String = "Bearer " + (authToken ?? "")
    print("baearer token::\(bearerToken)")

    // Common Headers
    urlRequest.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.acceptType.rawValue)
    urlRequest.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.contentType.rawValue)
    urlRequest.setValue(bearerToken, forHTTPHeaderField: HTTPHeaderField.authentication.rawValue)

    // Parameters
    if let parameters = parameters {
        do {
            urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }
    }

    return urlRequest
}

Here is alamofire request:

 import Foundation
import Alamofire

public class APIClient {
@discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (AFResult<T>)->Void) -> DataRequest {

    return AF.request(route)
                    .responseDecodable (decoder: decoder){ (response: AFDataResponse<T>) in
                        completion(response.result)
                        print("framework response::",response.result)
    }
}

public static func taskID(id: String, completion:@escaping (AFResult< [TaskID]>)->Void) {

    performRequest(route: APIRouter.TaskById(id: id), completion: completion)
}


}//APIClient

Initially it shows Dictionary, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary but found an array instead. but now i'm getting nil in console log. Do not know why i'm getting nil value. Is struct is correct based on my json response? I'm struggling with getting the nested data.

Any help much appreciated pls..

7
  • What is the difference between this and the other two questions you have posted the last couple of days? They look very similar. Commented Nov 18, 2019 at 6:32
  • @JoakimDanielson last two question i have coding keys and expected to decode dictionary but found an array instead. But now i'm getting nil values.. can you help me out of this Commented Nov 18, 2019 at 6:36
  • @JoakimDanielson now i have url request code too Commented Nov 18, 2019 at 6:37
  • But I, and others, have already tried to help you once with your json and still you seem to struggle with the same thing. I don’t really see the point as nothing has changed really. You need to separate the possible issues with your code, the actual json decoding (which was fine) and the AF and ApiClient classes you have Commented Nov 18, 2019 at 6:46
  • @JoakimDanielson, ok i have one doubt whether i need to pass struct name in array or only class name. public static func taskID(id: String, completion:@escaping (AFResult< [TaskID]>) if i pass TaskID it shows error that Expected to decode Dictionary but found an array instead. if i changed to array [TaskID] it prints nil value. Commented Nov 18, 2019 at 6:49

2 Answers 2

1

Your struct is correct. See playground below for proof; I don't get an error. nil is very common result when you don't have the entire data set and you assume that some optional field in the response is non optional just because you see it in your sample data (your sample is not necessarily representative). Absent an actual spec from the server, you need to figure out what field is failing to decode and you may need to get a bunch of data to figure out which fields are really optional. You can do that by either putting in error handling code in your AF project above or just by pasting your response into my playground below. Either way the decoding error should tell you which field was not present.

import PlaygroundSupport
import UIKit
import WebKit


public struct TaskID: Decodable {

let embedded: Embedded?
let count: Int?

enum CodingKeys: String, CodingKey {
    case count = "count"
    case embedded = "_embedded"
  }
}

 public struct Embedded: Decodable {


let task: [Task]?
enum CodingKeys: String, CodingKey {
       case task = "task"
   }

 }

public struct Task : Decodable {

let id : String?
let name: String?
let assignee: String?
let created: String?
let processDefinitionId: String?
enum CodingKeys: String, CodingKey {
    case id = "id"
    case name = "name"
    case assignee = "assignee"
    case created = "created"
    case processDefinitionId = "processDefinitionId"


 }

}


let data = """
{
"_links": {
    "self": {
        "href": "/task"
    }
},
"_embedded": {

    "task": [
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/1b64cf75-0616-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/1b64f688-0616-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": []
            },
            "id": "1b64f688-0616-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-13T13:04:20.687+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "1b64cf75-0616-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        },
        {
            "_links": {
                "assignee": {
                    "href": "/user/demo"
                },
                "execution": {
                    "href": "/execution/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "identityLink": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25/identity-links"
                },
                "processDefinition": {
                    "href": "/process-definition/quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25"
                },
                "processInstance": {
                    "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25"
                },
                "self": {
                    "href": "/task/412a2aca-06ae-11ea-8860-120ef5ab2c25"
                }
            },
            "_embedded": {
                "variable": [
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/loanAmount"
                            }
                        },
                        "_embedded": null,
                        "name": "loanAmount",
                        "value": "650000",
                        "type": "String",
                        "valueInfo": {}
                    },
                    {
                        "_links": {
                            "self": {
                                "href": "/process-instance/412a03b7-06ae-11ea-8860-120ef5ab2c25/variables/firstName"
                            }
                        },
                        "_embedded": null,
                        "name": "firstName",
                        "value": "Kamesh",
                        "type": "String",
                        "valueInfo": {}
                    }
                ]
            },
            "id": "412a2aca-06ae-11ea-8860-120ef5ab2c25",
            "name": "Quick Evaluation",
            "assignee": "demo",
            "created": "2019-11-14T07:13:27.558+0000",
            "due": null,
            "followUp": null,
            "delegationState": null,
            "description": null,
            "executionId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "owner": null,
            "parentTaskId": null,
            "priority": 50,
            "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25",
            "processInstanceId": "412a03b7-06ae-11ea-8860-120ef5ab2c25",
            "taskDefinitionKey": "QuickEvaluation",
            "caseExecutionId": null,
            "caseInstanceId": null,
            "caseDefinitionId": null,
            "suspended": false,
            "formKey": "a8apps:suryoday:gng:v0.1.0:kycUpload",
            "tenantId": null
        }

    ]
},
"count": 13
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
do {
  let decoded = try decoder.decode(TaskID.self, from: data)
  print(decoded)
} catch ( let error ) {
  print(error.localizedDescription)
}

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for answering.. I have doubt in this whether i need pass into array or class. func taskID(id: String, completion:@escaping (AFResult<EmbeddedResponseData>)->Void
still facing same issue
Now i have url request code too.. can you check it out
The wrapper is TaskID as in my example.
Instead of using the AF responseDecodable just get the data and decode it yourself so you can catch the decode error and figure out which field is failing.
0
import Foundation

// MARK: - Task
struct Task: Codable {
let links: VariableLinks?
let embedded: TaskEmbedded?
let count: Int?

enum CodingKeys: String, CodingKey {
    case links = "_links"
    case embedded = "_embedded"
    case count = "count"
}
}

// MARK: - TaskEmbedded
struct TaskEmbedded: Codable {
let task: [TaskElement]?

enum CodingKeys: String, CodingKey {
case task = "task"
}
}

// MARK: - TaskElement
struct TaskElement: Codable {
let links: TaskLinks?
let embedded: TaskEmbeddedClass?
let id: String?
let name: String?
let assignee: String?
let created: String?
let due: String?
let followUp: String?
let delegationState: String?
let taskDescription: String?
let executionId: String?
let owner: String?
let parentTaskId: Int?
let priority: Int?
let processDefinitionId: String?
let processInstanceId: String?
let taskDefinitionKey: String?
let caseExecutionId: Int?
let caseInstanceId: Int?
let caseDefinitionId: Int?
let suspended: Bool?
let formKey: String?
let tenantId: Int?

enum CodingKeys: String, CodingKey {
case links = "_links"
case embedded = "_embedded"
case id = "id"
case name = "name"
case assignee = "assignee"
case created = "created"
case due = "due"
case followUp = "followUp"
case delegationState = "delegationState"
case taskDescription = "description"
case executionId = "executionId"
case owner = "owner"
case parentTaskId = "parentTaskId"
case priority = "priority"
case processDefinitionId = "processDefinitionId"
case processInstanceId = "processInstanceId"
case taskDefinitionKey = "taskDefinitionKey"
case caseExecutionId = "caseExecutionId"
case caseInstanceId = "caseInstanceId"
case caseDefinitionId = "caseDefinitionId"
case suspended = "suspended"
case formKey = "formKey"
case tenantId = "tenantId"
}
}

// MARK: - TaskEmbeddedClass
struct TaskEmbeddedClass: Codable {
let variable: [Variable]?

enum CodingKeys: String, CodingKey {
case variable = "variable"
}
}

// MARK: - Variable
struct Variable: Codable {
let links: VariableLinks?
let embedded: String?
let name: String?
let value: String?
let type: String?
let valueInfo: ValueInfo?

enum CodingKeys: String, CodingKey {
case links = "_links"
case embedded = "_embedded"
case name = "name"
case value = "value"
case type = "type"
case valueInfo = "valueInfo"
}
}

// MARK: - VariableLinks
struct VariableLinks: Codable {
let linksSelf: SelfClass?

enum CodingKeys: String, CodingKey {
case linksSelf = "self"
}
}

// MARK: - SelfClass
struct SelfClass: Codable {
let href: String?

enum CodingKeys: String, CodingKey {
case href = "href"
}
}

// MARK: - ValueInfo
struct ValueInfo: Codable {
}

// MARK: - TaskLinks
struct TaskLinks: Codable {
let assignee: SelfClass?
let execution: SelfClass?
let identityLink: SelfClass?
let processDefinition: SelfClass?
let processInstance: SelfClass?
let linksSelf: SelfClass?

enum CodingKeys: String, CodingKey {
case assignee = "assignee"
case execution = "execution"
case identityLink = "identityLink"
case processDefinition = "processDefinition"
case processInstance = "processInstance"
case linksSelf = "self"
}
}

Also, if you feeling lazy you can use quicktype.io, As nil is a very common response from decodable, you can you an extension for DataRequest to parse and get the values from the request just like using responseDecodable, here you can use something like this,

 extension DataRequest {
    fileprivate func decodableResponseSerializer<T: Decodable>() -> DataResponseSerializer<T> {
        return DataResponseSerializer { _, response, data, error in
            guard error == nil else { return .failure(error!) }

            guard let data = data else {
                return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
            }

            return Result { try newJSONDecoder().decode(T.self, from: data) }
        }
    }

    @discardableResult
    fileprivate func responseDecodable<T: Decodable>(queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<T>) -> Void) -> Self {
        return response(queue: queue, responseSerializer: decodableResponseSerializer(), completionHandler: completionHandler)
    }
}

and then you can call it like this

Alamofire.request(url, method:.post, parameters: parameters, headers: headers)
        .responseDecodable { (response: DataResponse< Task >) in completion(response.result) }

as you were getting something like this, it only comes when you have a different type of data rather than which you have specified.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.