Deserialize a Dictionary (JSON) to a swift object - json

I'm trying to build a login function (POST method) and the resultant is a JSON with user details and few other details. I have created a class with all the fields I need to use from the result of POST call. But I'm facing an issue with deserialzing the json to the object of the class. Can some one help me with this. (I have seen similar questions on SO and tried solving using the solution. I have tried converting the json to string and then to swift object using var UserDetails = UserDetails(json:jsonString)
)
My code:
class UserDetails {
let token:String
let agent_id: Int
let user_id:Int
let company_id:Int
let affliate_Id:Int
let role_id:Int
let username: String
let surname:String
let lastname:String
init(token:String,agent_id: Int,user_id:Int,company_id:Int,affliate_Id:Int,role_id:Int,username: String,surname:String,lastname:String) {
self.token = token;
self.agent_id = agent_id;
self.user_id = user_id;
self.company_id = company_id;
self.affliate_Id = affliate_Id;
self.role_id = role_id;
self.username = username;
self.surname = surname;
self.lastname = lastname;
} }
My controller class:
let task = session.dataTask(with: request as URLRequest) { data, response, error in
guard data != nil else {
print("no data found: \(error)")
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
NSLog("Login SUCCESS");
let prefs:UserDefaults = UserDefaults.standard
prefs.set(username, forKey: "USERNAME")
prefs.set(udid, forKey: "UDID")
prefs.synchronize()
print("Response: \(json)")
//var jsonString = NSString(data: json, encoding: String.Encoding.utf8)! as String
//when I tried to do the above statement, an error is thrown. Cannot convert value of type NSDictionary to expected argument type Data
//var person:UserDetails = UserDetails(json: jsonString)
self.dismiss(animated: true, completion: nil)
} else {
let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)// No error thrown, but not NSDictionary
print("Error could not parse JSON: \(jsonStr)")
}
} catch let parseError {
print(parseError)// Log the error thrown by `JSONObjectWithData`
let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("Error could not parse JSON: '\(jsonStr)'")
}
}
task.resume()
JSON Response:
{
"user": {
"token": "ABCDEFGHI",
"agent_id": 0,
"user_id": 151,
"company_id": 1,
"affiliate_Id": 0,
"role_id": 1,
"username": "testman1",
"surname": "Test",
"lastname": "man",
},
"menu": [
{ .....
Can someone help me in solving this. Tia

You should avoid using Foundation classes (NSDictionary etc) and use Swift types.
I also suggest you add a failable initialiser to your UserDetails class that accepts a dictionary:
class UserDetails {
let token: String
let agentId: Int
let userId: Int
let companyId: Int
let affliateId: Int
let roleId: Int
let username: String
let surname: String
let lastname: String
init?(dictionary: [String:Any]) {
guard let token = dictionary["token"] as? String,
let agentId = dictionary["agent_id"] as? Int,
let userId = dictionary["user_id"] as? Int,
... // And so on
else {
return nil
}
self.token = token;
self.agentId = agentId;
self.userId = userId;
self.companyId = companyId;
self.affliateId = affliateId;
self.roleId = roleId;
self.username = username;
self.surname = surname;
self.lastname = lastname;
}
}
and in your completion block:
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:Any] {
if let userDict = json["user"] as [String:Any] {
guard let userObject = UserDetails(dictionary:userDict) else {
print("Failed to create user from dictionary")
return
}
// Do something with userObject
}
}
} catch let parseError {
I also took the liberty of removing the _ from your properties because _ are icky

First of all you need to use Swift native Dictionary instead of NSDictionary also batter if you define your init method of UserDetails with single parameter of type [String: Any].
class UserDetails {
var token:String = ""
var agent_id: Int = 0
var user_id:Int = 0
var company_id:Int = 0
var affliate_Id:Int = 0
var role_id:Int = 0
var username: String = ""
var surname:String = ""
var lastname:String = ""
init(userDic: [String: Any]) {
if let token = user["token"] as? String, let agent_id = user["agent_id"] as? Int,
let user_id = user["user_id"] as? Int, let company_id = user["company_id"] as? Int,
let affliate_Id = user["affliate_Id"] as? Int, let role_id = user["role_id"] as? Int,
let username = user["username"] as? String, let surname = user["surname"] as? String,
let lastname = user["lastname"] as? String {
self.token = token;
self.agent_id = agent_id;
self.user_id = user_id;
self.company_id = company_id;
self.affliate_Id = affliate_Id;
self.role_id = role_id;
self.username = username;
self.surname = surname;
self.lastname = lastname;
}
}
}
Now simply call this init method from your json response like this.
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any], let userDic = json["user"] as? [String: Any]{
let userDetails = UserDetails(userDic: userDic)
}

Related

how to get the indexpath of selected Action sheet picker Swift

I have got myself into confusion.I need someone to help me.
I have been trying to use Action sheet picker and everything was good but I need to get the selected item. How can I do it?
ActionSheetMultipleStringPicker.show(withTitle: "Select Country", rows: [
countriesArray,], initialSelection: [0],
doneBlock: {
picker, indexes, values in
print("values = \(values)")
print("indexes = \(indexes)")
print("picker = \(picker)")
DispatchQueue.main.async {
// Update UI
self.performSegue(withIdentifier: "pdfsegue", sender: nil)
}
return
}, cancel:
{
ActionMultipleStringCancelBlock in return
}, origin: sender)
these are my arrays:
var emiratesArray = [String]()
var emiratesIdArray = [Int]()
I am getting values into my arrays from JSON:
let url = NSURL(string: EMIRATES_URL)
URLSession.shared.dataTask(with: (url as?URL)!, completionHandler: {(data,response,error) ->
Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
{
print(jsonObj.value(forKey: "data")!)
if let messageArray = jsonObj.value(forKey: "data") as? NSArray
{
for message in messageArray
{
if let messageDict = message as? NSDictionary
{
if let data = data {
let successmessage = jsonObj.value(forKey: "success") as? Int
if(successmessage == 1)
{
if let emirate_name = messageDict.value(forKey: "emirate_name")
{
self.emiratesArray.append(emirate_name as! String)
print(emirate_name)
}
if let company_id = messageDict.value(forKey: "id")
{
self.emiratesIdArray.append(company_id as! Int)
print(company_id)
}
} else
{
}
}
}
}
}
}
}).resume()
someone help me How to get the selected item?If i were using tableview then I would have taken the id from the [indexpath.row] but in UIActionsheet picker,I dont know how get the id of selected value. Please someone help me please
I didn't found any Custom Class in your given code. You can create a class like below:
class Emirate: NSObject {
var id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
override var description: String {
return self.name
}
}
I have override the description property because ActionSheetMultipleStringPicker shows description of object given in the array if it is not String.
Declare your Emirate array
var emiratesArray = [Emirate]()
Your API request and Parsing should be like this:
let url = NSURL(string: "EMIRATES_URL")
URLSession.shared.dataTask(with: (url as?URL)!, completionHandler: {(data,response,error) ->
Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
{
print(jsonObj.value(forKey: "data")!)
if let messageArray = jsonObj.value(forKey: "data") as? NSArray
{
for message in messageArray
{
if let messageDict = message as? NSDictionary
{
if let data = data {
let successmessage = jsonObj.value(forKey: "success") as? Int
if(successmessage == 1)
{
if let emirate_name = messageDict.value(forKey: "emirate_name") as? String, let company_id = messageDict.value(forKey: "id") as? Int
{
self.emiratesArray.append(Emirate(id: company_id, name: emirate_name))
print(emirate_name)
}
} else
{
}
}
}
}
}
}
}).resume()
Now feed your action sheet self.emiratesArray
ActionSheetMultipleStringPicker.show(withTitle: "Select Country", rows: [
self.emiratesArray], initialSelection: [0],
doneBlock: {
picker, indexes, values in
print("values = \(values)")
print("indexes = \(indexes)")
print("picker = \(picker)")
DispatchQueue.main.async {
// Update UI
self.performSegue(withIdentifier: "pdfsegue", sender: nil)
}
return
}, cancel:
{
ActionMultipleStringCancelBlock in return
}, origin: sender)
You will now get the selected Emirate class objects in values array.
N.B- This code is just to give you an idea about how it can work.

Networking using JSONDecoder & codable protocol

I am wondering what i am doing wrong . I am trying to understand how to use urlsession and codable protocol using JSONDecoder. When i use JSONDecoder i am getting the following error message :
keyNotFound(CodingKeys(stringValue: "name", intValue: nil), my resaponse contain ''name'' . But when i use JSONSerialization, I am able to print the response . If someone can explain me.
Code using JSONDecoder
struct Business:Codable {
let name: String
enum CodingKeys: String, CodingKey {
case name = "name"
}
init(from decoder: Decoder) throws {
let value = try decoder.container(keyedBy: CodingKeys.self)
self.name = try value.decode(String.self, forKey:CodingKeys.name)
}
}
let task = session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse {
print(response)
} else{
print("error")
}
guard let data = data else {return}
do {
let business = try JSONDecoder().decode(Business.self, from: data)
print(business.name)
} catch {
print("Error parsing JSON: \(error)")
}
}
task.resume()
Code using JSONSerialization
struct Business: Decodable {
let name: String
let displayAddress: String
let categories: String
let imageUrl : String
init(json: [String:Any]) {
name = json["name"] as? String ?? ""
displayAddress = json["location"] as? String ?? ""
categories = json["categories"] as? String ?? ""
imageUrl = json["image_url"] as? String ?? ""
}
}
let task = session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse {
print(response)
} else{
print("error")
}
guard let data = data else {return}
do {
if let myjson = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? Dictionary<String,Any> {
print(myjson)
}
} catch {
print("Error parsing ")
}
}
task.resume()
The response
["region": {
center = {
latitude = "43.67428196976998";
longitude = "-79.39682006835938";
};
}, "businesses": <__NSArrayM 0x60000211cff0>(
{
alias = "pai-northern-thai-kitchen-toronto-5";
categories = (
{
alias = thai;
title = Thai;
}
);
coordinates = {
latitude = "43.647866";
longitude = "-79.38864150000001";
};
"display_phone" = "+1 416-901-4724";
distance = "3010.095870925626";
id = "r_BrIgzYcwo1NAuG9dLbpg";
"image_url" = "https://s3-media3.fl.yelpcdn.com/bphoto/t-g4d_vCAgZH_6pCqjaYWQ/o.jpg";
"is_closed" = 0;
location = {
address1 = "18 Duncan Street";
address2 = "";
address3 = "";
city = Toronto;
country = CA;
"display_address" = (
"18 Duncan Street",
"Toronto, ON M5H 3G8",
Canada
);
state = ON;
"zip_code" = "M5H 3G8";
};
name = "Pai Northern Thai Kitchen";
phone = "+14169014724";
price = "$$";
rating = "4.5";
"review_count" = 2405;
transactions = (
);
url = "https://www.yelp.com/biz/pai-northern-thai-kitchen-toronto-5?adjust_creative=A4ydpSOHv8wBNquTDeh0DQ&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=A4ydpSOHv8wBNquTDeh0DQ";
},
Business is not the root data object in your JSON. You need something like this:
struct Business: Codable {
let name: String
}
struct RootObject: Codable {
let businesses: [Business]
}
let rootObject = try JSONDecoder().decode(RootObject.self, from: data)
print(rootObject.businesses.first?.name)

Getting error when parsing JSON in Swift 3

1. This is the response String
{"error_msg": null,"applicationStateJson": {"notifications_size": "0","dfilterlogin": 1,"loginstype": null,"email_status": "0","address_status": "0","defaultfiltername": "hyderabad","login_status": "1","defaultfilterid": 145,"profile_id": null,"freelancer": "Y","otp_status": "1","notifications": []},"status": null}
2. Below one is the perfect JSONObject, I get it to using JSONLint
{
"error_msg": null,
"applicationStateJson": {
"notifications_size": "0",
"dfilterlogin": 1,
"loginstype": null,
"email_status": "0",
"address_status": "0",
"defaultfiltername": "hyderabad",
"login_status": "1",
"defaultfilterid": 145,
"profile_id": null,
"freelancer": "Y",
"otp_status": "1",
"notifications": []
},
"status": null
}
3. When I try the below code in Swift 3
let json1 = try? JSONSerialization.jsonObject(with: data, options: [])
if let object = json1 as? [String: Any]{
if let applicationState = object["applicationStateJson"] as? [String: Any]{
print("applicationState \(applicationState)")
}
}
4. I got JSONObject but it's not a proper JSONObject
(because the commas are changed into semicolon, null values are changed into "< null >" and then empty array [] changed into ())
Optional({
applicationStateJson = {
"address_status" = 0;
defaultfilterid = 145;
defaultfiltername = hyderabad;
dfilterlogin = 1;
"email_status" = 0;
freelancer = Y;
"login_status" = 1;
loginstype = "<null>";
notifications = (
);
"notifications_size" = 0;
"otp_status" = 1;
"profile_id" = "<null>";
};
"error_msg" = "<null>";
status = "<null>";
})
I want the JSONObject like the step 2, any help?
To read and use a JSON response in Swift does not require you to convert the JSON object back to JSON just to get a particular part. Once you have the data loaded into a Swift type you can work directly with it to get the parts you need.
So the long way which explains my point better...
let jsonData = jsonString.data(using: .utf8)!
let json1 = try? JSONSerialization.jsonObject(with: jsonData, options: [])
if let object = json1 as? [String: Any]{
if let applicationState = object["applicationStateJson"] as? [String: Any]{
print("applicationState \(applicationState)")
if let addressStatus = applicationState["address_status"] as? String {
print(addressStatus)
}
}
}
The Swift 4 way of doing this with the Codable Protocol
let jsonString = "{\"error_msg\": null,\"applicationStateJson\": {\"notifications_size\": \"0\",\"dfilterlogin\": 1,\"loginstype\": null,\"email_status\": \"0\",\"address_status\": \"0\",\"defaultfiltername\": \"hyderabad\",\"login_status\": \"1\",\"defaultfilterid\": 145,\"profile_id\": null,\"freelancer\": \"Y\",\"otp_status\": \"1\",\"notifications\": []},\"status\": null}"
struct ApplicationState: Codable {
let notificationsSize: String
let dFilterLogin: Int
let loginsType: String?
let emailStatus: String
let addressStatus: String
enum CodingKeys : String, CodingKey {
case notificationsSize = "notifications_size"
case dFilterLogin = "dfilterlogin"
case addressStatus = "address_status"
case loginsType = "loginstype"
case emailStatus = "email_status"
}
}
struct ApplicationStateResponse: Codable {
let errorMsg: String?
let applicationState: ApplicationState
enum CodingKeys : String, CodingKey {
case errorMsg = "error_msg"
case applicationState = "applicationStateJson"
}
}
let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let response = try! decoder.decode(ApplicationStateResponse.self, from: jsonData)
let appState = response.applicationState
print(appState.addressStatus)
Both of these print 0 for the address status as expected. one is much easier to work with than the other though.
This article which explains the codable protocol a bit more would be a good read.
Convert Swift Dictionary object to JSON string,
if let theJSONData = try? JSONSerialization.data(withJSONObject: applicationState, options: .prettyPrinted),
let theJSONText = String(data: theJSONData, encoding: String.Encoding.ascii) {
print("JSON string = \n\(theJSONText)")
}

How to convert JSON to a dictionary in swift? [duplicate]

Hi I am making an app which works with an API. I have a working code which receives data from the API. But I thought it would be better to make my code a bit cleaner. I want to set the data from the api in an dictionary but I can't get it working. Any help would be appreciated, thanx!
Here is the api result:
I want to set the AutorId and BranchId etc etc in a dictionary.
And this is de code which I have now.
This is the Project class:
class Project: NSObject {
var AuthorId: String?
var BranchId: String?
var CompanyId: String?
var ContactId: String?
var Date: String?
var Deadline: String?
var Description: String?
var Id: String?
var State: String?
init(dictionary: [String: Any]) {
self.AuthorId = dictionary["AuthorId"] as? String
self.BranchId = dictionary["BranchId"] as? String
self.CompanyId = dictionary["CompanyId"] as? String
self.ContactId = dictionary["ContactId"] as? String
self.Date = dictionary["Date"] as? String
self.Deadline = dictionary["Deadline"] as? String
self.Description = dictionary["Description"] as? String
self.Id = dictionary["Id"] as? String
self.State = dictionary["State"] as? String
}
}
and here I am trying to set it in an dictionary:
func apiRequest() {
apiRequestHeader()
var running = false
let urlProjects = NSURL(string: "https://start.jamespro.nl/v4/api/json/projects/?limit=10")
let task = session?.dataTask(with: urlProjects! as URL) {
( data, response, error) in
if let taskHeader = response as? HTTPURLResponse {
print(taskHeader.statusCode)
}
if error != nil {
print("There is an error!!!")
print(error)
} else {
if let content = data {
do {
let dictionary = try JSONSerialization.jsonObject(with: content) as! [String:Any]
print(dictionary)
if let items = dictionary["items"] as? [[String:Any]] {
let project = Project(dictionary: items)
print(project)
self.projects.append(project)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}
catch {
print("Error: Could not get any data")
}
}
}
running = false
}
running = true
task?.resume()
while running {
print("waiting...")
sleep(1)
}
}

Parse JSON data in swift

I am trying to parse my json data in swift 3.0. I am using Alamofire 4.0+
Here is my json
{
"token":
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQ0MjgzNzgxMTF9.CNonyvtQbRgaqqkdPO5KwqpVaUmlGrpaTqlBxmvaX80",
"expires": 1504428378111,
"user":
[{"user_id":13,"user_first_name":"Himanshu","user_last_name":"Srivastava","full_name":"Himanshu Srivastava"}]
}
Here is my model class to hold these values
import Foundation
import ObjectMapper
class LoginResult:Mappable{
var token:String?
var expires:Double?
var users:[[String:Any]]?
required init?(map:Map){
}
func mapping(map:Map)->Void{
self.token <- map["token"]
self.expires <- map["expires"]
self.users <- map["user"]
}
}
None of the solution available on internet worked for me. How can I parse this json and map to the model class?
Any help here?
I was mistaken, the value for key user is indeed a regular array.
This is a solution without a third party mapper and with an extra User struct (by the way the value for key expires is an Int rather than Double).
Assuming the user data comes from a database which always sends all fields the user keys are forced unwrapped. If this is not the case use optional binding also for the user data:
struct User {
let firstName : String
let lastName : String
let fullName : String
let userID : Int
}
class LoginResult {
let token : String
let expires : Int
var users = [User]()
init(json : [String:Any]) {
self.token = json["token"] as? String ?? ""
self.expires = json["expires"] as? Int ?? 0
if let users = json["user"] as? [[String:Any]] {
self.users = users.map { User(firstName: $0["user_first_name"] as! String,
lastName: $0["user_last_name"] as! String,
fullName: $0["full_name"] as! String,
userID: $0["user_id"] as! Int)
}
}
}
}
let json = "{\"token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQ0MjgzNzgxMTF9.CNonyvtQbRgaqqkdPO5KwqpVaUmlGrpaTqlBxmvaX80\",\"expires\":504428378111,\"user\":[{\"user_id\":13,\"user_first_name\":\"Himanshu\",\"user_last_name\":\"Srivastava\",\"full_name\":\"Himanshu Srivastava\"}]}"
let jsonData = json.data(using: .utf8)!
do {
if let userData = try JSONSerialization.jsonObject(with: jsonData) as? [String:Any] {
let loginResult = LoginResult(json: userData)
print(loginResult.users[0])
// do something with loginResult
}
} catch {
print(error)
}
Here is the answer with map replaced by dictionary. Don't forget to handle error or unwrap carefully :)
let str = "{\"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQ0MjgzNzgxMTF9.CNonyvtQbRgaqqkdPO5KwqpVaUmlGrpaTqlBxmvaX80\",\"expires\": 1504428378111,\"user\": [{\"user_id\":13,\"user_first_name\":\"Himanshu\",\"user_last_name\":\"Srivastava\",\"full_name\":\"Himanshu Srivastava\"}]}"
let data = str.data(using: .utf8)
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]
//Pass this json into the following function
}catch let error{
}
func mapping(json:[String: Any]?)->Void{
self.token <- json?["token"] as? String
self.expires <- json?["expires"] as? Double
self.users <- json?["user"] as? [[String: Any]]
}