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
Arkit - How to Put 3D Object on Qrcode
Migrating Data to App Groups Disables Icloud Syncing
How to Create a Packed Data Structure in Swift
How to Alloc/Dealloc Unsafe Pointers in Swift
Using Apple's New Audioengine to Change Pitch of Audioplayer Sound
Conflicting Definition of Swift Struct and Array
Scenekit -- How to Get Animations for a .Dae Model
How to Implement Interstitial Iads in Swift(Xcode 6.1)
Swift .Uppercasestring or .Lowercasestring Property Replacement
What Language Is Swift Written In
Building User Database Model in Firebase
Swiftui: How to Iterate Over an Array of Bindable Objects
Xcode: Could Not Load Modelio.Framework, Scenekit.Framework, etc
Xcode 10 Beta 5 - Clang: Error: Linker Command Failed with Exit Code 1