Swift 4 Codable Array'S

Swift 4 Codable Array's

Update regarding Alamofire 5: responseJSONDecodable.

struct Person: Codable {
let firstName, lastName: String
let age: Int

enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}

Alamofire.request(request).responseJSONDecodable { (response: DataResponse<Person>) in
print(response)
}

Alamofire 4 won't add Codable support for now (see #2177), you can use this extension instead: https://github.com/Otbivnoe/CodableAlamofire.

let jsonData = """
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
""".data(using: .utf8)!

struct Person: Codable {
let firstName, lastName: String
let age: Int

enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}

let decoded = try! JSONDecoder().decode([Person].self, from: jsonData)

Sample: http://swift.sandbox.bluemix.net/#/repl/59a4b4fad129044611590820

Using CodableAlamofire:

let decoder = JSONDecoder()
Alamofire.request(url).responseDecodableObject(keyPath: nil, decoder: decoder) { (response: DataResponse<[Person]>) in
let persons = response.result.value
print(persons)
}

keypath corresponds to the path where the results are contained in the JSON structure. E.g:

{
"result": {
"persons": [
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
}
}

keypath => results.persons

[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]

keypath => nil (empty keypath throws an exception)

Swift 4 JSON Codable - value returned is sometimes an object, others an array

You might encapsulate the ambiguity of the result using an Enum with Associated Values (String and Array in this case), for example:

enum MetadataType: Codable {
case array([String])
case string(String)

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
self = try .array(container.decode(Array.self))
} catch DecodingError.typeMismatch {
do {
self = try .string(container.decode(String.self))
} catch DecodingError.typeMismatch {
throw DecodingError.typeMismatch(MetadataType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload not of an expected type"))
}
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .array(let array):
try container.encode(array)
case .string(let string):
try container.encode(string)
}
}
}

struct Hotel: Codable {
let firstFloor: Room

struct Room: Codable {
var room: MetadataType
}
}

Swift 4.x Codable Array object nested inside a JSON

You should pass the right type to the json decoder. I've changer the type of some properties from double optionals, to just optionals. I added newJSONDecoder() and newJSONEncoder() since you didn't mention them in your question. And, I've declared a struct JsonResponse to make the decoding easier.

Here is what the result looks like:

let json = """
{
"photos": {
"page": 1,
"pages": 21375,
"perpage": 30,
"total": "641243",
"photo": [
{
"id": "44231730474",
"owner": "130309051@N04",
"secret": "9e12dbee3e",
"server": "1909",
"farm": 2,
"title": "Avio",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
{
"id": "44039888945",
"owner": "130309051@N04",
"secret": "7ba28027a9",
"server": "1971",
"farm": 2,
"title": "Avio",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
{
"id": "44039885865",
"owner": "130309051@N04",
"secret": "12f38bcfc2",
"server": "1929",
"farm": 2,
"title": "Tailor Made 488",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
{
"id": "44903333472",
"owner": "130309051@N04",
"secret": "dc2d36b45c",
"server": "1901",
"farm": 2,
"title": "Tailor Made 488",
"ispublic": 1,
"isfriend": 0,
"isfamily": 0
},
]
}
}
"""

let jsonData = json.data(using: .utf8)!

struct Photo: Codable {
let id, owner, secret, server: String?
let farm: Int?
let title: String?
let ispublic, isfriend, isfamily: Int?
}

struct JsonResponse: Codable {
let page: Int
let pages: Int
let perpage: Int
let total: String
let photo: [Photo]
}

func newJSONDecoder() -> JSONDecoder {
return JSONDecoder()
}

func newJSONEncoder() -> JSONEncoder {
return JSONEncoder()
}

// MARK: Convenience initializers and mutators

extension Photo {
init(data: Data) throws {
self = try newJSONDecoder().decode(Photo.self, from: data)
}

init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}

init(fromURL url: URL) throws {
try self.init(data: try Data(contentsOf: url))
}

func with(
id: String? = nil,
owner: String? = nil,
secret: String? = nil,
server: String? = nil,
farm: Int? = nil,
title: String? = nil,
ispublic: Int? = nil,
isfriend: Int? = nil,
isfamily: Int? = nil
) -> Photo {
return Photo(
id: id ?? self.id,
owner: owner ?? self.owner,
secret: secret ?? self.secret,
server: server ?? self.server,
farm: farm ?? self.farm,
title: title ?? self.title,
ispublic: ispublic ?? self.ispublic,
isfriend: isfriend ?? self.isfriend,
isfamily: isfamily ?? self.isfamily
)
}

func jsonData() throws -> Data {
return try newJSONEncoder().encode(self)
}

func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
return String(data: try self.jsonData(), encoding: encoding)
}

func flickrURL() -> String{
return "https://farm\(farm!).staticflickr.com/\(server!)/\(id!)_\(secret!).jpg"
}

static func photosFromApi(data: Data) -> [Photo]? {
do {
let decodedData = try newJSONDecoder().decode([String:JsonResponse].self, from: data)
if let photos = decodedData.values.first {
return photos.photo
} else {
return []
}
} catch let error{
print("error decoding \(error)")
return nil
}
}
}

Photo.photosFromApi(data: jsonData)

Filter an (Codable) array by another array

You need to do that filter for each id in the favourites array. You get an array of arrays as a result. To get the final array, you need to join those arrays to a single array. This "map each thing to an array and join the arrays" operation is what a flatMap does:

workoutFav.flatMap { favouriteId in // for each favourite ID
jsonErgWorkouts.filter { $0.id == favouriteId } // find workouts that match the ID
} // flatMap joins all those arrays returns by "filter" together, no need to do anything else

Codable Handle Single or Array

Do this

struct Root  : Codable {

let data:[DItem]

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
data = try container.decode([DItem].self, forKey: .data)
}
catch {
let res = try container.decode(DItem.self, forKey: .data)
data = [res]
}

}
}

struct DItem: Codable {
let name,age:String
}

let res = try JSONDecoder().decode(Root.self, from:data)

until you fix the response

add json array with object to Codable model in swift

When you are using codable you don't need to do the Array<String> and stuff like that. You can simply make an another decodable and codable and achieve it.

struct ProductModel: Decodable{
var image_galleries: [Galleries]
let id: String
let status: Int
}

struct Galleries: Decodable {
let image_url: String
}

Also, instead of writing image_galleries, you should use imageGalleries and when you are decoding, you can use .convertFromSnakeCase, it is a key decoding strategy as in Swift we follow camelCase instead of snake_case.

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {

let json = try decode.decode(ProducModel.self, from: data)
} catch let error {
print(error)
}

How to make codable class to decode json array which doesn't have a key/name?

Delete UserWalletHistoryList

struct UserWalletHistoryList: Codable {
var historyList: [UserWalletHistory]
}

and decode an array of UserWalletHistory

JSONDecoder().decode([UserWalletHistory].self, from: data)

and as the JSON provides all keys in all dictionaries declare the struct members as non-optional and add CodingKeys to map the uppercase keys to lowercase member names

struct UserWalletHistory : Codable {
let action: String
let status: Int
let transactionDate: String
let description: String

private enum CodingKeys : String, CodingKey {
case action = "Action"
case status = "Status"
case transactionDate = "TransactionDate"
case description = "Description"
}
}


Related Topics



Leave a reply



Submit