Conver FIRTimestamp to JSON - json

I'm having problems converting a document to Firebase, but I can not convert the FIRTimestamp data.
let json = try? JSONSerialization.data(withJSONObject: d.data(), options: .prettyPrinted)
Error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (FIRTimestamp)'

To remove FIRTimestamp from JSON
struct leadDocument: Codable {
let state: String
let details: String
}
let dataDescription = document.data() // your json response or value
var leadData = dataDescription
_ = leadData.removeValue(forKey: "serverTimeStamp") // remove FIRTimestamp
let requestData = try! JSONSerialization.data(withJSONObject: leadData, options: JSONSerialization.WritingOptions.prettyPrinted) as NSData?
let results = try JSONDecoder().decode(leadDocument.self, from: requestData! as Data)
OR covert FIRTimestamp to JSON
let db = Firestore.firestore()
let settings = db.settings
settings.areTimestampsInSnapshotsEnabled = true
db.settings = settings
let timestamp: Timestamp = document.get("serverTimeStamp") as! Timestamp
let date: Date = timestamp.dateValue()
print(date)

If you're trying to serialize the contents of a FIRTimestamp, you should either:
Convert it to a NSDate with dateValue, and serialize that instead
Convert it to seconds (and nanoseconds if desired) using the linked methods
When you deserialize those values, you may to convert them back into a FIRTimestamp with one of its constructors.

Related

Create ordered json string to use for key signing

To understand the problem I need to provide some background to what I am trying to achieve.
I am trying to create a JWT with this library that will be signed with a private key.
My problem is that the dictionary that I use to create the JSON is unordered, which therefore results in an unordered JSON string.
The code below prints the JSON string in any order.
let dictionary = ["aKey": "aValue", "anotherKey": "anotherValue"]
if let theJSONData = try? JSONSerialization.data(
withJSONObject: dictionary,
options: .prettyPrinted
),
let theJSONText = String(data: theJSONData,
encoding: String.Encoding.ascii) {
print("JSON string = \n\(theJSONText)")
}
let privateKey = theJSONText.data(using: .utf8)
let jwtSigner = JWTSigner.hs256(privateKey: privateKey)
let signedJWT = try myJWT.sign(using: jwtSigner) // This produces a JWT with an invalid signature
The result of this is that my JWT produces an invalid signature. How do I produce a JSON string that has order?
JSONEncoder encodes a dictionary always in the same order (which however might not be the given order of the dictionary). But maybe it fulfills your needs.
if let theJSONData = try? JSONEncoder().encode(dictionary) {
let theJSONText = String(data: theJSONData, encoding: .utf8)!
print("JSON string = \n\(theJSONText)")
}
And there is also the sortedKeys option of OutputFormatting
And – once again – never prettyPrint anything which is going to be sent to a server.

"Invalid type in JSON write (__NSTaggedDate)" when trying to decode JSON date

I'm getting an error "Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (__NSTaggedDate)'" when I try to decode a JSON object from a databank that has a date variable. The error happens in the line: let jsonData = try JSONSerialization.data(withJSONObject: self) of the code below:
extension Array where Element == Dictionary<String,Any> {
func decode<T>() throws -> [T] where T: Codable {
let jsonData = try JSONSerialization.data(withJSONObject: self)
// 1.
let decoder = JSONDecoder()
// 2.
return try decoder.decode([T].self, from: jsonData)
}
}
I'm using this method to turn the result of a Postgres SELECT query into an array of Codable objects using the PG dependency.
case .success(let result):
let array = result.rows.map { Dictionary($0) }
let objects = try! array.decode() as [Model]
completion(objects,nil)

Use Swift Decoder to pull attributes from JSON array

I have a JSON array created using this call:
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [Any] else {
print("This is not JSON!!!")
return
}
I am trying to get elements from the JSON objects in the array to display them using the following code:
struct sWidget: Codable{
var createdBy: String
var createdDate: Date
var status: String
var widgetNumber: String
var updatedBy: String
var updatedDate: Date
}
do {
let decoder = JSONDecoder()
for (index, value) in json.enumerated() {
let currentWidget = try decoder.decode(sWidget.self, from: json[index] as! Data)
let currentNum = currentWidget.widgetNumber
//print(currentNum)
widgetNums.append(currentNum)
}
}
catch {
print("decoding error")
}
The code compiles but when I run it I get this error in the output:
Could not cast value of type '__NSDictionaryM' (0x1063c34f8) to
'NSData' (0x1063c1090). 2018-08-09 09:41:02.666713-0500
TruckMeterLogScanner[14259:1223764] Could not cast value of type
'__NSDictionaryM' (0x1063c34f8) to 'NSData' (0x1063c1090).
I am still investigating but any tips would be helpful.
Did you try that fetching objects like above mentioned? Because i see that you are using Codable. Fetching is very simple with that actually.
let yourObjectArray = JSONDecoder().decode([sWidget].self, data: json as! Data)
May be this line can be buggy but you can fetch them with one line.
Extending #Cemal BAYRI's answer:
JSONDecoder() throws, so make sure to either us try? or try (don't forget do-catch with try)
guard let data = content as? Data else {
return [sWidget]()
}
let jsonDecoder = JSONDecoder()
1. try?
let yourObjectArray = try? jsonDecoder.decode([sWidget].self, data: data)
2. try
do {
let yourObjectArray = try jsonDecoder.decode([sWidget].self, data: data)
} catch let error {
}
Note: You would need to take care of Data and Date formatting. Below is an example for Date:
jsonDecoder.dateDecodingStrategy = .iso8601
You can also check it out here

Swift 4 JSONSerialization.jsonObject

I am using Xcode 9.2 and Swift 4. How can I check if the returned json data is null?
Now it gives me error Type 'Any' has no subscript members at line if json[0]["data"]
var json: NSMutableArray = []
var newsArray: NSMutableArray = []
let url = URLFactory()
var data = try! NSData(contentsOf: url.getURL()) as Data
do {
json = try JSONSerialization.jsonObject(with: data, options: []) as! NSMutableArray
if json[0]["data"] {
// data is not null
}
} catch let error as NSError {
// handle error
}
My JSON returns something like this:
{
"data":
[
{
"news_id":123,
"title":"title",
"news_date":"2017-02-08 21:46:06",
"news_url":"url",
"short_description":"description",
"category_id":4,
"category_name":"Health",
"latlng":
[
{
"lat":"43.003429",
"lng":"-78.696335"
}
]
}
{ ....
}
If you're using Swift 4, I might suggest JSONDecoder:
First, define the types to hold the parsed data:
struct ResponseObject: Codable {
let data: [NewsItem]
}
struct NewsItem: Codable {
let newsId: Int
let title: String
let newsDate: Date
let newsURL: URL
let shortDescription: String
let categoryID: Int
let categoryName: String
let coordinates: [Coordinate]
// because your json keys don't follow normal Swift naming convention, use CodingKeys to map these property names to JSON keys
enum CodingKeys: String, CodingKey {
case newsId = "news_id"
case title
case newsDate = "news_date"
case newsURL = "news_url"
case shortDescription = "short_description"
case categoryID = "category_id"
case categoryName = "category_name"
case coordinates = "latlng"
}
}
struct Coordinate: Codable {
let lat: String
let lng: String
}
And then you can parse it:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
let responseObject = try decoder.decode(ResponseObject.self, from: data)
print(responseObject.data)
} catch {
print(error)
}
Clearly, if your JSON is different, you might need to change your objects accordingly (e.g. it struck me odd that latlng was an array of coordinates). Also, you can define custom init(from:) methods if you want to convert some of these strings into numbers, but I'd rather fix the JSON, instead (why are latlng returning string values rather than numeric values).
For more information, see Encoding and Decoding Custom Types.
As an aside, I'd advise against this pattern (note, this is your network request logic, but excised of NSData):
let data = try! Data(contentsOf: url.getURL())
That will retrieve the data synchronously, which can be problematic because
the app will be frozen while the data is being retrieved resulting in a poor UX;
you risk having your app killed by the watchdog process which looks for frozen apps; and
you don't have robust error handling and this will crash if the network request fails.
I'd suggest using URLSession:
let task = URLSession.shared.dataTask(with: url.getURL()) { data, _, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}
// now parse `data` like shown above
// if you then need to update UI or model objects, dispatch that back
// to the main queue:
DispatchQueue.main.async {
// use `responseObject.data` to update model objects and/or UI here
}
}
task.resume()

Creating a certain JSON data structure in Swift

For backend communication, my app requires a method to create a certainly structured JSON, and thats where i struggle.
The created JSON is supposed to look like this:
{
"data": {
"color":"yellow",
"size":"big"
}
}
Serializing a Dictionary with the required Data does not really seem to have the option to format the content properly, my best results look like this:
Optional({
Kategorie = Strassenschaeden;
PLZ = 46282;
Strasse = Erftweg;
Unterkategorie = Schlagloch;
})
I didnt find any helpful weblinks for my problem, and since im new to Swift and its documentation Im kinda stuck at the moment.
So my questions are:
Whats the preferred data structure for my JSON data (Dictionary/Array) and how do I create a JSON that is well-formated?
Thanks in advance :)
Edit: This is the interesting part of what i have used to achieve my "best result":
var data: [String: String] = ["Kategorie": "\(Kategorie)", "Unterkategorie": "\(Unterkategorie)", "Strasse": "\(Strasse)","PLZ": "\(PLZ)"]
self.post(data, url: "http://*************") { (succeeded: Bool, msg: String) -> () in
var alert = UIAlertView(title: "Success!", message: msg, delegate: nil, cancelButtonTitle: "Okay.")
func post(params : Dictionary<String, String>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
let JSONData:NSData = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted, error: &err)!
var json = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &err) as? NSDictionary
println(json)
Here
let JSONData:NSData = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted, error: &err)!
var json = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &err) as? NSDictionary
you are converting the params dictionary to JSON data – and then you convert the
JSON data back do a dictionary! What you probably want is to create a string
from the JSON data:
let jsonData = NSJSONSerialization.dataWithJSONObject(params, options: .PrettyPrinted, error: &err)!
let json = NSString(data: jsonData, encoding: NSUTF8StringEncoding)!
println(json)
Remarks:
Properties and variables should have names starting with lower case letters, e.g.
jsonData.
The explicit type annotation :NSData is not needed here, the Swift compiler can
infer the type automatically.
The option can be given as .PrettyPrinted instead of NSJSONWritingOptions.PrettyPrinted, the compiler infers the enumeration type
automatically.
Instead of forced unwrapping with ! you should use optional binding to check
for success.
Just an itch, that no one here recommend swiftyJSON for working with JSON in Swift.
Try it, you will lose your pain of dealing with JSON.
https://github.com/SwiftyJSON/SwiftyJSON
To Read JSON
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let jsonObject = JSON(data: jsonData!)
To Write JSON
let jsonString = jsonObject.rawString()