I read a snippet from this POST but have not quite understood.
https://www.hackingwithswift.com/example-code/system/how-to-parse-json-using-nsjsonserialization
I am confused on some syntax in the following snippets.
In the following, I am not knowing why try goes here, it is strange syntax to me. Any information about try and as! for this expression?
let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
In the following, I am not knowing what is as? [String] doing?
if let names = json["names"] as? [String] {
I know this question might be fundamental, I just need a keyword for me to search the related anwser, thanks. Here is my whole code block.
// define a string
let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"
// convert the string into NSUTF8StringEncoding
let data = str.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// put the following statements in try catch block
do {
// not knowing why try goes here, strange syntax to me.
// any higher conception about try and as! for this expression
let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
// json should be an object(dictionary, hash) use the key "names" to store string array into names
// not knowing what is `as? [String]` doing? any keyword for this syntax?
if let names = json["names"] as? [String] {
print(names)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
// not knowing why try goes here, strange syntax to me. what if it fails? as! is what syntax, any keyword to search for it?
The try here is to basically to try to perform the function on that line. If it fails it will go to the catch. Which in this case will just print("Failed to load: \(error.localizedDescription)")
// json should be an object(dictionary, hash) use the key "names" to store string array into names // not knowing what is as? [String] doing? any keyword for this syntax?
The line that you are confuse about is performing a if else on the json object. It check for a key that is name and also want to ensure that its value is a String therefore the as?. In this case if the key name does not exist it will not met the condition. If the value of name is not a String it will not met the condition.
Like what #Martin R mention in the comments, you should read up more on Type Casting in Swift. This should help you. Explanation with some example if you have trouble with Apple Documentation.
As for try catch it is actually use in other languages as well not just Swift. You can read up more here.
Related
I'm fairly new to swift programming. I am playing with sockets and am trying to understand this snippet:
socket.on("Hello") { [weak self] (data, ack) in
if let data = data[0] as? [String: String],
let rawMessage = data["msg"]
{
DispatchQueue.main.async {
self?.messages.append(rawMessage)
}
}
}
I can understand the first bit; socket.on("Hello"). It declares what to do when the socket receives "Hello". And I also know that self?.messages.append(rawMessage) is appending the message to a list. However, the format of the other parts is confusing to me. I am trying to modify it so I can accept any type of data. I think that if let data = data[0] as? [String:String] is filtering the data. But when I remove it and its parentheses, XCode throws error's at me. Could you please explain to me this code and how I should go about modifying it?
Please guys i need to parse a string to look like these in swift
"[{"question":9, "answer":25}", "question\":10, "answer":27}]"
where the index and value are dynamically gotten from a loop. I was able to get to these
["{\"question\":9, \"answer\":25}", "{\"question\":10, \"answer\":27}", "{\"question\":11, \"answer\":29}", "{\"question\":12, \"answer\":33}", "{\"question\":13, \"answer\":37}"]
so i have tried this
for i in 0..<answersForQuestionInPage.count{
let questions = answersForQuestionInPage[i] as Answer
do {
let data = try JSONEncoder().encode(questions)
// 2
let string = String(data: data, encoding: .utf8)!
answers.append(string)
print("This is the main value \(string)")
} catch{
}
}
this still gives me an array with this format
["{\"question\":9, \"answer\":25}", "{\"question\":10,
\"answer\":27}", "{\"question\":11, \"answer\":29}",
"{\"question\":12, \"answer\":33}", "{\"question\":13,
\"answer\":37}"]
with the object
"{\"question\":9, \"answer\":25}"
still wrapped in a string liteal " " what i want is for this return array to be in this format
[{"question":9, "answer":25}, {"question":10,
"answer":27}, {"question":11, "answer":29},
"{"question":12, "answer":33}, {"question":13,
"answer":37}]
I didn't understand the whole thing, but you said you need to parse the String, but I think you meant JSON. So, you can do it like this and get the values. Do let me know if it is what you needed, otherwise please add clarity in your question and I will edit and update my answer accordingly.
struct Quiz: Decodable {
let question, answer: Int
}
private func fetchQuizzes() {
//After getting the data from API, you can do this
guard let quiz = try? JSONDecoder().decode([Quiz].self,from: data) else { print("Unable to parse"); return }
print(quiz)
print(quiz.first?.answer) //First Answer
}
Just like Rob said before, you have a JSON here.
Using Robs code you decode the given JSON and create an array of Quiz objects (Robs struct).
You can now work with that array and transform it to your needs.
API gives me back a variable that has type Any. It looks like this when I print it.
{
"sender" : "Kira",
"created" : "08.05.2018",
"text" : "Cncncm"
}
I tried to use SwiftyJSON to cast it like this let mydata = JSON(data) but it failes. I tried to use Swift 4 decoding technique but that failed as well. I tried to do this let myData = data as? Dictionary<String, String> but it fails again.
I am clueless what to do here. Any tips or solutions?
Finally a chance to demonstrate one of the Codable protocols hidden gems. Please run the following in a Playground:
import Cocoa
let jsonData = """
{
"sender" : "Kira",
"created" : "08.05.2018",
"text" : "Cncncm"
}
""".data(using: .utf8)!
struct SenderText: Codable {
let sender: String
let created: Date
let text: String
}
let dayFormatter = DateFormatter()
dayFormatter.dateFormat = "dd.MM.yyyy"
let date = dayFormatter.date(from:"08.05.2018")
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dayFormatter)
do {
let sendText = try decoder.decode(SenderText.self, from: jsonData)
print(sendText)
} catch {
print(error)
}
The sheer elegance of how easy it is to define such an intricate parser mapping a messy JSON-string to your favourite struct will hardly ever stop to amaze me. No matter how weird your date format looks, it is hardly more than 3 lines away from being parsed during the process.
There is something in regard to casting you should note though: In Swift, as in most object oriented languages, you can only cast something to something else if (and only if) it already is something else in the first place (but that knowledge has been lost somewhere). Since your String is "just" a String (in disguise of an Any maybe) you won't be able to cast it to anything else. However the Codable protocol provides you with a terrific means to decode from the Strings Data with astonishing ease. This process should not be mistaken as a cast, even if it looks largely the same. It is the creation and initialisation of another, more fittingly structured object from a simple piece of Data that you are likely to have gotten from your average web service of choice.
Great so far, at least in my book.
You can parse it like this as it's a json string
let trd = yourVar as? String
if let data = trd?.data(using: String.Encoding.utf8) {
do {
var content = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String:String]
print(content)
}
catch let error as NSError {
print(error)
}
}
This question already has answers here:
Printing optional variable
(15 answers)
Cannot get rid of Optional() string
(5 answers)
Closed 5 years ago.
this is my code:
func uplouadPost() {
// shortcut to data to be php
let parseJSON = UserDefaults.standard.value(forKey: "parseJSON") as?
NSDictionary
let userID = parseJSON!["userID"] as! String
....
if error == nil {
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!,
options: .mutableContainers) as? NSDictionary
print("========================\(userID)")
print I get ========================Optional(23)
But I don't want Optioanl
how to just get 23
What's more, I tried to unwrap the "userID" by this way. However it doesn't work
let unwrappedUserID = [userID]
print (unwrappedUserID)
Thank you guys
The best method of checking for and unwrapping Optionals is using either a guard statement or if-let statements.
So suppose you have a dictionary defined as:
let parseJson: [String: Any]? = ["userId": 23]
Even though it has a value, it is still an Optional type so to access the values in the dictionary we want, we need to check for the possibility of it having a nil value, assuming we didn't create it and know that it has a real value.
Using if-let statements, we can do:
if let json = parseJson {
// WILL ONLY EXECUTE IF parseJson IS NOT nil
// json is now of type [String: Any] instead of [String: Any]?
let userId = json["userId"]
print("=========\(userId)") // =========23
}
This creates a new scope where the json constant now contains the non-nil and unwrapped optional value of parseJson. In that case if parseJson did equal nil, then the code inside of the if-let block would not execute.
The other option is a guard statement, which is very similar.
guard let json = parseJson else {
// ONLY EXECUTES IF parseJson IS nil
// MUST EXIT CURRENT SCOPE AS A RESULT
return // or throw NSError()
}
let userId = json["userId"]
print("==========\(userId)") // ==========23
In this case, the code after the guard will only execute if parseJson is non-nil because the inside of the guard block must exit the scope.
Try this, let unwrappedUserID = userID!
While encoding JSON, I´m unwrapping stuff with an if let statement, but I'd like to make a variable globally available
do {
if
let json = try JSONSerialization.jsonObject(with: data) as? [String: String],
let jsonIsExistant = json["isExistant"]
{
// Here I would like to make jsonIsExistant globally available
}
Is this even possible? If it isn't, I could make an if statement inside of this one, but I don't think that would be clever or even possible.
delclare jsonIsExistant at the place you want it. If you are making an iOS App, than above viewDidLoad() create the variable
var jsonIsExistant: String?
then at this point use it
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: String],
let tempJsonIsExistant = json["isExistant"] {
jsonIsExistant = tempJsonIsExistant
}
}
This could be rewritten like so though
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: String] {
jsonIsExistant = json["isExistant"]
}
} catch {
//handle error
}
If handled the second way, then you have to check if jsonIsExistant is nil before use, or you could unwrap it immediately with a ! if you are sure it will always have a field "isExistant" every time that it succeeds at becoming json.
It doesn't make sense to expose a variable to the outside of an if let statement:
if let json = ... {
//This code will only run if json is non-nil.
//That means json is guaranteed to be non-nil here.
}
//This code will run whether or not json is nil.
//There is not a guarantee json is non-nil.
You have a few other options, depending on what you want to do:
You can put the rest of the code that needs json inside of the if. You said you didn't know if nested if statements are "clever or even possible." They're possible, and programmers use them quite often. You also could extract it into another function:
func doStuff(json: String) {
//do stuff with json
}
//...
if let json = ... {
doStuff(json: json)
}
If you know that JSON shouldn't ever be nil, you can force-unwrap it with !:
let json = ...!
You can make the variable global using a guard statement. The code inside of the guard will only run if json is nil. The body of a guard statement must exit the enclosing scope, for example by throwing an error, by returning from the function, or with a labeled break:
//throw an error
do {
guard let json = ... else {
throw SomeError
}
//do stuff with json -- it's guaranteed to be non-nil here.
}
//return from the function
guard let json = ... else {
return
}
//do stuff with json -- it's guaranteed to be non-nil here.
//labeled break
doStuff: do {
guard let json = ... else {
break doStuff
}
//do stuff with json -- it's guaranteed to be non-nil here.
}