Parsing JSON in Swift and accessing values? - json

I have successfully parsed JSON for:
birthday = "04/10/1986";
id = 202038339983;
location = {
city = Jupiter;
country = "United States";
state = FL;
};
My question is when part of the JSON is:
submissions = {
data = (
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
});}
How am I supposed to access created_time, id, viewer, and message?
I have been able to print the whole submissions JSON response to the console with this code :
guard let jsonD = responseFromServer as? [String : Any] else {return}
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
My console will display:
["data": <__NSArrayI 0x6040001a86c0>(
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
})]
My question is how should I parse the JSON so I can access the created_time inside submissions?
Here is the HTTP Request:
struct XClass: RequestProtocol {
var Path = "/User"
var parameters: [String : Any]? = ["stuff": "id, birthday, location, submissions"]
var aToken = aToken.current
var httpMethod: RequestHTTPMethod = .GET
var apiVersion: APIVersion = .defaultVersion
struct Response: ResponseProtocol {
var id = String()
var birthday = String()
var city = String()
var state = String()
var country = String()
var viewSubs = [String : Any]()
init(XResponse: Any?) {
guard let jsonD = XResponse as? [String : Any] else {return}
id = (jsonD["id"] as? String)!
birthday = (jsonD["birthday"] as? String)!
let XArr = (jsonD["location"] as? [String : String])
city = XArr!["city"]!
country = XArr!["country"]!
state = XArr!["state"]!
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
viewSubs = theSubs1
}}}
func getXData(){
let connection = RequestConnection()
connection.add(XClass()) { response, result in
switch result {
case .success(let response):
print("Request Succeeded: \(response)\n\n\n")
case .failed(let error):
print("Request Failed: \(error)")
}}
connection.start()
}

Create a struct
struct Data: Decodable {
var created_time : String
var id : String
var viewer : String
}
call to the api url from URLSession
guard let url = URL(string: "your api url")
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error.localizedDescription)
} else {
guard let data = data else {return}
var data: [Data]() = JSONDecoder().decode(Data.self, data)
for dat in data{
print(dat.created_time)
print(dat.id)
print(dat.viewer)
}
}

If you are not using Decodable from Swift 4, or still in Swift 3,
then you can specify that the data in "submissions" is an array of dictionaries (double brackets) then you can iterate that.
Change
let subs1 = (jsonD["submissions"] as? [String : Any])
To
let subs1 = (jsonD["submissions"] as? [[String : Any]])
for sub in subs1 {
let time = sub["created_time "] as? [String : Any]
...
}

Related

How To Pass An Array Of Data Into A JSON Request Swift

I am trying to take information from a Firebase snapshot of each child within a certain branch of the database and pass it into a function which uses a google API request. However, instead of passing in one lot of information, completing the function, and then doing the same for the next lot of information it is instead passing all the information all at once. This is leading the url only getting one address instead of all of them. I am getting the error "Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444" too.
Code For Getting The Snapshot:
func fetchSalonsForMarkers() {
let ref = Database.database().reference()
let _ = ref.child("user_profile").observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.value as! [String : AnyObject] {
let snapshotChildData = child.value as! [String : AnyObject]
var uniqueSalonData = Salon()
for (key, value) in snapshotChildData {
uniqueSalonData.acceptedTerms = snapshotChildData["acceptedterms"] as? String
uniqueSalonData.acceptedTermsModdifiedAt = snapshotChildData["acceptedtermsmoddifiedat"] as? String
uniqueSalonData.businessName = snapshotChildData["businessname"] as? String
uniqueSalonData.companyAddressCounty = snapshotChildData["companyaddresscounty"] as? String
uniqueSalonData.companyAddressPostCode = snapshotChildData["companyaddresspostcode"] as? String
uniqueSalonData.companyAddressStreet = snapshotChildData["companyaddressstreet"] as? String
uniqueSalonData.companyAddressTown = snapshotChildData["companyaddresstown"] as? String
uniqueSalonData.companyEmail = snapshotChildData["companyemail"] as? String
uniqueSalonData.companyMobilePhone = snapshotChildData["companymobilephone"] as? Int
uniqueSalonData.ownerFirstName = snapshotChildData["ownerFirstName"] as? String
uniqueSalonData.ownerLastName = snapshotChildData["ownerLastName"] as? String
uniqueSalonData.stripeCustomerId = snapshotChildData["stripeCustomerId"] as? String
}
self.salons.append(uniqueSalonData)
self.longLatLookUp(street: uniqueSalonData.companyAddressStreet ?? "Street Error", town: uniqueSalonData.companyAddressTown ?? "Town Error", county: uniqueSalonData.companyAddressCounty ?? "County Error", postcode: uniqueSalonData.companyAddressPostCode ?? "Postcode Error", businessName: uniqueSalonData.businessName ?? "Name Error")
}
})
}
Code For API Call(Google Geocoding)
func longLatLookUp(street: String, town : String, county: String, postcode: String, businessName: String) {
let completeSalonAddress = ("\(street) \(county) Scotland")
let formattedSalonAddress = completeSalonAddress.replacingOccurrences(of: " ", with: "")
let url = URL(string: "https://maps.googleapis.com/maps/api/geocode/json?address=\(formattedSalonAddress)&key=APIKEY")!
//API Key does have valid key.
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
let json = try? JSONDecoder().decode(Welcome.self, from: data)
let salonLat = json?.results[0].geometry.location.lat
let salonLng = json?.results[0].geometry.location.lng
self.addMarkers(lat: salonLat!, lng: salonLng!, businessName: businessName)
}
task.resume()
}
Code For Adding Markers(For Context):
func addMarkers(lat: Double, lng: Double, businessName: String) {
DispatchQueue.main.async {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: lat , longitude: lng)
marker.title = businessName
marker.snippet = "Test"
marker.appearAnimation = GMSMarkerAnimation.pop
let markerPic: UIImage = UIImage(named: "AppIcon")!
marker.icon = markerPic
marker.map = self.mapView
}
}

swift how to add dictionary keys values in array

I am working on swift json Parsing where i am recieving json Data from Rest API for eg:
{"baseUrl":"http:\/\/localhost:9010\/",
"pluginOptions":{"values":[{"StartSyncTime":"2020-10-06 17:22:34","uuid":"977a5d03-2cdf-4008-87e6-01594e4369ef","LastSyncTime":"2020-10-06",
"MessageWrapper":{"Routing":"OriginSession","SourceSystem":"Mobile","MessageId":"51152ccd-32c6-475b-86a6-a2c883d551f5",
"UserID":"77ee794e-433d-4b93-bcaa-53408ede50fd","TenantID":"null","TenantName":"visurtest","Payload":"{}",
"DataType":"SQLLiteDDLAsync","MessageKind":"READ"}}],
"database":"structionDB","encrypted":false,"mode":"no-encryption"}}
I am getting as type [String:Any?] from rest api in below code
var pluginOptions : [String: Any]? = call.getObject("pluginOptions")
pluginOptions?[MessageWrapperKeys.DataType] = [DataTypes.SQLLiteDDLAsync]
pluginOptions?[MessageWrapperKeys.MessageKind] = [MessageKinds.READ]
pluginOptions?[MessageWrapperKeys.Payload] = ["{}"]
from above Json Response i need to extract MessageWrapper Dictionary like below
{"Routing":"OriginSession","SourceSystem":"VisurMobile","MessageId":"51152ccd-32c6-475b-86a6-a2c883d551f5",
"UserID":"77ee794e-433d-4b93-bcaa-53408ede50fd","TenantID":"null","TenantName":"visurtest","Payload":"{}",
"DataType":"SQLLiteDDLAsync","MessageKind":"READ"}
where above last 3 key vale i have added from code,Now i have to send Rest Api Call with above request but i did not able to figure it out how i will extraxt MessageWrapper Keys and append above 3 Key Values so that i get final jsonString Request like
{"Routing":"OriginSession","SourceSystem":"Mobile","MessageId":"05086b82-3623-492b-926a-d3bb8d01aa52","UserID":"77ee794e-433d-4b93-bcaa-53408ede50fd","TenantID":"null","TenantName":"test","Payload":"{}","DataType":"SQLLiteDDLAsync","MessageKind":"READ"}
below code i have triyed but not able to access
func readTableFromBackend(_ call: CAPPluginCall) -> Observable<String>{
var swrapper: [[String: Any]] = [[:]]
var pluginOptions : [String: Any]? = call.getObject("pluginOptions")
let json = JSON(pluginOptions)
var arrayNames = json["values"].arrayValue.map {$0["MessageWrapper"].string}
for arrayvalues in json["values"].arrayValue{
for arrayvalues in json["MessageWrapper"].arrayValue{
var string = arrayvalues["UserID"].string
}
var string = arrayvalues["MessageWrapper"].string
print(string)
}
pluginOptions?[MessageWrapperKeys.DataType] = [DataTypes.SQLLiteDDLAsync]
pluginOptions?[MessageWrapperKeys.MessageKind] = [MessageKinds.READ]
pluginOptions?[MessageWrapperKeys.Payload] = ["{}"]
var values = pluginOptions?["values"]
var modes = pluginOptions?["mode"]
for (key,value)in pluginOptions ?? ["":""]{
print("\(key) -> \(value)")
}
var finajsonstring: String = ""
var pluginOptions1: [String:Any] = [MessageWrapperKeys.DataType:DataTypes.SQLLiteDDLAsync, MessageWrapperKeys.MessageKind:MessageKinds.READ,MessageWrapperKeys.Payload: "{}"]
swrapper.append(pluginOptions ?? ["":""])
swrapper.append(pluginOptions1 )
let apidata = PullBackendData()
let plugindata = PluginCallParameterOptions()
var url: String? = call.getString("baseUrl")
// let payload = swrapper
let address = plugindata.getApiUrl(controllerName: Controllers.DataSync, baseUrl: url ?? "")
let jsonData = try? JSONSerialization.data(withJSONObject: pluginOptions)
guard let jsonString = String(data : jsonData!, encoding: .utf8
) else { return Observable.just("Error")}
let data = Data(jsonString.utf8)
do{
let newdata : NSDictionary = try JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
}
if((newdata.value(forKey: "values")) != nil){
let info : NSArray = newdata.value(forKey: "values") as! NSArray
let info1 : NSArray = info.value(forKey: "MessageWrapper") as! NSArray
var wrapperarray: [[String:Any]] = [[String:Any]]()
wrapperarray = newdata.value(forKey: "values") as! [[String:Any]]
wrapperarray.append(pluginOptions1)
let jsonData: NSData = try JSONSerialization.data(withJSONObject: info1, options: []) as NSData
let jsonData2: NSData = try JSONSerialization.data(withJSONObject: wrapperarray, options: []) as NSData
do {
var jsonstring = try NSString(data: jsonData as Data, encoding: String.Encoding.utf8.rawValue)! as String
var jsonstring2 = NSString(data: jsonData2 as Data, encoding: String.Encoding.utf8.rawValue)! as String
//getting final string and sending to rest api
finajsonstring = jsonstring2 as String
}catch {
print("error")
}
}
} catch let error as NSError{
print(error)
}
let response = apidata.post(_for: address, bodyData: finajsonstring)
return Observable.from(optional: response)
}

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)

Deserialize a Dictionary (JSON) to a swift object

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)
}

Parsing JSON response in Swift/iOS

I have the following JSON.
[{"chatId":"1","user1_id":"1212","user2_id":"8543211123","user1_name":"dave","user2_name":"daveee","user1_profile_pic":"http:\/\/graph.facebook.com\/1212\/picture?type=large","user2_profile_pic":"https:\/\/scontent-waw1-1.xx.fbcdn.net\/v\/t1.0-9\/1212.jpg?oh=c288ac7b31a61aee751e8ddafb05e78a&oe=57DC702E","message":{"1":{"message_id":"24242241","sender":"1212","chatId":"1","text":"hello i am","timestamp":"2016-05-24 17:13:08"},"2":{"message_id":"421421","sender":"1212","chatId":"1","text":"great","timestamp":"2016-05-24 17:15:08"}}},{"chatId":"2","user1_id":"23413524635","user2_id":"1212","user1_name":"Leo","user2_name":"dave","user1_profile_pic":"https:\/\/scontent-fra3-1.xx.fbcdn.net\/v\/l\/t1.0-1\/1212.jpg?oh=1212&oe=579AE3AE","user2_profile_pic":"http:\/\/graph.facebook.com\/1212\/picture?type=large","message":{"1":{"message_id":"21321213","sender":"1212","chatId":"2","text":"yes, hi","timestamp":"2016-05-25 15:46:57"}}}]
I want to loop through the message and for each counter, extract the values. This is my code:
for anItem in jsonArray as! [Dictionary<String, AnyObject>]
var chat_messages : [Message]? = nil
var count_messages = 0;
if let dataArray = anItem["message"] as? [Int : Dictionary<String, AnyObject> ] {
for onemessage in dataArray as! [Dictionary<String, AnyObject>] {
let curr_message = Message()
if let messageid = onemessage["message_id"] as? String {
curr_message.id = messageid
}
if let messagedate = onemessage["timestamp"] as? NSDate {
curr_message.date = messagedate
}
if let messagesender = onemessage["sender"] as? String {
curr_message.sender = messagesender
}
if let messagetext = onemessage["text"] as? String {
curr_message.text = messagetext
}
chat_messages![count_messages] = curr_message
count_messages = count_messages + 1
}
}
The problem is that the line if let dataArray = anItem["message"] as? [Int : Dictionary<String, AnyObject> ] { always fails and the if condition is never entered.
If you are using NSJSONSerialization, then every dictionary is of the type: [String : AnyObject]. So this line:
if let dataArray = anItem["message"] as? [Int : Dictionary<String, AnyObject> ] {
should be:
if let dataArray = anItem["message"] as? [String : AnyObject] {
And to loop through the message dictionaries inside dataArray you could replace:
for onemessage in dataArray as! [Dictionary<String, AnyObject>] {
with:
for (_, messageDictionary) in dataArray {
if let onemessage = messageDictionary as? [String : AnyObject] {
and the rest of your code to get the various values out of the onemessage dictionary should work as you have it written now, except that this line will crash:
chat_messages![count_messages] = curr_message
Because you are forced unwrapping chat_messages, which you initialized as nil:
var chat_messages : [Message]? = nil
instead of as an empty array:
var chat_messages = [Message]()