Swift ObjectMapper: How to parse array inside of an array - ios

This is my JSON response:
[
[
{
"id": 22,
"request_id": "rqst5c12fc9e856ae1.06631647",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Apache Load/Ubuntu",
}
],
[
{
"id": 24,
"request_id": "rqst5c130cae6f7609.41056231",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
}
]
]
This JSON structure got an array inside of an array, the object of the inner array is what I am trying to parse. Here is the my mapper:
struct JobResponseDataObject: Mappable {
init?(map: Map) {
}
var id: Int?
var requestId: String?
var businessName: String?
var businessEmail: String?
mutating func mapping(map: Map) {
id <- map["id"]
requestId <- map["request_id"]
businessName <- map["business_name"]
businessEmail <- map["business_email"]
}
}
I have tried create another mapper struct to hold the array of objects [JobResponseDataObject] and use Alamofire's responseArray with it, but it didn't work. I have also tried prefixing my json id with 0. but that didn't work too. Please help
Thank

So here's the deal...Codable is a pretty cool protocol from Apple to handle parsing JSON responses from APIs. What you're getting back is an array of arrays, so your stuff's gonna be look like this:
[[ResponseObject]]
So anyway, you'd make a struct of your object, like so:
struct ResponseObject: Codable {
let id: Int?
let requestId: String?
let businessName: String?
let businessEmail: String?
let title: String?
}
You'll note I changed the key name a bit (instead of request_id, I used requestId). The reason is JSONDecoder has a property called keyDecodingStrategy which presents an enum of canned decoding strategies you can select from. You'd do convertFromSnakeCase.
Here's code you can dump into a playground to tinker with. Basically, declare your struct, match it up to whatever the keys are in your JSON, declare a decoder, feed it a decoding strategy, and then decode it.
Here's how you could do an Alamofire call:
private let backgroundThread = DispatchQueue(label: "background",
qos: .userInitiated,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil)
Alamofire.request(url).responseJSON(queue: backgroundThread) { (response) in
guard response.result.error == nil else {
print("💥KABOOM!💥")
return
}
if let data = response.data {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let parsedResponse = try decoder.decode([[ResponseObject]].self, from: data)
print(parsedResponse)
} catch {
print(error.localizedDescription)
}
}
}
Here's code you can chuck in a playground.
import UIKit
let json = """
[
[
{
"id": 22,
"request_id": "rqst5c12fc9e856ae1.06631647",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Apache Load/Ubuntu",
}
],
[
{
"id": 24,
"request_id": "rqst5c130cae6f7609.41056231",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
}
]
]
"""
struct ResponseObject: Codable {
let id: Int?
let requestId: String?
let businessName: String?
let businessEmail: String?
let title: String?
}
if let data = json.data(using: .utf8) {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let parsedResponse = try decoder.decode([[ResponseObject]].self, from: data)
print(parsedResponse)
} catch {
print(error.localizedDescription)
}
}

You should use this JobResponseDataObject struct as [[JobResponseDataObject]] instead of [JobResponseDataObject] - where you are making a property using this struct in your parent struct or class.

You can use Codable here for mapping the JSON response, The JobResponseDataObject struct should look like,
struct JobResponseDataObject: Codable {
var id: Int?
var requestId: String?
var businessName: String?
var businessEmail: String?
var title: String?
private enum CodingKeys: String, CodingKey {
case id = "id"
case requestId = "request_id"
case businessName = "business_name"
case businessEmail = "business_email"
case title = "title"
}
}
let json = JSON(responseJSON: jsonData)
do {
if let value = try? json.rawData(){
let response = try! JSONDecoder().decode([[JobResponseDataObject]].self, from: value)
}
} catch {
print(error.localizedDescription)
}

Related

How to use Alamofire with Codable protocol and parse data

Firs time using Codable protocol so many things not clear. After going through several tutorials started with the Codable thing to parse data. Below is the code:
struct MyTasks : Codable {
var strDateDay : String = ""
var strReminder : String = ""
var strRepetitions : String = ""
var name : String = ""
var strNotes : String = ""
var strUserId : String = ""
private enum CodingKeys: String, CodingKey {
case strDateDay = "duedate"
case strReminder = "reminder"
case strRepetitions = "recurring"
case name = "name"
case strNotes = "notes"
case strUserId = "userId"
}
}
let networkManager = DataManager()
networkManager.postLogin(urlString: kGetMyTasks, header: header, jsonString: parameters as [String : AnyObject]) { (getMyTasks) in
print(getMyTasks?.name as Any) // -> for log
Common_Methods.hideHUD(view: self.view)
}
// Network manager:
func postLogin(urlString: String, header: HTTPHeaders, jsonString:[String: AnyObject], completion: #escaping (MyTasks?) -> Void) {
let apiString = KbaseURl + (kGetMyTasks as String)
print(apiString)
Alamofire.request(apiString, method: .post, parameters: jsonString , encoding: URLEncoding.default, headers:header).responseJSON
{ response in
let topVC = UIApplication.shared.keyWindow?.rootViewController
DispatchQueue.main.async {
//Common_Methods.showHUD(view: (topVC?.view)!)
}
guard let data = response.data else { return }
do {
let decoder = JSONDecoder()
let loginRequest = try decoder.decode(MyTasks.self, from: data)
completion(loginRequest)
} catch let error {
print(error)
completion(nil)
}
}
}
Now, this is the response:
keyNotFound(CodingKeys(stringValue: "strDateDay", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"strDateDay\", intValue: nil) (\"strDateDay\").", underlyingError: nil))
Below is the json response i am trying to parse:
{
"data": [
{
"userId": 126,
"name": "My task from postman ",
"notes": null,
"totalSteps": 0,
"completedSteps": 0,
"files": 0
},
{
"userId": 126,
"name": "My task from postman 1",
"notes": null,
"totalSteps": 0,
"completedSteps": 0,
"files": 0
}
]
}
I know i am missing something but even after spending more than half day i haven't properly understood what is wrong and where. Please guide.
problem is with your struct the names of the properties in the struct should be match with json data or if you want to use custom name you should use enum CodingKeys to convert them to your liking
struct MyTasks: Codable {
let data: [Datum]
}
struct Datum: Codable {
let userID: Int?
let name: String
let notes: String?
let totalSteps, completedSteps, files: Int
enum CodingKeys: String, CodingKey {
case userID = "userId"
case name, notes, totalSteps, completedSteps, files
}
}
func postLogin(urlString: String, header: HTTPHeaders, jsonString:[String: AnyObject], completion: #escaping (MyTasks?) -> Void) {
let apiString = KbaseURl + (kGetMyTasks as String)
print(apiString)
Alamofire.request(apiString, method: .post, parameters: jsonString , encoding: URLEncoding.default, headers:header).responseJSON
{ response in
let topVC = UIApplication.shared.keyWindow?.rootViewController
DispatchQueue.main.async {
//Common_Methods.showHUD(view: (topVC?.view)!)
}
guard let data = response.data else { return }
do {
let decoder = JSONDecoder()
let loginRequest = try decoder.decode(MyTasks.self, from: data)
completion(loginRequest)
} catch let error {
print(error)
completion(nil)
}
}
}
And one more thing keep in mind that you know the exact type of notes and make it optional otherwise it rise error, there was no type so I put a optional String in there.
Hope this will help.
The problem is that in your top JSON you have a single "data" property of type array.
You are asking JSONDecoder to decode a JSON object containing only "data" property into a Swift object called "MyTasks" with the stored properties you defined (including strDateDay).
So the decoder sends you this response because he can't find the strDateDay in that top object.
You have to make a top object for deserialization. Something like :
struct MyResponse: Codable {
var data: [MyTasks]
}
Then just give it to your JSONDecoder like you have already done :
let loginRequest = try decoder.decode(MyResponse.self, from: data)
The data you send to the decoder is your full JSON stream (the data property of Alamofire's response object), and not only the "data" property of your JSON structure.
I'd suggest to use CodyFire lib cause it support Codable for everything related to requests.
Your POST request with it may look like
struct MyTasksPayload: JSONPayload {
let param1: String
let param2: String
}
struct MyTasksResponseModel: Codable {
let strDateDay: String
let strReminder: String
let strRepetitions: String
let name: String
}
let server = ServerURL(base: "https://server1.com", path: "v1")
APIRequest<[MyTasksResponseModel]>(server, "mytasks", payload: payloadModel)
.method(.post)
.onRequestStarted {
// show HUD here
}
.onError {
// hide hud and show error here
}
.onSuccess { tasks in
// here's your decoded tasks
// hide hud here
}
Use APIRequest<[MyTasksResponseModel]> to decode array
Use APIRequest to decode one object

How to parse nested json? Swift

i have a local json string and i am trying to parse it but when i try to do so, i am constantly getting an error. I have also seen this error in nested dictionary but couldnt find an error.
Below is the json string
let jsonNestedString = "{\"meta\":{\"page\":1,\"total_pages\":4,\"per_page\":10,\"total_records\" : 38}, \"reweries\":[\"id\":1234,\"name\":\"saintArnold\"},{\"id\":52892,\"name\":\"buffalo bayou\"]}"
i am doing this process via Codable and below is the struct i have created for the same
struct PagedBreweries:Codable{
struct Meta:Codable{
let page : Int
let total_pages:Int
let per_page:Int
let total_records: Int
enum CodingKeys: String, CodingKey{
case page
case total_pages
case per_page
case total_records
}
}
struct Brewery :Codable{
let id:Int
let name:String
}
let meta:Meta
let breweries :[Brewery]
}
then this data is parsed to a function as below
func jsonNested(){
let jsonData = jsonNestedString.data(using: .utf8)
let decoder = JSONDecoder()
let data = try! decoder.decode(PagedBreweries.Meta.self, from: jsonData!)
print(data)
}
and when i try to build, the error i get is present in try! decoder.decode
command
and the error is
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "page", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"page\", intValue: nil) (\"page\").", underlyingError: nil))
Could any one provide a solution?
Thanks in advance
Correct json
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"reweries": [{
"id": 1234,
"name": "saintArnold"
},
{
"id": 52892,
"name": "buffalo bayou"
}
]
}
struct Root: Codable {
let meta: Meta
let reweries: [Rewery]
}
struct Meta: Codable {
let page, totalPages, perPage, totalRecords: Int
enum CodingKeys: String, CodingKey { // snake case may be used
case age = "page"
case totalPages = "total_pages"
case perPage = "per_page"
case totalRecords = "total_records"
}
}
struct Rewery: Codable {
let id: Int
let name: String
}
let jsonNestedString = """
{\"meta\":{\"page\":1,\"total_pages\":4,\"per_page\":10,\"total_records\":38}, \"reweries\":[{\"id\":1234,\"name\":\"saintArnold\"},{\"id\":52892,\"name\":\"buffalo bayou\"}]}
"""
// or
let jsonNestedString = """
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"reweries": [{
"id": 1234,
"name": "saintArnold"
},
{
"id": 52892,
"name": "buffalo bayou"
}
]
}
"""
do {
let jsonData = jsonNestedString.data(using: .utf8)
let decoder = JSONDecoder()
let data = try decoder.decode(Root.self, from: jsonData!)
print(data)
} catch {
print(error)
}
The JSON is corrupt and you are decoding the wrong root object.
This is the correct JSON being decoded with your structs and conforming to the Swift naming convention
let jsonNestedString = """
{"meta":{"page":1,"total_pages":4,"per_page":10,"total_records" : 38}, "breweries":[{"id":1234,"name":"saintArnold"},{"id":52892,"name":"buffalo bayou"}]}
"""
struct PagedBreweries : Codable {
struct Meta : Codable {
let page : Int
let totalPages:Int
let perPage:Int
let totalRecords: Int
}
struct Brewery : Codable {
let id:Int
let name:String
}
let meta:Meta
let breweries :[Brewery]
}
func jsonNested(){
let jsonData = Data(jsonNestedString.utf8)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let data = try! decoder.decode(PagedBreweries.self, from: jsonData)
print(data)
}
jsonNested()
// Printed result:
// PagedBreweries(meta: __lldb_expr_1.PagedBreweries.Meta(page: 1, totalPages: 4, perPage: 10, totalRecords: 38), breweries: [__lldb_expr_1.PagedBreweries.Brewery(id: 1234, name: "saintArnold"), __lldb_expr_1.PagedBreweries.Brewery(id: 52892, name: "buffalo bayou")])

How to use Decodable in Swift?

I am using a free dates API in my project. I am using Decodable to parse the JSON data.
Here I created my struct:-
struct jsonStruct: Decodable {
var message: Bool?
var data: [dateData]
}
struct dateData: Decodable {
var quarter: Int?
var day: String?
var month: String?
}
This is my code to use the decoder:-
let jsonUrlString = "https://api.lrs.org/random-date-generator?lim_quarters=40&source=api-docs"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, reponse, err) in
guard let data = data else { return }
print(data)
do {
let jsonData = try JSONDecoder().decode([dateData].self, from: data)
print(jsonData)
}
catch let jsonerr {
print("error serrializing error",jsonerr)
}
}.resume()
But I am getting an error in my code. It goes in the catch block only and I am getting this error in my console:-
error serrializing error typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
I don't understand what I am doing wrong in my code.
API Data:-
{
messages: false,
data: {
2018-01-02: {
quarter: 1,
day: "2",
month: "1",
db: "2018-01-02",
long: "Tuesday, January 2nd, 2018",
unix: 1514876400
},
struct Job: Decodable {
var title: String
var salary: Float
init(title: String, salary: Float) {
self.title = title
self.salary = salary
}
enum CodingKeys: String, CodingKey {
case title, salary
}
}
struct Person: Decodable {
var job: Job
var firstName: String
var lastName: String
var age: Int
init(job: Job, firstName: String, lastName: String, age: Int) {
self.job = job
self.firstName = firstName
self.lastName = lastName
self.age = age
}
enum CodingKeys: String, CodingKey {
case job = "job_information", firstName = "firstname", lastName =
"lastname", age
}
}
let rawData = """
{
"job_information": {
"title": "iOS Developer",
"salary": 5000
},
"firstname": "John",
"lastname": "Doe",
"age": 20
}
""".data(using: .utf8)!
let person = try JSONDecoder().decode(Person.self, from: rawData)
print(person.firstName) // John
print(person.lastName) // Doe
print(person.job.title) // iOS Developer
You need
struct Root: Codable {
let messages: Bool
let data: [String: Datum]
}
struct Datum: Codable {
let quarter: Int
let day, month, db, long: String
let unix: Int
}
let jsonData = try JSONDecoder().decode(Root.self, from: data)
print(jsonData.data.values)
As the root of the json is a dictionary not an array , also data is a dictionary
jsonData.data.forEach {
if $0 == " 2018-01-02" {
print($1.month)
}
}

Decoding/parsing specific JSON with Swift

I am having a lot of trouble trying to decode this JSON with Swift 4.
{
"Items": [
{
"id": 1525680450507,
"animal": "bee",
"type": "insect",
"diet": [
"a",
"b",
"c"
]
}
],
"Count": 1,
"ScannedCount": 5
}
Here's where I try to decode
let decoder = JSONDecoder()
let data = try decoder.decode([Animal].self, from: data)
I have created a struct like this
struct Animal: Codable {
var id: Int
var animal: String
var type: String
var diet: [String]
}
let decoder = JSONDecoder()
let data = try decoder.decode(ItemsResponse.self, from: data)
This doesn't work. I get an error that says
"Expected to decode Array<\Any> but found a dictionary instead."
So I thought maybe I needed something like this
struct ItemsResponse: Codable {
var Items: [Animal]
var Count: Int
var ScannedCount: Int
}
But this doesn't work either. Now I get
"Expected to decode Array<\Any> but found a string/data instead."
How do I make a struct that will decode this JSON?
let data = try decoder.decode([Animal].self, from: data)
[Animal].self is not correct you can use it like this :
struct DataJson: Codable {
let items: [Item]
let count, scannedCount: Int
enum CodingKeys: String, CodingKey {
case items = "Items"
case count = "Count"
case scannedCount = "ScannedCount"
}
}
struct Item: Codable {
let id: Int
let animal, type: String
let diet: [String]
}
// MARK: Convenience initializers
extension DataJson {
init(data: Data) throws {
self = try JSONDecoder().decode(DataJson.self, from: data)
}
func jsonData() throws -> Data {
return try JSONEncoder().encode(self)
}
func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
return String(data: try self.jsonData(), encoding: encoding)
}
}
extension Item {
init(data: Data) throws {
self = try JSONDecoder().decode(Item.self, from: data)
}
func jsonData() throws -> Data {
return try JSONEncoder().encode(self)
}
func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
return String(data: try self.jsonData(), encoding: encoding)
}
}
Try this:
import Foundation
let json = """
{
"Items": [
{
"id": 1525680450507,
"animal": "bee",
"type": "insect",
"diet": [
"a",
"b",
"c"
]
}
],
"Count": 1,
"ScannedCount": 5
}
"""
struct Animal: Codable {
var id: Int
var animal: String
var type: String
var diet: [String]
}
struct ItemsResponse: Codable {
var Items: [Animal]
var Count: Int
var ScannedCount: Int
}
let data = try! JSONDecoder().decode(ItemsResponse.self, from: json.data(using: .utf8)!)
Of course, you should properly handle the possible failures (i.e. don't do try!, and don't force-unwrapp the json.data()! part)
But the code above works and hopefully answers your question.

Parse Complex Nested Data From server

I'm trying to parse data i've received from the server so as to display it into a UIPicker view. but it's too complex for me to parse and get it displayed into UIPIcker View. Whats the best way i can parse the following data into and make it ready for a UIPickerView.
This is the session trying to parse the information from the server
let url = NSURL(string: "http://dummy.com/api")!
let request = URLRequest(url: url as URL)
URLSession.shared.dataTask(with: request) { data, response, error in
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
guard let parseJSON = json else{
print("Error While Parsing")
return
}
print(parseJSON)
let responseDic = parseJSON["response"] as? NSDictionary
let utilityCode = responseDic?["utility_code"] as? String
if utilityCode == "AFRICELL" {
let africellPackages = responseDic?["packages"] as! NSArray
print(africellPackages)
}
}catch{
return
}
}
}.resume()
The following data is the response from the server when the GET request is made.
{
"status": "OK",
"response": [
{
"utility_code": "AIRTEL",
"packages": [
{
"package_id": 33,
"package_name": "Daily 10MB",
"package_code": "6000",
"package_price": 300
},
{
"package_id": 34,
"package_name": "Daily 20MB",
"package_code": "6002",
"package_price": 500
},
{
"package_id": 65,
"package_name": "Weekly Roaming 200MB",
"package_code": "6030",
"package_price": 100000
}
]
},
{
"utility_code": "AFRICELL",
"packages": [
{
"package_id": 68,
"package_name": "Daily 10 MB",
"package_code": "5000",
"package_price": 290
}
]
},
{
"utility_code": "SMART",
"packages": [
{
"package_id": 69,
"package_name": "Daily 50 MB",
"package_code": "8000",
"package_price": 500
}
]
},
{
"utility_code": "SMILE",
"packages": [
{
"package_id": 70,
"package_name": "Smile 1GB",
"package_code": "7006",
"package_price": 32000
}
]
}
]
}
Cheers and thanks for the help!
All examples below are provided without error checking for brevity. For production code, you should handle errors properly.
Swift 3 without any external framework
Most of the work to decode JSON manually involves defining the data model:
struct JSONResponse {
var status: String
var response: [Utility]
init(jsonDict: [String: AnyObject]) {
self.status = jsonDict["status"] as! String
self.response = [Utility]()
let response = jsonDict["response"] as! [AnyObject]
for r in response.map({ $0 as! [String: AnyObject] }) {
let utility = Utility(jsonDict: r)
self.response.append(utility)
}
}
}
struct Utility {
var code: String
var packages: [Package]
init(jsonDict: [String: AnyObject]) {
self.code = jsonDict["utility_code"] as! String
self.packages = [Package]()
let packages = jsonDict["packages"] as! [AnyObject]
for p in packages.map({ $0 as! [String: AnyObject] }) {
let package = Package(jsonDict: p)
self.packages.append(package)
}
}
}
struct Package {
var id: Int
var name: String
var code: String
var price: Int
init(jsonDict: [String: AnyObject]) {
self.id = jsonDict["package_id"] as! Int
self.name = jsonDict["package_name"] as! String
self.code = jsonDict["package_code"] as! String
self.price = jsonDict["package_price"] as! Int
}
}
And how to use it:
let jsonDict = try! JSONSerialization.jsonObject(with: data) as! [String: AnyObject]
let jsonResponse = JSONResponse(jsonDict: jsonDict)
let utilities = jsonResponse.response
print(utilities)
Swift 3 with ObjectMapper
Decoding JSON is it of a pain in standard Swift. You need an external JSON framework like ObjectMapper to ease the pain in mapping between the JSON data and your data model. Install ObjectMapper from CocoaPod or Carthage.
First, define your data model in a separate file:
import ObjectMapper
struct JSONResponse : Mappable {
var status: String?
var response: [Utility]?
init?(map: Map) { }
mutating func mapping(map: Map) {
self.status <- map["status"]
self.response <- map["response"]
}
}
struct Utility : Mappable {
var code: String?
var packages: [Package]?
init?(map: Map) { }
mutating func mapping(map: Map) {
self.code <- map["code"]
self.packages <- map["packages"]
}
}
struct Package : Mappable {
var id: Int?
var name: String?
var code: String?
var price: Int?
init?(map: Map) { }
mutating func mapping(map: Map) {
self.id <- map["package_id"]
self.name <- map["package_name"]
self.code <- map["package_code"]
self.price <- map["package_price"]
}
}
Then you can use it to map JSON to your object like this:
// data is what your get in the dataTask's completion block
let jsonString = String(data: data, encoding: .utf8)!
if let jsonResponse = JSONResponse(JSONString: jsonString),
let utilities = jsonResponse.response {
print(utilities)
}
Everything has to be declared optional because you don't know if the value will be in the JSON string or not.
In Swift 4
Things get a lot simpler in Swift 4 with the new Encodable and Decodable protocols. Both combined to form the Encodable protocol (yes, protocol composition is a new thing in Swift 4). You no longer need ObjectMapper in Swift 4.
You still need to define your data model first:
struct JSONResponse : Codable {
var status: String
var response: [Utility]
}
struct Utility : Codable {
var code: String
var packages: [Package]
private enum CodingKeys: String, CodingKey {
case code = "utility_code"
case packages
}
}
struct Package : Codable {
var id: Int
var name: String
var code: String
var price: Int
private enum CodingKeys: String, CodingKey {
case id = "package_id"
case name = "package_name"
case code = "package_code"
case price = "package_price"
}
}
And here's how you use the new JSONDecoder struct in Swift 4:
let jsonRepsonse = try! JSONDecoder().decode(JSONResponse.self, from: data)
let utilities = jsonRepsonse.response
print(utilities)
So what's going on here?
Both ObjectMapper and Codable map the values in JSON to your data model.
In ObjectMapper, you have to explicitly list which property maps to which value in JSON in a function named mapping(map:):
mutating func mapping(map: Map) {
self.id <- map["package_id"]
self.name <- map["package_name"]
self.code <- map["package_code"]
self.price <- map["package_price"]
}
<- is the "mapping operator" defined by ObjectMapper (it's not in the Standard Library).
With Swift 4's Codable, the compiler automates a lot of things for you, which makes it appear magical and confusing at first. Essentially you define your mappings in an enum called CodingKeys:
struct JSONResponse : Codable {
var status: String
var response: [Utility]
// Define your JSON mappings here
private enum CodingKeys: String, CodingKey {
case status = "status"
case response = "response"
}
}
struct Package : Codable {
var id: Int
var name: String
var code: String
var price: Int
// Define your JSON mappings here
private enum CodingKeys: String, CodingKey {
case id = "package_id"
case name = "package_name"
case code = "package_code"
case price = "package_price"
}
}
If your JSON keys are the same as your property name, like in the JSONResponse struct, you don't have to define the explicit value for each case and you can simply write:
struct JSONResponse : Codable {
var status: String
var response: [Utility]
private enum CodingKeys: String, CodingKey {
case status // no explicit value here
case response
}
}
Better yet, since every JSON key is the same as your property name, you can omit the CodingKeys enum all together and let the compiler handle that for you:
// All you need to do is to conform it to Codable
// CodingKeys is generated automatically
struct JSONResponse : Codable {
var status: String
var response: [Utility]
}
To learn more about Codable, watch the What's new in Foundation session from WWDC 2017.

Resources