Parse JSON via Codable protocol - ios

I have an answer with Firebase in this form:
["allNews": <__NSArrayM 0x6000015f06c0>(
{
createDate = "21.02.19";
creator = "lol#gmail.com";
creatorImageURL = "<null>";
creatorUID = kzorlyIOI3RgEjCV1XDLQUhu5CS2;
newsImageURL = "";
text = "Daft g s dfg ";
title = "Test ";
},
{
createDate = "21.02.19";
creator = "plol2#gmail.com";
creatorImageURL = "<null>";
creatorUID = Tw1JzFzcVbelRUA7GoFZ9CIWIwr1;
newsImageURL = "";
text = Vcbccvbvb;
title = hdbdvbccfb;
}
)
]
How can I parse it via the Codable protocol?
Below is my code:
struct AllNews: Codable {
var allNews: [DetailNews]
}
struct DetailNews: Codable {
var creator: String
var creatorUID: String
var title: String
var text: String
var createDate: String
var creatorImageURL: String
var newsImageURL: String
}
that's how I parse the data
guard let newsData = try? JSONSerialization.data(withJSONObject: document.data(), options: []) else { return }
let decodeJSON = JSONDecoder()
let allNews = try? decodeJSON.decode([DetailNews].self, from: newsData)
print(allNews)
but all the same allNews comes nil although the news data comes to me in the form of json which is attached above

You are receiving response in format:
[ "allNews": (
{
key : value
},
{
key : value
}
)]
As your news array lies in allNews key so you should pass AllNews struct to decode your response as:
guard let newsData = try? JSONSerialization.data(withJSONObject: document.data(), options: []) else { return }
let allNews = try? JSONDecoder().decode(AllNews.self, from: newsData)
print(allNews)

This should help you parse your data
let value = response.data
do {
let allValues = try JSONDecoder().decode([DetailNews].self, from: value)
} catch let error {
print(error)
}
Make sure value is of type Data

Related

iOS error 'Invalid type in JSON write (FIRTimestamp)'

I am trying to map my data to Model.
Where I am using Firestore snapshot listener, to get data.
here, I am getting data and mapping to "User" model that;
do{
let user = try User(dictionary: tempUserDic)
print("\(user.firstName)")
}
catch{
print("error occurred")
}
Here is my Model:
struct User {
let firstName: String
// var lon: Double = 0.0
// var refresh:Int = 0
// var createdOn: Timestamp = Timestamp()
}
//Testing Codable
extension User: Codable {
init(dictionary: [String: Any]) throws {
self = try JSONDecoder().decode(User.self, from: JSONSerialization.data(withJSONObject: dictionary))
}
private enum CodingKeys: String, CodingKey {
case firstName = "firstName"
}
}
Correct me if I am wrong.
Crashing because I am getting "Timestamp" in data.
Data getting from listener :
User Dictionary:
[\"firstName\": Ruchira,
\"lastInteraction\": FIRTimestamp: seconds=1576566738 nanoseconds=846000000>]"
How to map "Timestamp" to Model?
Tried "CodableFirstore" https://github.com/alickbass/CodableFirebase
An approach is to create an extension to type Dictionary that coverts a dictionary to any other type, but automatically modifies Date and Timestamp types to writeable JSON strings.
This is the code:
extension Dictionary {
func decodeTo<T>(_ type: T.Type) -> T? where T: Decodable {
var dict = self
// This block will change any Date and Timestamp type to Strings
dict.filter {
$0.value is Date || $0.value is Timestamp
}.forEach {
if $0.value is Date {
let date = $0.value as? Date ?? Date()
dict[$0.key] = date.timestampString as? Value
} else if $0.value is Timestamp {
let date = $0.value as? Timestamp ?? Timestamp()
dict[$0.key] = date.dateValue().timestampString as? Value
}
}
let jsonData = (try? JSONSerialization.data(withJSONObject: dict, options: [])) ?? nil
if let jsonData {
return (try? JSONDecoder().decode(type, from: jsonData)) ?? nil
} else {
return nil
}
}
}
The .timestampString method is also declared in an extension for type Date:
extension Date {
var timestampString: String {
Date.timestampFormatter.string(from: self)
}
static private var timestampFormatter: DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone(identifier: "UTC")
return dateFormatter
}
}
Usage, like in the case of the question:
let user = tempUserDict.decodeTo(User.self)
I solved this by converting the FIRTimestamp fields to Double (seconds) so the JSONSerialization could parse it accordingly.
let items: [T] = documents.compactMap { query in
var data = query.data() // get a copy of the data to be modified.
// If any of the fields value is a `FIRTimestamp` we replace it for a `Double`.
if let index = (data.keys.firstIndex{ data[$0] is FIRTimestamp }) {
// Convert the field to `Timestamp`
let timestamp: Timestamp = data[data.keys[index]] as! Timestamp
// Get the seconds of it and replace it on the `copy` of `data`.
data[data.keys[index]] = timestamp.seconds
}
// This should not complain anymore.
guard let data = try? JSONSerialization.data(
withJSONObject: data,
options: .prettyPrinted
) else { return nil }
// Make sure your decoder setups the decoding strategy to `. secondsSince1970` (see timestamp.seconds documentation).
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
return try? decoder.decode(T.self, from: data)
}
// Use now your beautiful `items`
return .success(items)

Error: value of type 'Optional<NSMutableArray>' has no subscripts Array[0] in Swift iOS

I am fetching some data from my Objective C classes. Here is my 'dataArray' data:
Optional(<__NSArrayM 0x2818cff60>(
{
date = 1574164423;
shakeState = 1;
},
{
date = 1574164431;
shakeState = 1;
}
)
)
I have created a Modal class 'ShakeInfo', that contain date and shakeState value.
I just want to convert this Array into array of 'ShakeInfo' object. My problem is when I am trying to print 'dataArray[0]' then I am getting error :
error: <EXPR>:3:1: error: value of type 'Optional<NSMutableArray>' has no subscripts
dataArray[0]
How can I read this array value index wise. Please advise me.
Edited:
Here is my code after getting dataArray:
do {
let data = try JSONSerialization.data(withJSONObject: dataArray!)
let responseStr = String(data: data, encoding: .utf8)!
print(responseStr)//[{"date":"1574164424","shakeState":1},{"date":"1574164430","shakeState":1}]
var shakeInfoDetails = [ShakeInfo]()
//how to add 'responseStr' value in the shakeInfoDetails Modal Array
} catch {
print("JSON serialization failed: ", error)
}
let data = #"[{"date":"1574164424","shakeState":1},{"date":"1574164430","shakeState":1}]"#.data(using: .utf8)!
//Model
struct ModelRecord : Codable {
var date : String
var shakeState : Int
}
class Model {
var date : Date
var shakeState : Int
init?(record: ModelRecord) {
guard let secondsFrom1970 = Double(record.date) else {
return nil
}
date = Date(timeIntervalSince1970: secondsFrom1970)
shakeState = record.shakeState
}
}
//Decoding
let decoder = JSONDecoder()
do {
let records = try decoder.decode([ModelRecord].self, from: data)
let models = records.compactMap { Model(record: $0) }
}
catch {
print("Decoding Error: \(error)")
}
Answer to original question:
let a1 : NSMutableArray? = NSMutableArray(array: ["A", "B", "C"])
a1?.object(at: 2)
Please read about the following:
Optionals in Swift
Codable - https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types
Arrays in Swift Standard Library
NSMutableArray defined in Foundation

how to add Json value into model Array to display into tableview in swift

I'm using the tableview to display the Two Json value but the problem is I cant add value into model struct to displaying into tableview using two Api's. i want to show percentage value in one of the cell label and
here is my json
[
{
"Percentage": 99.792098999,
}
]
my second json value
{
"Categories": [
"Developer",
"ios "
],
"Tags": [
{
"Value": "kishore",
"Key": "Name"
},
{
"Value": "2",
"Key": "office"
},
]
}
and i need show the Categories value in Categories label in tableview
value and key on tableview
here is my Struct
struct info: Decodable {
let Categories: String?
let Tags: String?
let Value: String?
let Key: String?
var Name: String?
let percentage: Double?
}
here its my code
var List = [info]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
print(json as Any)
guard let jsonArray = json as? [[String: Any]] else {
return
}
print(jsonArray)
for dic in jsonArray{
guard let per = dic["percentage"] as? Double else { return }
print(per)
}
and second json
if let array = json["Tags"] as? [[String: String]] {
for dict in array {
let key = dict["Key"]
let value = dict["Value"]
switch key {
case "office":
case "Name":
default:
break;
}
}
here is my cell for row indexpath
cell.Categories.text = list[indexpath.row].percentage
cell.Name.text = list[indexpath.row].name
cell.office.text = list[indexpath.row].office
Please use Swift 4 Codable protocol to decode the value from JSON.
//1.0 Create your structures and make it conform to Codable Protocol
struct Tags: Codable{
var Key: String
var Value: String
}
struct Sample: Codable{
var Categories: [String]
var Tags: [Tags]
}
In your method, perform below steps:
//2.0 Get your json data from your API. In example below, i am reading from a JSON file named "Sample.json"
if let path = Bundle.main.path(forResource: "Sample", ofType: "json") {
do {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
do {
//3.0 use JSONDecoder's decode method to decode JSON to your model.
let sample = try JSONDecoder().decode(Sample.self, from: jsonData)
//4.0 User the "sample" model to do your stuff. Example, printing some values
print("Sample.Category = \(sample.Categories)")
print("Sample.Name = \(sample.Tags[0].Value)")
print("Sample.Office = \(sample.Tags[1].Value)")
} catch let error {
print("Error = \(error)")
}
} catch {
// handle error
}
}
I prefer to use Codable all the time with JSON even for simpler types so for percentage I would do
struct ItemElement: Decodable {
let percentage: Double
enum CodingKeys: String, CodingKey {
case percentage = "Percentage"
}
}
and we need to keep these values in a separate array, declared as a class property
let percentageList: [Double]()
and json encoding would then be
let decoder = JSONDecoder()
do {
let result = try decoder.decode([ItemElement].self, from: data)
percentageList = result.map { item.percentage }
} catch {
print(error)
}
Similar for the second part
struct Item: Decodable {
let categories: [String]
let tags: [Tag]
enum CodingKeys: String, CodingKey {
case categories = "Categories"
case tags = "Tags"
}
}
struct Tag: Decodable {
let value, key: String
enum CodingKeys: String, CodingKey {
case value = "Value"
case key = "Key"
}
}
use a dictionary for the result, again as a class property
var values = [String: String]()
and the decoding
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Item.self, from: data)
for item in result.tags {
values[item.key] = values.item.value
}
} catch {
print(error)
}
and then in the cell for row code
cell.Categories.text = percentageList[indexpath.row].percentage
cell.Name.text = values["name"]
cell.office.text = values["office"]
Note that this last code looks very strange since you don't have an array of name/office values judging by your json. Maybe you have simplified it some way but the code above is the best I can do with the information given even if it possibly wrong

Convert array of dictionaries into a dictionary and init the model

I'm getting an array of dictionaries from the server. Then I'm trying to convert it to jsonDictionary it seems like I'm doing something wrong. How can I also init my Users model?
Here is the code:
func getSearchedUsers(key: String, completion: #escaping(SearchedUsers?) -> Void) {
if let url = URL(string: baseURL + "search?qerty=\(key)") {
Alamofire.request(url).responseJSON { (response) in
if let array = response.result.value as? [[String:Any]] {
var dictionary = [String:Any]()
for item in array {
for (key, value) in item {
dictionary.updateValue(value, forKey: key)
}
}
} else {
completion(nil)
}
}
}
}
And here is the model:
class SearchedUsers {
let id: Int
let username: String?
let fullName: String?
let profilePicture: URL?
let isPrivate: Bool
init(data: [String: Any]) {
id = data["id"] as! Int
username = data["username"] as? String
fullName = data["fullName"] as? String
isPrivate = data["isPrivate"] as! Bool
profilePicture = data["profilePicUrl"] as? URL
}
}
How can I get this to work?
Here is the response I get:
[Result]: SUCCESS: (
{
byline = "21.9k followers";
followerCount = 21911;
friendshipStatus = {
following = 0;
"incoming_request" = 0;
"is_bestie" = 0;
"is_private" = 0;
"outgoing_request" = 0;
};
fullName = "Undefined Variable";
hasAnonymousProfilePicture = 0;
id = 8513861541;
isPrivate = 0;
isVerified = 0;
mutualFollowersCount = 0;
picture = "https://scontent-ams3-1.cdninstagram.com/vp/885ac17fe17809de22790f0559f61877/5CD13A1C/t51.2885-19/s150x150/39312159_480582069091253_3011569611268161536_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
pk = 8513861541;
profilePicId = "1857507164564653723_8513861541";
profilePicUrl = "https://scontent-ams3-1.cdninstagram.com/vp/885ac17fe17809de22790f0559f61877/5CD13A1C/t51.2885-19/s150x150/39312159_480582069091253_3011569611268161536_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
reelAutoArchive = on;
username = "i_am_variable";
},
{
byline = "467 followers";
followerCount = 467;
friendshipStatus = {
following = 0;
"incoming_request" = 0;
"is_bestie" = 0;
"is_private" = 0;
"outgoing_request" = 0;
};
fullName = undefined;
hasAnonymousProfilePicture = 0;
id = 8657882817;
isPrivate = 0;
isVerified = 0;
latestReelMedia = 1547794887;
mutualFollowersCount = 0;
picture = "https://scontent-ams3-1.cdninstagram.com/vp/fb3c992c899aa269bdce2c4c1db8575b/5CD068BA/t51.2885-19/s150x150/46378106_2062632390480778_1266491662662631424_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
pk = 8657882817;
profilePicId = "1931972067016763185_8657882817";
profilePicUrl = "https://scontent-ams3-1.cdninstagram.com/vp/fb3c992c899aa269bdce2c4c1db8575b/5CD068BA/t51.2885-19/s150x150/46378106_2062632390480778_1266491662662631424_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
reelAutoArchive = on;
username = "undefi.ned";
})
It's an array of dictionaries, I need to parse it in a proper way. That's my main issue.
If you know how to parse dictionary, then you should know how to make one ;) There are tools out there to make your own model class, like: http://www.jsoncafe.com/
EDIT: As suggested by Robert in the comment section below, you can learn Decodable.
You can use that to give yourself an idea how a model class could or should look like. Use it however you like. In a decent project, there could be tons of data, and you don't want to make a class model out of it especially if you're the only one handling the iOS project.
So we suppose, we have this json data, based on your post:
{
"id": 1,
"username": "dd",
"fullName": "dd",
"profilePicture": "ddd",
"isPrivate": true
}
We could make a model out of it like so:
//
// UserRootClass.swift
// Model Generated using http://www.jsoncafe.com/
// Created on January 18, 2019
import Foundation
class UserRootClass : NSObject {
var fullName : String!
var id : Int!
var isPrivate : Bool!
var profilePicture : String!
var username : String!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
fullName = dictionary["fullName"] as? String
id = dictionary["id"] as? Int
isPrivate = dictionary["isPrivate"] as? Bool
profilePicture = dictionary["profilePicture"] as? String
username = dictionary["username"] as? String
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if fullName != nil{
dictionary["fullName"] = fullName
}
if id != nil{
dictionary["id"] = id
}
if isPrivate != nil{
dictionary["isPrivate"] = isPrivate
}
if profilePicture != nil{
dictionary["profilePicture"] = profilePicture
}
if username != nil{
dictionary["username"] = username
}
return dictionary
}
}
The model class above was made using the tool I gave above, but I removed the NSCoding protocol methods.
I hope this helps! Good luck and welcome to Stackoverflow.
You can use Decodable if you have Struct instead of Class for easy parsing. Here is the example in Alamofire 5.0
struct SearchedUsers: Decodable {
let id: Int
let username: String?
let fullName: String?
let profilePicture: URL?
let isPrivate: Bool
}
AF.request("http://url_endpoint/").responseData { response in
do {
// data we are getting from network request
let decoder = JSONDecoder()
let response = try decoder.decode([SearchedUsers].self, from: response.data!)
} catch { print(error) }
}

swift 4 wrong json array parsing

I have the json like this:
{"result":0,"data":[{\"ID":7,"TITLE":"123"},{\"ID":8,"TITLE":"123"}]}
I have the struct like this:
struct ResponseResult: Decodable {
let result: Int
let data: [IdResponseResult]
}
struct IdResponseResult: Decodable {
let ID: Int
let TITLE: String
}
So, when I run request like this:
Alamofire.request("https://xxx.xxx.xxx",headers:headers).responseJSON { response in
if let json = response.data {
let decoder = JSONDecoder()
let result = try? decoder.decode(ResponseResult.self, from: json)
print(response.value)
completion(result)
}
}
and print response.value I'm getting this:
data = (
{
"ID" = 7;
TITLE = 123;
},
{
"ID" = 8;
TITLE = 123;
}
);
result = 0;
}
And I cant parse it. How can i resolve it??
the decoding failure is caused by the struct resp2
struct resp2: Decodable {
let ID: Int
let TITLE: String
}
you are defining TITLE:String, but in the JSON you have an Int "TITLE":123.
In case you really want a String (make sense, since is a "Title"), you may need to fix the server side.
edit:
I tried the Decodable as it is now and I am able to recover your structures, you may check with:
let string = "{\"result\": 0,\"data\": [{\"ID\": 7,\"TITLE\": \"123\"}, {\"ID\": 8,\"TITLE\": \"123\"}]}"
let data = string.data(using: .utf8)
do {
let decoder = JSONDecoder()
let result = try? decoder.decode(ResponseResult.self, from: data!)
print(result?.data.first?.TITLE ?? "") // 123
} catch _ {
}
Then I guess there has to be something weird when you receive the data from the service.

Resources