How to handle Dictionary with missing key - ios

I have a dictionary with info (name, age etc) that I need to use in a tableView to populate a profile viewController.
My problem is that I dont know if the user for example has added his name, and therefore if there is a key = "name".
So when I index into the dictionary
print("firstname: \(userDocument["name"]!["first"] as! String)")
the app crash if the key does not exist..
How can I avoid this? should I use if let on all the keys ?
I tried:
print("firstname: \(userDocument["name"]?["first"] as? String)")
but then I get the Optional(Elon Musk) and I dont what that as the label.text.
Any help is much appreciated! Thank you

How about:
var defaultName = "unspecified"
print("firstname: \((userDocument["name"]?["first"] as? String) ?? defaultName)")
This checks whether (userDocument["name"]?["first"] as? String) contains a value, and provides a substitute (defaultName) if it doesn't. You can change the substitute text to whatever you see fit.

The ! means "please crash if it doesn't exist". So what you wrote is crashing because you told the compiler to make it crash. If you don't want the crash, don't do that.
Use ? in the right places which will give you an optional value. Then you can use optional ?? defaultValue to substitute a value in case the optional is nil, or do whatever is appropriate if the value isn't there.

Related

Object (Optional Any): get elements

I'm trying to pass information with an observed notification in my app.
The standard way to do that, is to set the userinfo. However, the data I want to pass is a Set, not a dictionary.
So, I do this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object:self.productIds)
The object arrives fine, but now I'm unable to get to it:
in the console I do this:
po notification.object!
▿ 2 elements
- 0 : ZTJ
- 1 : ZTM
However, when I try to get to one of the elements, I get this:
po notification.object![0]
error: <EXPR>:8:21: error: value of type 'Any' has no subscripts
notification.object![0]
What am I doing wrong?
You know that notification.object is a Set, but the compiler doesn't because it's declared as Any, which means it could be anything, and so it can't find which implementation of object[0] it should use.
To read this object you need to cast it to a set.
if let mySet = notification.object as? Set<MyType> {
// Do whatever
}
Keep in mind that the object property of Notification is designed to be used as a filter, If you pass a value when adding an observer, you'll only get notifications that are sent with that exact same object.
The userInfo dictionary is to send related information, like your set. In this case I would send a dictionary like this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object: nil, userInfo: ["products": productIds])
The notification has an object type of Any?.
When you're poing it in the console, you're asking it to print its description, which Any can do.
When you're asking it to subscript, Any can't do that, because subscripting is not defined on that type. You need to cast it to the expected type:
po (notification.object as? [String])?[0]
In general, it's best to nail down the type of any Any as soon as you can. Think of Any as a box used to send things through the post. The first thing you do is open it and find out what's inside.

Swift 4 - querying facebook with FBSDKGraphRequest

I try to create a function for querying the Facebook database. Unfortunately, the syntax changed with the last version of swift. Maybe someone can post me the solution ?
Thx.
Func donneesFB
It's a lot easier to help you if you post your code as text instead of in an image.
Two things that will likely help you out here:
First, cast your result to the type of dictionary you are expecting before trying to access it:
guard let resultDict = result as? [String:Any] else { return }
You should now be able to use it like you tried to:
let nom = resultDict["name"] as? String
Secondly, for the error on your first line, simply get rid of the argument labels, nom, prenom and so on, leaving just the types.

Not using unwrapping in guard statements

I understand the use of optionals enough to know when its necessary to unwrap an optional using the exclamation point. Why is it that the exclamation point isn't needed in a guard statement?
This code works and compiles but doesn't use exclamation points:
struct Blog{
var author:String?
var name: String?
}
func blogInfo2(blog:Blog?){
guard let blog = blog else {
print("Blog is nil")
return
}
guard let author = blog.author, name = blog.name else {
print("Author or name is nil")
return
}
print("BLOG:")
print(" Author: \(author)")
print(" name: \(name)")
}
This code also works if you do put the exclamation points:
struct Blog{
var author:String?
var name: String?
}
func blogInfo2(blog:Blog?){
guard let blog = blog! else {
print("Blog is nil")
return
}
guard let author = blog.author!, name = blog.name! else {
print("Author or name is nil")
return
}
print("BLOG:")
print(" Author: \(author)")
print(" name: \(name)")
}
Isn't this a little contradictory or can someone clearly explain why the exclamation point isn't needed?
guard let unwrapped = optional is an Optional Binding (no link available directly to the correct book section, unfortunately). It safely tries to unwrap the value in the optional. If there is a value, the unwrap succeeds, and the value is assigned to the given name.
You should heavily favor the use of optional bindings, with either guard or if (the difference is the scope of the "unwrapped" name) over the use of forced unwrapping with !. A failed force unwrap is a fatal error; your program will simply crash.
I understand the use of optionals enough to know when its necessary to unwrap an optional using the exclamation point.
I have the feeling that you don't understand Swift Optionals enough if you make that assertion.
The contract behind an Optional is that it may or may not be nil; you don't know it and therefore you have to Unwrap it (like opening a box and see what's inside) before you can tell.
The box may be empty (nil Optional) or it may contain a value.
There are very few reasons to use the Forced Unwrap (!). Very Few, and it's generally considered a bad practice in most (but not all) cases.
To continue the analogy, by force unwrapping stuff, you're saying there is something in this box, and I want you to trust me and don't check.
Knowing that an Empty box will crash your application, this is a very dangerous thing to do, considering Optionals were introduced in Swift to protect you from these sort of crashes to begin with.
The if let/guard let statements basically peek inside the box and if there is something, they give it to you, but they also give you the chance to do something else in case the box is empty.
Xcode does force unwrapping when using IBOutlets because by design the contract is that those objects will be available by the time you can use them in your view controller, but unless you're 100% sure a value is not going to be nil then it's almost always better (and future proof) to use a guard statement (or an if).
In my experience, even when you know for sure, it's still safer to toss a guard and forget about the future problems that may arise.
The exclamation "force" unwraps an optional. guard let or if let unwraps it without forcing anything, so no exclamation point is used.

What is wrong with this line of Swift iOS Code?

I have created an iOS app using Swift and everything is working fine and dandy on the simulator. I get no errors or crashes at all, but when I submit my app to put up on the app store Apple rejects it and lets me know that it crashes when the user makes a selection. I cannot recreate this error/crash. I took the crash logs and symbolicated them. This line of code came up as the culprit for the crashes:
linksToPass = getLinks(season) as [String:[String]]
This line is trying to store the resulting Dictionary from the getLinks() function I created. It for sure is getting a dictionary and if there is no dictionary to send back I create a dictionary which has error information in it, so it is for sure returning a dictionary in that format no matter what. Seeing as I cannot recreate the crash, I am just trying to error check this line of code in any way possible so it does't crash when I resubmit to Apple.
I tried checking if the resulting dictionary was nil like so:
if(getLinks(seasons) != nil){
linksToPass = getLinks(season) as [String:[String]]
}
This is not valid though, and XCode lets me know that UInt8 is not compatible with NSDictionary or something of that nature.
I then fixed that line and changed it to this:
if(getLinks(seasons) != ["":[""]]){
linksToPass = getLinks(season) as [String:[String]]
}
I am just not sure if this is even a good way to check for errors. I was wondering if there were any suggestions on how I may go about making sure this line does not fail and result in a crash. Thank you very much.
EDIT:
Here is my getLinks() function if that helps add more info to the problem:
var season = ""
let hymn_links = Hymn_Links()
func getLinks (nameofseason:String) -> NSDictionary
{
switch (nameofseason)
{
default:
return ["Maps Not Found": []]
}
}
EDIT #2:
This is my updated getLinks() function with the use of optionals.
func getLinks (nameofseason:String) -> NSDictionary?
{
switch (nameofseason)
{
default:
return nil
}
}
Also in my statement of linksToPass I changed it to:
if let links = getLinks(season) as? [String:[String]]
{
linksToPass = links
hymnnames = [String] (linksToPass.keys)
}
There are some known issues with the Swift optimiser. Some people have resorted to shipping with debug builds.
My suggestion would be to test with an optimised build to see if you can reproduce it. You can then try shipping a debug build to the App store.
General Code Comments
Why are you returning an NSDictionary rather than a Swift dictionary anyway? Without knowing the contents and creation method for your hymn_links object I can't be sure how good it is.
I would avoid as casts until Swift 1.2 and stick to using as? and then handling the nil case. At least in your "Edit 2" a nil will cause a crash as nil cannot be cast to [String:[String]] although [String:[String]]? should be possible.
Can you guarantee that all of the items returned by the switch statement will never under any circumstances be nil? If not getLinks should return an Optional.
Note that is is virtually impossible for getLinks to know that one of the items will never be nil and in Swift un-handed nils are a crash waiting to happen. Unless all these methods correctly handle nil.
Return an Optional and handle that in the statement that calls getLinks.
Languages handle nils differently, Objective-C handles them rather well, Java and Swift by crashing. But Swift has a mechanism to handle nils without crashing: Optionals, use it.

fatal error: unexpectedly found nil while unwrapping an Optional value - Stanford Calculator

I'm watching the Stanford Swift lecturers on ItunesU and have some trouble in understanding.
The lecturer is typecasting a String to a Double at one point.
He did it as follows:
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
display.text is a UILabel
Anyhow, when I'm doing it like this my app crashes and I get the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
When I typecast the String like this:
(display.text! as NSString).doubleValue
it works fine.
I know there must be an optional var anywhere but I'm not that into it to say what is wrong at this point.
The issue is your display.text doesn't have a valid number in it's text. So it'll return nil.
So your expression will become:
return nil!.doubleValue
And it'll definitely crash.
For converting a String to double your second approach is more suitable. Means using:
(display.text! as NSString).doubleValue
is a better approach for this case.
Reference : NSNumberFormatter Class Reference
- numberFromString:
Returns an NSNumber object created by parsing a given string.
Declaration
Swift
func numberFromString(_ string: String) -> NSNumber?
Objective-C
- (NSNumber *)numberFromString:(NSString *)string
Parameters
string
An NSString object that is parsed to generate the returned number
object.
Return Value
An NSNumber object created by parsing string using the receiver’s
format. Returns nil if there are no numbers in the passed string.
Discussion
Any leading spaces in a string are ignored. For example, the strings “
5” and “5” are handled in the same way.
Mika, I just figured out why my calculator program is crashing with the same error message as you. Remember when we created the enter key, it was still "bound" to the appendDigit function and we had to option/right click on the button to break the connection. I was fighting the same issue as you, and it turned out that I had copy/pasted my operation keys and all of them were still connected to the appendDigit function. That meant every time I hit the "x" key, it was sending a non-number to the appendDigit function which when unwrapped was the equivalent of "nil".
Hope that helps.
Wilton
In the first form, numberFromString returns nil if display.text does not contain a number.
My guess is that in this first case your display.text is not initialized with a number. So numberFromString return a nil that you are trying to unwrap using !. This throw the error.
In the second form, you are typecasting as NSString no matters what is the content (empty, "1.0", "foo"...) and then calling doubleValue which will return 0.0 if the string doesn’t begin with a valid text (different than a number).This doesn't throw an error, in the worst case it will return 0.0.
I'm currently following the lectures and ran into the same issue. user3763062 and I had the same problem: we both accidentally left the enter key bounded to appendDigit function.
Deleting the connection solved my issue.
Well, I guess you learning the Swift programming and making your first calculator.
If I'm right, you getting this crash because you forgot to delete a message that your Enter button sending. In this case you getting an error because Enter symbol does not represent a number.
I was running into the same issue when I tested the app on my iPhone. The problem was the dot, for decimal numbers. My iPhone region was set to Romania, where we use comma to separate decimals and I believe the numberFromString depends on the region when making transformations. After I changed the region to US, the nil exception did not appear.
You can use a conditional cast to Double and nil coalescing operator to make sure it does't return nil as follow:
var myNumber = NSNumberFormatter().numberFromString("10.5") as? Double ?? 0
println(myNumber) // 10.5
myNumber = NSNumberFormatter().numberFromString("10") as? Double ?? 0
println(myNumber) // 10.0
myNumber = NSNumberFormatter().numberFromString("abc") as? Double ?? 0
println(myNumber) // 0.0
For me, this works:
return Double(display.text!)!

Resources