Not using unwrapping in guard statements - ios

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.

Related

How to avoid crashes in tableView(_:cellForRowAt)?

In the Fabric, I see a lot of crashes in the "tableView(_:cellForRowAt)" section. There is not a certain scenario for this exceptions. Anytime and in the any screen it can occur.There is no data for analysing the crashes. Only I know there are crashes in "tableView(_:cellForRowAt)".
I want to prevent this kind of exceptions although I do not know the root cause. Can I use a method like preventing NullPointer Exception (if (!null)) ?
Below two crashes in the different code sections ;
let XXX = Constants.sharedInstance.url+"/service/photo/"+userdas[(indexPath as NSIndexPath).row].id!+"/"+userdas[(indexPath as NSIndexPath).row].photo!+"/2"
and
self.notificationModel[indexPath.row].userNot.XXX?.XXXImageView = image
From your code, it's clear that you're making a couple of explicit force unwraps that could lead you to crash.
userdas[(indexPath as NSIndexPath).row].id!
userdas[(indexPath as NSIndexPath).row].photo!
self.notificationModel[indexPath.row].userNot.XXX?.XXXImageView
I guess that in the third case XXXImageView is implicitly unwrapped UIImageView that also might be nil.
To avoid the crash in your first section you can use a guard
guard let id = userdas[indexPath.row].id,
let photo = userdas[indexPath.row].photo else {
return
}
let XXX = Constants.sharedInstance.url+"/service/photo/"+id+"/"+photo+"/2"
I'm not sure what you're doing in the second section, but you just need to check that you unwrapped parameters aren't nil as well

Understanding Optionals in Swift when creating AVAudioPlayers

I'm new to Swift, making the transition from Obj-C and not having much experience of other languages.
I've read up about optionals, understand the overall premise and sort of get why they might be a good thing. But I'm struggling to grasp how they work in practice. The following code works fine, but I'd like an explanation of why it is the way it is.
I'm making an app that plays audio from a file using AVAudioPlayer. I'm firstly creating an instance of the AVAudioPlayer class:
var player: AVAudioPlayer?
Q1. Why does this have to be an optional type? Presumably because it has yet to be initialised / has yet to contain any data?
The code I am then using to load audio into player is as follows:
guard let audioFile = Bundle.main.path(forResource: "sample-audio", ofType: "caf") else {
print("sample-audio.caf does not exist in the bundle.")
return
}
let audioURL = URL(fileURLWithPath: audioFile)
do {
player = try AVAudioPlayer(contentsOf: audioURL)
player?.delegate = self
player?.prepareToPlay()
} catch {
print(error)
}
Q2. If player is an optional type, why is the ? symbol omitted here when assigning the URL to it using the try/catch statement?
Q3. If an optional is like a "box you have to unwrap", containing either a value or nothing/nil, then how is it possible to directly call properties and functions on it such as player?.delegate and player?.prepareToPlay()
Sorry, three questions, but interrelated…
Answers will really help me get my head around this fundamental aspect of Swift.
Q1: It doesn't have to be optional, but yes it hasn't been initialized at that point. You have several options: ? and initialize it when you need to, ! and you must initialize it before you try to use it or else your program will crash, or initialize it at the point of declaration. What you do depends entirely on the context. In this particular case it's probably good practice to have it optional.
Q2: There's no need for a ? on that line. Where would it go? The compiler knows player is optional, and nothing is being unwrapped, you're just assigning a value to your optional variable.
Q3: Optional chaining allows you to call things on optional objects without actually unwrapping them first. It just implicitly does that work for you so your code can be much more concise. If the object is nil nothing happens, otherwise it will run the call. Note that it cascades the optionality down, so even if the property etc is not optional, it will inherit the optionality in a statement like that.
do-catch process always related to optional suppose you declared the player like this
var player: AVAudioPlayer!
and the do block fails , it's may be other parts of the VC uses player , so once it's referenced the app will crash because of ! , that's why it's declared ?
//
what does it means ? here
player?.delegate = self
simply if player is nil the statement will not run , and if not then the delegate will be set
//
You don't do
player? = //////
because it meaningless and will cause the statement not to be run , as the key behind it is calling properties and methods of player if it's not nil and here there is no thing to attach or call
A1: Yes because it has yet to be initialised / has yet to contain any data. But in this case – the property is going to be initialized always before being used – you can declare it as implicit unwrapped optional AVAudioPlayer! and get rid of the question marks. In this particular case you can even use a non-optional with the default initializer of AVAudioPlayer but this does not work with any type.
var player = AVAudioPlayer()
A2: You can assign both non-optional and optional values to an optional variable. The ? is only relevant when you are sending messages from the variable.
A3: player?.delegate = self is called Optional chaining. If player is nil then the rest of the line is being ignored.
Since the audio file must exist because the application bundle is not mutable at runtime you can omit all guards and catchs. The code must not crash. If it does you made a design mistake.
var player = AVAudioPlayer()
let audioURL = Bundle.main.url(forResource: "sample-audio", withExtension: "caf")!
player = try! AVAudioPlayer(contentsOf: audioURL)
player.prepareToPlay()

Initializer for conditional binding must have Optional type, not '[AVCaptureDevice]'

I am working on giving my app camera capabilities. I was following a tutorial (AppCoda: https://www.appcoda.com/avfoundation-swift-guide/). I have looked at other S.O. answers and have learned that the right side of the statement(below) must be an optional for the error to be solved. How would I make it an optional and fix the error?
Optional allows you to have a nil in a variable. Then the compiler forces you to check if there is a value before you can use a variable. However, if you don't have an optional, that's a good thing for you - you don't have to check for the value cause it is ensured that there always will be one.
Therefore in your case you don't want an optional there, rewrite your code as follows:
let cameras = session.devices.flatMap { $0 }
guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
Because obviously the call session.devices.flatMap { $0 } return an array [AVCaptureDevice], not an optional array [AVCaptureDevice]?. Therefore the compiler can guarantee that the cameras will always have an array value, and never a nil. Thus you can just guard against the empty array which would mean that there are no cameras (cause the list of cameras is empty).

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