1

I'm using an external framework for showing message list and detail screen.

Framework internal Message model which we can't modify:

public struct Message: Decodable, Encodable {
    public var id: String?
    public var title: String?
    public var subTitle: String?
    public var status: String?
}

Our API response:

{ 
   "data":{ 
      "custId":"1234",
      "type":"premium",
      "totalCount":"100",
      "msgList":[ 
         { 
            "id":"1",
            "title":"Main Title",
            "subTitle":"Sub title",
            "status":"R"
         },
         { 
            "id":"2",
            "title":"Main Title",
            "subTitle":"Sub title",
            "status":"R"
         }
      ],
      "categoryCount":"50"
   }
}

How I can extract msgList array from JSON response and decode to Message model.

Something like only passing list data/json:

let responseMessage = try JSONDecoder().decode([Message.self], from: list)

Appreciate your help and suggestion!

Thanks

Harshal Wani
  • 2,249
  • 2
  • 26
  • 41
  • *Something like only passing list data/json*. That's not possible with standard `Decodable`. You have to decode always from the top (the dictionary with key `data`) or write a custom `init` method. [quicktype.io](https://app.quicktype.io/?l=swift) can generate the structs for you. – vadian Sep 25 '19 at 09:36
  • Possible duplicate of [How to decode a nested JSON struct with Swift Decodable protocol?](https://stackoverflow.com/questions/44549310/how-to-decode-a-nested-json-struct-with-swift-decodable-protocol) – Pratik Sodha Sep 25 '19 at 10:00

4 Answers4

1

You have to create the payload structure.

struct Data: Decodable {
    struct Payload: Decodable {
        let msgList: [Message]
    }

    let data: Payload
}

Decode JSON using JSONDecoder.

let responseMessage = try JSONDecoder().decode([Message.self], from: list)

messageList can access using : responseMessage.data.msgList

Pratik Sodha
  • 3,679
  • 2
  • 19
  • 38
CZ54
  • 5,488
  • 1
  • 24
  • 39
0

It should be quite easy but you need to decode the full JSON object unless you want to start overriding the init(from decoder:) method and doing some stuff manually.

Trying to pull out just the messages array from the JSON would be more hassle than it's worth.

import UIKit

let jsonData = """
{
   "data":{
      "custId":"1234",
      "type":"premium",
      "totalCount":"100",
      "msgList":[
         {
            "id":"1",
            "title":"Main Title",
            "subTitle":"Sub title",
            "status":"R"
         },
         {
            "id":"2",
            "title":"Main Title",
            "subTitle":"Sub title",
            "status":"R"
         }
      ],
      "categoryCount":"50"
   }
}
""".data(using: .utf8)

struct Root: Decodable {
    let data: Customer
}

struct Customer: Decodable {
    let custId: String
    let type: String
    let totalCount: String
    let msgList: [Message]
}

public struct Message: Decodable, Encodable {
    public var id: String?
    public var title: String?
    public var subTitle: String?
    public var status: String?
}

do {
    let result = try JSONDecoder().decode(Root.self, from: jsonData!)
    print(result)
} catch {
    print(error)
}

You can access the messages like this:

let messages = result.data.msgList
Scriptable
  • 19,402
  • 5
  • 56
  • 72
0

Should look something like this:

// MARK: - Root
struct Root: Codable {
    let data: DataClass
}

// MARK: - DataClass
struct DataClass: Codable {
    let custID, type, totalCount: String
    let msgList: [MsgList]
    let categoryCount: String

    enum CodingKeys: String, CodingKey {
        case custID = "custId"
        case type, totalCount, msgList, categoryCount
    }
}

// MARK: - MsgList
struct MsgList: Codable {
    let id, title, subTitle, status: String
}

Then to load your data:

let list: [String : Any] = [:] // load data accordingly...
if let responseMessage = try JSONDecoder().decode(Root.self, from: list)
{
    // responseMessage.data.msgList
}

Class names are just an example, feel free to rename them accordingly.

inexcitus
  • 2,471
  • 2
  • 26
  • 41
0

You can try

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let str = """

{
   "data":{
      "custId":"1234",
      "type":"premium",
      "totalCount":"100",
      "msgList":[
         {
            "id":"1",
            "title":"Main Title",
            "subTitle":"Sub title",
            "status":"R"
         },
         {
            "id":"2",
            "title":"Main Title",
            "subTitle":"Sub title",
            "status":"R"
         }
      ],
      "categoryCount":"50"
   }
}
"""

        do {

            let res = try JSONDecoder().decode(Root.self, from:Data(str.utf8))

            print(res.list)
        }
        catch {

            print(error)
        }


    }

}

struct Root : Decodable {
    let list : [Message]
    struct AnyCodingKey : CodingKey {
        var stringValue: String
        var intValue: Int?
        init(_ codingKey: CodingKey) {
            self.stringValue = codingKey.stringValue
            self.intValue = codingKey.intValue
        }
        init(stringValue: String) {
            self.stringValue = stringValue
            self.intValue = nil
        }
        init(intValue: Int) {
            self.stringValue = String(intValue)
            self.intValue = intValue
        }
    }
    init(from decoder: Decoder) throws {
        var con = try! decoder.container(keyedBy: AnyCodingKey.self)
        con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"data"))
        let res = try! con.decode([Message].self, forKey: AnyCodingKey(stringValue:"msgList"))
        self.list = res       
    }
}


struct Message: Codable {
    let id,title,subTitle,status: String? 
}
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87