5

Here is struct codable code:

import Foundation

public struct TaskID: Codable {
    let embedded: Embedded
}

public struct Embedded: Codable {
    let task: [Task]
}

public struct Task : Codable {

    let embedded: EmbeddedVariable
    let id : String
    let name: String
    let assignee: String
    let created: String
    let processDefinitionId: String
}

public struct EmbeddedVariable: Codable {

    let variable : [Variables]
}

public struct Variables: Codable {

    let value : String
    let name: String
}

I have tried by codingKey and also tried using _embedded too. Face same issue.

Error log: Response could not be decoded because of error:

 Alamofire.AFError.
 ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil)))))

The data couldn’t be read because it isn’t in the correct format.

Here is the code for JSONSerialization:

      // 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 the code json return response:

   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<MyTaskData>)->Void) {
    
    performRequest(route: APIRouter.TaskById(id: id), completion: completion)
}


}//APIClient
PvUIDev
  • 95
  • 1
  • 9
  • 38

4 Answers4

6

In your JSON payload,There is a extra comma at end of the value for key processDefinitionId.

Try this JSON Formatter tool to validate the JSON: jsonformatter

"task":[
        {
         //...

         "id": "412a2aca-06ae-11ea-8860-120ef5ab2c25",
         "name": "Quick Evaluation",
         "assignee": "demo",
         "created": "2019-11-14T07:13:27.558+0000",
         "processDefinitionId": "quickEvaluation:1:129ce2b1-0616-11ea-8860-120ef5ab2c25", // remove this coma(,) from this line

        }

Update:

Use CodingKey for the _embedded. Try the following way

// MARK: - TaskID
struct TaskID: Codable {
    let embedded: Embedded
    let count: Int

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

// MARK: - Embedded
struct Embedded: Codable {
    let task: [Task]
}

// MARK: - Task
struct Task: Codable {
    let embedded: EmbeddedVariable
    let id, name, assignee, created: String
    let processDefinitionID: String

    enum CodingKeys: String, CodingKey {
        case embedded = "_embedded"
        case id, name, assignee, created
        case processDefinitionID = "processDefinitionId"
    }
}

// MARK: - EmbeddedVariable
struct EmbeddedVariable: Codable {
    let variable: [Variable]
}

// MARK: - Variable
struct Variable: Codable {
    let links: Links
    let name, value, type: String
    let valueInfo: ValueInfo

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

// MARK: - Links
struct Links: Codable {
    let linksSelf: SelfClass

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

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

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

Jakir Hossain
  • 3,830
  • 1
  • 15
  • 29
  • still facing same error Response could not be decoded because of error: The data couldn’t be read because it isn’t in the correct format. framework response:: failure(Alamofire.AFError.responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary but found an array instead.", underlyingError: nil))))) – PvUIDev Nov 15 '19 at 09:17
  • here is my full json response data https://drive.google.com/file/d/1rwl0KHbWHpq7c6x274M6dX9o1hpDGSL5/view?usp=sharing – PvUIDev Nov 15 '19 at 09:24
  • https://drive.google.com/file/d/1rwl0KHbWHpq7c6x274M6dX9o1hpDGSL5/view?usp=sharing use this – PvUIDev Nov 15 '19 at 09:26
  • gaved drive url access – PvUIDev Nov 15 '19 at 09:33
  • in your JSON response, there is no value in any `variable[]`. so i recommend you try by removing your `EmbeddedVariable`. – Jakir Hossain Nov 15 '19 at 10:07
4

How about changing all embeded to _embeded in codable struct?

In your APIClient class, JSONDecoder instance created with default. and it doesn't change key names during decoding include underscore.

The JSONDecoder.KeyDecodingStrategy.useDefaultKeys strategy is the strategy used if you don't specify one. Documentation from Apple

Code

public struct TaskID: Codable {
    let _embedded: Embedded
}

public struct Task : Codable {

    let _embedded: EmbeddedVariable
    let id : String
    let name: String
    let assignee: String
    let created: String
    let processDefinitionId: String
}
Makeeyaf
  • 96
  • 2
  • Thanks for answering. i found answer, its because of header accept type. From backend they are using accept - application/hal+json. here i have used application/json. how can i use application/hal+json based on my code. specifically for particular post method alone they are using application/hal+json remaining application/json are used. – PvUIDev Nov 20 '19 at 04:07
3

try use QuickType.io

// MARK: - Welcome
struct Welcome: Codable {
    let embedded: WelcomeEmbedded
    let count: Int

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

To parse values from Alamofire responses:

   Alamofire.request(url).responseWelcomeEmbedded { response in
     if let welcomeEmbedded = response.result.value {
       ...
     }
   }
// MARK: - WelcomeEmbedded
struct WelcomeEmbedded: Codable {
    let task: [Task]
}

To parse values from Alamofire responses:

   Alamofire.request(url).responseTask { response in
     if let task = response.result.value {
       ...
     }
   }
// MARK: - Task
struct Task: Codable {
    let embedded: TaskEmbedded
    let id, name, assignee, created: String
    let processDefinitionID: String

    enum CodingKeys: String, CodingKey {
        case embedded = "_embedded"
        case id, name, assignee, created
        case processDefinitionID = "processDefinitionId"
    }
}

To parse values from Alamofire responses:

   Alamofire.request(url).responseTaskEmbedded { response in
     if let taskEmbedded = response.result.value {
       ...
     }
   }
// MARK: - TaskEmbedded
struct TaskEmbedded: Codable {
    let variable: [Variable]
}

To parse values from Alamofire responses:

   Alamofire.request(url).responseVariable { response in
     if let variable = response.result.value {
       ...
     }
   }
// MARK: - Variable
struct Variable: Codable {
    let links: Links
    let embedded: JSONNull?
    let name, value, type: String
    let valueInfo: ValueInfo

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

To parse values from Alamofire responses:

   Alamofire.request(url).responseLinks { response in
     if let links = response.result.value {
       ...
     }
   }
// MARK: - Links
struct Links: Codable {
    let linksSelf: SelfClass

    enum CodingKeys: String, CodingKey {
        case linksSelf = "self"
    }
}
To parse values from Alamofire responses:

   Alamofire.request(url).responseSelfClass { response in
     if let selfClass = response.result.value {
       ...
     }
   }
// MARK: - SelfClass
struct SelfClass: Codable {
    let href: String
}

To parse values from Alamofire responses:

   Alamofire.request(url).responseValueInfo { response in
     if let valueInfo = response.result.value {
       ...
     }
   }
// MARK: - ValueInfo
struct ValueInfo: Codable {
}

// MARK: - Helper functions for creating encoders and decoders

func newJSONDecoder() -> JSONDecoder {
    let decoder = JSONDecoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        decoder.dateDecodingStrategy = .iso8601
    }
    return decoder
}

func newJSONEncoder() -> JSONEncoder {
    let encoder = JSONEncoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        encoder.dateEncodingStrategy = .iso8601
    }
    return encoder
}

// MARK: - Alamofire response handlers

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)
    }

    @discardableResult
    func responseWelcome(queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<Welcome>) -> Void) -> Self {
        return responseDecodable(queue: queue, completionHandler: completionHandler)
    }
}
Erez Mizrahi
  • 197
  • 6
  • Thanks for answering. i found answer, its because of header accept type. From backend they are using accept - application/hal+json. here i have used application/json. how can i use application/hal+json based on my code. specifically for particular post method alone they are using application/hal+json remaining application/json are used. – PvUIDev Nov 20 '19 at 04:06
  • if your are using urlRequest : urlRequest.addValue("application/hal+json", forHTTPHeaderField: "Content-Type") Alamofire: let headers: HTTPHeaders = [ "Content-Type": "application/hal+json" ] Alamofire.request("https://mashape-community-urban-dictionary.p.mashape.com/define?term=smh", headers: headers) .responseJSON { response in debugPrint(response) } – Erez Mizrahi Nov 21 '19 at 08:35
1

Here is a basic Alamofire request. There should be no error if you use the following structure parsing json.

    Alamofire.request(url, headers: headers).responseJSON { response in
        switch response.result {
        case .success(let value):
            let jsonData = value as! NSDictionary
            completionHandler(jsonData, nil)
            print("****** JSON \(jsonData)")
        case .failure(let error):
            completionHandler(nil, error)
        }

Here I have placed it inside of your code. You should try the headers like I have shown below. You need to fill in your appropriate headers.

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)

    let headers: HTTPHeaders = [
        "Content-Type": "application/json",
        "Authorization": "Basic " + currentToken
    ]

    Alamofire.request(url, headers: headers).responseJSON { response in
        switch response.result {
        case .success(let value):
            let jsonData = value as! NSDictionary
            completionHandler(jsonData, nil)
            print("****** JSON \(jsonData)")
        case .failure(let error):
            completionHandler(nil, error)
        }

//return urlRequest
}

Hope This helps

Julian Silvestri
  • 1,970
  • 1
  • 15
  • 33
  • Thanks for answering. i found answer, its because of header accept type. From backend they are using accept - application/hal+json. here i have used application/json. how can i use application/hal+json based on my code. specifically for particular post method alone they are using application/hal+json remaining application/json are used. – PvUIDev Nov 20 '19 at 04:06
  • Where it says in my answers (inside the headers) set the value of content-type to application/hal+JSON – Julian Silvestri Nov 20 '19 at 04:08