{
"message" : "success ",
"status" : "1",
"Result" : {
"name" : "abc",
"lastname" : null,
"middlename" : null,
"id" : 20431
}
}
i want to store result object into single userdefaults using model and how to retrieve it
From the docs here. Have you tried like this:
//convert the JSON to a raw String
if let rawString = json.rawString() {
userDefaults.set(rawString, forKey: "jsonData")
} else {
print("json.rawString is nil")
}
First create a model for this, using Codable protocol
struct MyJSON : Codable {
let message : String?
let status : String?
let result : JSONResult? // Don't use Result, a keyword in swift
}
struct JSONResult : Codable {
let name : String?
let lastname : String?
let middlename : String?
let id : Int?
}
Then Use the protocol to map the JSON, save the model in UserDefaults.
let jsonString =
"""
{
"message" : "success ",
"status" : "1",
"result" : {
"name" : "abc",
"lastname" : null,
"middlename" : null,
"id" : 20431
}
}
"""
let jsonData = Data(jsonString.utf8)
let data = try JSONDecoder().decode(MyJSON.self, from: jsonData)
// save model in userDefaults
func saveModel() {
if let encoded = try? JSONEncoder().encode(data) {
UserDefaults.standard.set(encoded, forKey: "MySavedValue")
}
}
//get the model
func getModel() -> MyJSON? {
guard let data = UserDefaults.standard.object(forKey: "MySavedValue") as? Data else {
return nil
}
return try? JSONDecoder().decode(MyJSON.self, from: data)
}
how to use
saveModel()
print(getModel()?.message) // print("Success")
Related
I need to make the codable model for the dynamic keys of dictionary coming from response below is the response I'm getting.
{
"data" : [
{
"desc1" : null,
"file1" : "uploads\/posts\/Aug-2021\/1629271422310452767"
},
{
"desc2" : "hello",
"file2" : "uploads\/posts\/Aug-2021\/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"
}
],
"status" : "success"
}
This desc1 and file1 is dynamic till like file1, file2 and so on, I need to have codable model for that below is my model that is not supportive.
struct ListModel: Codable {
public var data: [data]?
}
struct data: Codable {
let file : String?
let desc : String?
}
Anything support by codable protocol for that. Thanks in Advance.
You need a custom initializer. Of course this will only work if your json will always come formatted as described:
struct File {
var file: String? = ""
var desc: String? = ""
}
struct Response {
let files: [File]
let status: String
enum CodingKeys: String, CodingKey {
case files = "data", status
}
}
extension Response: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.status = try container.decode(String.self, forKey: .status)
let elements = try container.decode([[String: String?]].self, forKey: .files)
self.files = elements.reduce(into: []) {
var file = File()
for (key, value) in $1 {
if key.hasPrefix("file") {
file.file = value
} else if key.hasPrefix("desc") {
file.desc = value
}
}
$0.append(file)
}
}
}
Playground testing:
let json = """
{
"data" : [
{
"desc1" : null,
"file1" : "uploads/posts/Aug-2021/1629271422310452767"
},
{
"desc2" : "hello",
"file2" : "uploads/posts/Aug-2021/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"
}
],
"status" : "success"
}
"""
do {
let response = try JSONDecoder().decode(Response.self, from: Data(json.utf8))
print(response)
} catch {
print(error)
}
This will print:
Response(files: [File(file: Optional("uploads/posts/Aug-2021/1629271422310452767"), desc: nil), File(file: Optional("uploads/posts/Aug-2021/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"), desc: Optional("hello"))], status: "success")
here my JSON is -
{
"status": "success",
"data": [
{
"_id": "15756323",
"name": "A",
"icons": "https://sdfgsuew34j.png",
"createdAt": "2021-03-18T13:06:44.054Z",
"updatedAt": "2021-03-18T13:06:44.054Z",
"__v": 0,
"id": "6053503drm476"
},
{
"_id": "45646054821",
"name": "S",
"icons": "https://dkj/djf.png",
"createdAt": "2021-03-19T10:51:07.066Z",
"updatedAt": "2021-03-19T10:51:07.066Z",
"__v": 0,
"id": "6054821b11kd6873"
}
]
}
I'm trying to print API data in collectionView
here's some code
var places: [Post]?
func apicall() {
let url = URL(string: "http://17.550.457.84/api/category/list")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil {
do {
self.places = try JSONDecoder().decode([Post].self, from: data! )
} catch {
print("Error during JSON serialization: \(error.localizedDescription)")
}
}
}.resume()
}
I get a message in the debugger -> Error during JSON serialization: The data couldn’t be read because it isn’t in the correct format.
i also try to change -> self.places = try JSONDecoder().decode([Post].self, from: data! )` to
self.places = try JSONDecoder().decode(Post.self, from: data! )
then I got the error -> Cannot assign the value of type 'Post' to type '[Post]?'
here my model class is
struct Post : Codable {
let _id : String?
let name : String?
let icons : String?
let createdAt : String?
let updatedAt : String?
let __v : Int?
let id : String?
enum CodingKeys: String, CodingKey {
case _id = "_id"
case name = "name"
case icons = "icons"
case createdAt = "createdAt"
case updatedAt = "updatedAt"
case __v = "__v"
case id = "id"
}
The error message basically says "Your model and coming data model(JSON) are not equal". You need to change your model like :
struct BaseModel : Codable{
var status : String?
var data : [Post]?
}
And when you try to deserialize it you need to make a instance of that BaseModel so
var baseModel: BaseModel?
var places: [Post]?
when response success
do {
let responseData = try JSONDecoder().decode(BaseModel.self, from: data! )
self.places = responseData.data!
} catch {
print("Error during JSON serialization: \(error.localizedDescription
}
I am trying to compare the studentId with the array of dictionaries which has multiple studentId's. I need to get the dictionary which matches with the particular StudentID..Can any one please suggest the perfect solution. I am new to swift.
"students" : [
{
"studentId" : "STUDENT123456789",
"middleName" : "Evangeline",
"firstName" : "Dia",
"rollNo" : "1001",
"studentClass" : {
"className" : "Grade 10",
"classId" : "CLASS123456789",
}
}
{
"studentId" : "STUDENT14354678960",
"middleName" : "Joseph",
"firstName" : "Parker",
"rollNo" : "1002",
"studentClass" : {
"className" : "Grade 10",
"classId" : "CLASS15468975467",
}
}
]
I have students array which is an array of dictionaries.Now I have to compare student Id with this existing array containing multiple studentID's. so when it matches with the student ID, I need to get that particular dictionary data.
For example, I have studentId as "STUDENT14354678960" so I need to get the data containing related to this Id..
Use first, it returns the first found object or nil
if let student = students.first(where: {$0["studentId"] as! String == "STUDENT123456789"}) {
print(student["firstName"])
} else {
print("not found")
}
It's highly recommended to use a custom struct or class for the student data for example
let jsonString = """
{"students" : [
{"studentId" : "STUDENT123456789", "middleName" : "Evangeline", "firstName" : "Dia", "rollNo" : "1001", "studentClass" : { "className" : "Grade 10", "classId" : "CLASS123456789"}},
{"studentId" : "STUDENT14354678960", "middleName" : "Joseph", "firstName" : "Parker", "rollNo" : "1002", "studentClass" : {"className" : "Grade 10", "classId" : "CLASS15468975467"}}
]}
"""
struct Root : Decodable {
let students : [Student]
}
struct Student : Decodable {
let studentId, middleName, firstName, rollNo : String
let studentClass : StudentClass
}
struct StudentClass : Decodable {
let className, classId : String
}
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(Root.self, from: data)
let students = result.students
if let student = students.first(where: {$0.studentId == "STUDENT123456789" }) {
print(student)
}
} catch {
print(error)
}
You can use where with a closure:
let search = students.first { (element) -> Bool in
if let dict = element as? [String:Any] {
return dict["studentId"] == yourID
}
}
I'm following https://developers.google.com/places/web-service/details this doc to add all the information about a place, and for example to add "geometry"
"geometry" : {
"location" : {
"lat" : -33.866651,
"lng" : 151.195827
},
i created this function in my class that work well
private let geometryKey = "geometry"
private let locationKey = "location"
private let latitudeKey = "lat"
private let longitudeKey = "lng"
class EClass: NSObject {
var location: CLLocationCoordinate2D?
init(placeInfo:[String: Any]) {
placeId = placeInfo["place_id"] as! String
// coordinates
if let g = placeInfo[geometryKey] as? [String:Any] {
if let l = g[locationKey] as? [String:Double] {
if let lat = l[latitudeKey], let lng = l[longitudeKey] {
location = CLLocationCoordinate2D.init(latitude: lat, longitude: lng)
}
}
}
}
but but i'm having difficulty adding "reviews"
"reviews" : [
{
"author_name" : "Robert Ardill",
"author_url" : "https://www.google.com/maps/contrib/106422854611155436041/reviews",
"language" : "en",
"profile_photo_url" : "https://lh3.googleusercontent.com/-T47KxWuAoJU/AAAAAAAAAAI/AAAAAAAAAZo/BDmyI12BZAs/s128-c0x00000000-cc-rp-mo-ba1/photo.jpg",
"rating" : 5,
"relative_time_description" : "a month ago",
"text" : "Awesome offices. Great facilities, location and views. Staff are great hosts",
"time" : 1491144016
}
],
i tried to follow the same concept of the function i created for geometry like this
if let t = place.details?["reviews"] as? [String:Any] {
if let n = t["author_name"], let m = t["text"] {
Mylabel.text = "\(t)"
}
but is not working, i also tried to add a breakpoint and only the first line enters. What can i do? How can i create a build to show the review with a label or anything i need?
Take advantage of Codable in Swift 4. You can simply convert your JSON into a specific struct. e.g. Based on your JSON:
let json = """
{
"reviews" : [
{
"author_name" : "Robert Ardill",
"author_url" : "https://www.google.com/maps/contrib/106422854611155436041/reviews",
"language" : "en",
"profile_photo_url" : "https://lh3.googleusercontent.com/-T47KxWuAoJU/AAAAAAAAAAI/AAAAAAAAAZo/BDmyI12BZAs/s128-c0x00000000-cc-rp-mo-ba1/photo.jpg",
"rating" : 5,
"relative_time_description" : "a month ago",
"text" : "Awesome offices. Great facilities, location and views. Staff are great hosts",
"time" : 1491144016
}
]
}
"""
You can convert it into a Response struct using the following code:
struct Response: Codable {
struct Review: Codable, CustomStringConvertible {
let text: String
let authorName: String
var description: String {
return "Review text: \(text) authorName: \(authorName)"
}
enum CodingKeys: String, CodingKey {
case text
case authorName = "author_name"
}
}
let reviews: [Review]
}
do {
if let data = json.data(using: .utf8) {
let decoder = JSONDecoder()
let decoded = try decoder.decode(Response.self, from: data)
print(decoded.reviews)
} else {
print("data is not available")
}
} catch (let e) {
print(e)
}
In your code t is not a Dictionary it is an Array instead. So try doing something like this. Rest of that you can change as per your logic.
if let t = place.details?["reviews"] as? [String:Any] {
for dic in t {
if let n = dic["author_name"], let m = dic["text"] {
Mylabel.text = "\(t)"
}
}
}
Yeah, but You can also try to make it like that:
struct reviews: Codable{
var reviews: [review]?
}
struct review: Codable{
var author_name: String?
var author_url: String?
var language: String?
var profile_photo_url: String?
var rating: Int?
var relative_time_description: String?
var text: String?
var time: Int?
}
And then:
if let dict = place.details?["reviews"] as? [String: Any],
let dataToDecode = dict.data(using: .utf8){
do{
let decodedReviews = try JSONDecoder().decode(reviews.self, from: dataToDecode)
// here you have decoded reviews in array
}catch let err{
print(err)
}
}
I have searched a lot to correctly parse a multi dimensional JSON arrays in swift. From the search results , what i could grasp was that it some what similar to retro-fit parsing in Android. i.e creating a parsing class for each json response. Please excuse me if i am making a mistake. I am new to IOS swif.
This is the distance matrix api json output for my distance call
{
"destination_addresses" : [ "My Destination" ],
"origin_addresses" : [
"My Source"
],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "23.3 km",
"value" : 23333 //lastly i take this
},
"duration" : {
"text" : "49 mins",
"value" : 2938
},
"status" : "OK" //then i check this
}
]
}
],
"status" : "OK" //first i check this
}
This how i did it
This is my api call(works fine)
http://maps.googleapis.com/maps/api/distancematrix/json?origins=latitude,longitude&destinations=latitude,longitude&mode=driving&language=en-EN&sensor=false
func extract_Validate(jsonData:NSData)
{
var distInMeters = 0
var chkVarOne = "notOK"
let json1 = NSString(data: jsonData as Data, encoding: String.Encoding.utf8.rawValue)
let data = json1!.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)
print(data!)
do{
print("Inside do")
let json = try JSONSerialization.jsonObject(with: jsonData as Data, options:.allowFragments) as! NSObject
if let dictionary = json as? [String: Any] {
print("inside dictionary ")
if let detailsDict = dictionary as? NSDictionary {
print("Parse Data")
for (key, value) in detailsDict {
print("Property: \"\(key as! String)\"")
if key as! String == "status" && value as! String == "OK"{
chkVarOne = "OK"
}
if chkVarOne == "OK"
{
if key as! String == "rows"
{
if let elementsDict = value as? NSDictionary {
for(keyEle, valueEle) in elementsDict{
}
}
if let elementsDict = value as? NSArray
{
if let array = elementsDict as? [Any] {
if let firstObject = array.first {
if let distanceSet = firstObject as? NSDictionary{
for(keyDis, valueDis) in distanceSet{
// print("Property: \"\(keyDis as! String)\"")
if keyDis as! String == "elements"
{
if let DistDict = valueDis as? NSDictionary {
for(keyDict, valueDict) in DistDict{
print("Property: \"\(keyDict as! String)\"")
}
}
if let DistDict = valueDis as? NSArray {
// print(DistDict)
if let distArr = DistDict as?[Any]{
if let frst = distArr.first{
//print(frst)
if let distOne = frst as? NSDictionary{
var checkvar = "notOK"
for(ketOneDis, valOneDis) in distOne{
// print("Property: \"\(ketOneDis as! String)\"", valOneDis)
if ketOneDis as! String == "status" && valOneDis as! String == "OK"{
checkvar = "OK"
}
if checkvar == "OK"
{
if ketOneDis as! String == "distance"
{
// print(valOneDis)
if let valtwoDis = valOneDis as? NSDictionary{
for(kDis, vDis) in valtwoDis{
// print("Property: \"\(kDis as! String)\"", vDis)
if kDis as! String == "value"{
distInMeters = vDis as! Int
}
}
if let valTwoDis = valOneDis as? NSArray{
print(valTwoDis)
}
}
}
}
}
}
}
}
}
}
//print(distanceSet,"rttgtgtgtgtgtgtg")
}
if let distSet = firstObject as? NSArray{
print(distSet,"dfdffddfdfdfd")
}
}
}
}
}
//let rows
}
}
} //ending here
}
}
}
catch{
print("error in JSONSerialization")
}
print(distInMeters," is the resulting value")
}
This code is working fine fine. But i know this is not the way to do it.
So please help me
I think there might be some errors occur in this code later. Don't know for sure
The easiest and most effective way to do this is by using object mapping. Something like Gloss (https://github.com/hkellaway/Gloss) would do the trick. In your case you will have the following classes (objects):
Response
import Foundation
import Gloss
//MARK: - Response
public struct Response: Glossy {
public let destinationAddresses : [String]!
public let originAddresses : [String]!
public let rows : [Row]!
public let status : String!
//MARK: Decodable
public init?(json: JSON){
destinationAddresses = "destination_addresses" <~~ json
originAddresses = "origin_addresses" <~~ json
rows = "rows" <~~ json
status = "status" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"destination_addresses" ~~> destinationAddresses,
"origin_addresses" ~~> originAddresses,
"rows" ~~> rows,
"status" ~~> status,
])
}
}
Element:
import Foundation
import Gloss
//MARK: - Element
public struct Element: Glossy {
public let distance : Distance!
public let duration : Distance!
public let status : String!
//MARK: Decodable
public init?(json: JSON){
distance = "distance" <~~ json
duration = "duration" <~~ json
status = "status" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"distance" ~~> distance,
"duration" ~~> duration,
"status" ~~> status,
])
}
}
Row:
import Foundation
import Gloss
//MARK: - Row
public struct Row: Glossy {
public let elements : [Element]!
//MARK: Decodable
public init?(json: JSON){
elements = "elements" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"elements" ~~> elements,
])
}
}
Distance:
import Foundation
import Gloss
//MARK: - Distance
public struct Distance: Glossy {
public let text : String!
public let value : Int!
//MARK: Decodable
public init?(json: JSON){
text = "text" <~~ json
value = "value" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"text" ~~> text,
"value" ~~> value,
])
}
}
After creating the classes, you map the JSON to the Object by doing the following:
let jsonResponse = ..//Your JSON response
guard let distanceResponse = Response(json: jsonResponse) else {
// handle decoding failure here
}
//You can use the distanceResponse object here as an object and access it's values.
Learn more about object mapping in this tutorial: https://www.raywenderlich.com/150322/swift-json-tutorial-2