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!
Related
I got notification in didreceiveRemoteNotification but I can not cast userInfo to dictionary of type [String: Any]
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let dict = userInfo as! [String: Any]
if let response = dict["message"] as? [String: Any], let baseResponse = Mapper<NotificationModel>().map(JSON: response) {
//do some stuff
}
}
when I try to cast dict["message"] as! [String: Any] error happens and it says:
Could not cast value of type '__NSCFString' (0x1cfa84f90) to 'NSDictionary' (0x1cfa85bc0).
Here is dict["message"] when I print it in console:
▿ Optional<Any>
- some : {"sender":
{"avatar_url":"http:\/\/api.moneyar.com\/APIs\/images\/15783070400.jpg","user_id":"15783","name":"mahdi moqadasi"}
,"conversation_id":"15783"
,"message_id":103597,
"time":1546778745,
"type":1,"message":"foo"
}
For the following answer, the code is not tested against a compiler, there might be some typo issue that could be easily fixed, some of them are intentionally done to exergue the logic behind it, and not add with if let/guard let, as?, etc. that are needed but add noise in the explanation.
I won't repeat #vadian answer, which is correct an explain why it fails.
So we are clear that dict["message"] is a String.
A piece of information that you seem to be missing in the JSON acronym is for what stands the "N": Notation.
When you printed dict["message"], you didn't have really a key/value object, you have a String representing a key-value object, but not in a Swift representation. You printed JSON Stringified (because it's clearly more readable that hex data JSON). If after the answer you print jsonDict, you'll see that the output structure might be different.
So, as always, your basic tools are:
Data <== data(encoding:)/init(data:encoding:) ==> String
Data <== jsonObject(with:options:)/data(withJSONObject:options:) ==> Array or Dictionary //I bypass voluntarily the specific case of String at top level
Let's do it then!
let jsonStringifiedString = dict["message"] as String
let jsonStringifiedData = jsonStringifiedString.data(using: .utf8) as Data
let jsonDict = try JSONSerialization.jsonObject(with: jsonStringifiedData, options: []) as [String: Any]
let baseResponse = Mapper<NotificationModel>().map(JSON: jsonDict)
If I were you, I'd look into Mapper if there is no way to do something like:
let baseResponse = Mapper<NotificationModel>().map(JSONData: jsonStringifiedData)
or
let baseResponse = Mapper<NotificationModel>().map(JSONString: jsonStringifiedString)
Because there are sometimes JSONStringified embedded in JSON, where you might need to call it on a String or on a Data directly.
Or just because the basic URLSession request returns a Data object in its closure, and you want to use it directly.
The error
Could not cast value of type '__NSCFString' (0x1cfa84f90) to 'NSDictionary' (0x1cfa85bc0).
is clear. The value of key message is a string
of type is the real type
to is the expected wrong type
if let response = dict["message"] as? String, ...
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.
}
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.
I have a function in Swift that needs to be able to handle multiple types. Specifically, it needs to be able to parse both Dicts and Strings.
The problem I have is the Dicts could be several types, depending on their origin. So I could be provided with [String:Any] or [String:String] (coming from Swift) or [String:AnyObject] (coming from objc). The top level parsing function takes Any, which it then tests for specific types and attempts to parse them.
At first I just tried testing for if let dict = object as? [String:Any], but if I passed in another type [String:AnyObject] or [String:String] it failed. So I tried testing each type:
func parseLink(object: Any) {
if let dict = object as? [String:Any] {
return self.parseDict(dict)
} else if let dict = object as? [String:AnyObject] {
return self.parseDict(dict)
} else if let dict = object as? [String:String] {
return self.parseDict(dict)
} else if let string = object as? String {
return parseURL(string)
}
}
func parseDict(dict: [String:Any]) { ..... }
So I've created some Unit Tests to test the behavior:
func testDictTypes() {
let testDict: [String:Any] = [ "orgId" : "123456789" ]
let link = OrgContextLinkParser().parseLink(testDict)
XCTAssertNotNil(link)
let testDict1: [String:AnyObject] = [ "orgId" : "123456789" ]
let link2 = OrgContextLinkParser().parseLink(testDict1)
XCTAssertNotNil(link2)
let testDict3: [String:String] = [ "orgId" : "123456789" ]
let link3 = OrgContextLinkParser().parseLink(testDict3)
XCTAssertNotNil(link3)
}
This all compiles fine, but I get a fatal runtime error if a [String:AnyObject] is passed in. This is troubling since Swift's type system is supposed to prevent these kind of errors and I get no warning or errors thrown when I compile.
I also really don't want to duplicate the exact same logic multiple times just to handle different dict types. I.E., handling [String:Any], [String:AnyObject] and [String:String] have virtually the exact same logic.
The only possible solution I've seen is to actually duplicate the dictionary, which seems rather expensive (Convert [String: AnyObject] to [String: Any]). For performance reasons, it seems better to just copy paste the code and change the function signatures... but really!? That's seems excessive.
The best solution seems to be to parse a Dict as [String:AnyObject] and copy the value only if it's [String:Any]:
if let dict = object as? [String:Any] {
var objDict: [String:AnyObject] = [:]
for (key, value) in dict {
if let obj = value as? AnyObject {
objDict[key] = obj
}
}
return self.parseDict(objDict)
I don't particularly like this, but so far it's be best I've been able to come up with.
Does anyone have any idea how to handle this properly? I'm especially concerned that I can cast Any as [String:AnyObject], pass it to a function that takes [String:Any] and I get no compiler errors, even though it crashes at runtime.
So I have created a function which I use for all my requests, which will retry the request if fails and also add some of my headers to all requests for security.
The problem I am having is if data is nil, no values are being set in data.
Where am I going wrong here?
func performAndRetryRequestWithURL(method: Alamofire.Method, url: String, parameters: [String: AnyObject]?, completionHandler:(AnyObject?) -> Void) {
var data: [String: AnyObject]?
if (parameters != nil) {
data = parameters!
} else {
data = [String: AnyObject]?()
}
var unixTime = NSDate().timeIntervalSince1970
// I tried both ways to add timestamp
data?["timestamp"] = unixTime
data?.updateValue(unixTime, forKey: "timestamp")
data?.updateValue(hash("myfakekey"), forKey: "key")
println(data)
If parameters has data it seems to append, but if parameters is nil, data will be nil also.
Make data not optional:
var data = parameters ?? [String: AnyObject]()
If parameters is not nil, data will be assigned to parameter's unwrapped value. Otherwise, it will be initialized to an empty, non-optional dictionary.
The problem is in this line:
data = [String: AnyObject]?()
This looks at first glance like it should create an empty dictionary of type [String: AnyObject], but what it actually does is create an Optional dictionary that is initialized to nil. Get rid of the ? and it should work:
data = [String: AnyObject]()
Or, alternatively, since you've already identified data as being a dictionary of type [String: AnyObject]?, you could simply write:
data = [:]
That would create an empty dictionary as well.