Can we use "guard" instead of let <var> = try? <action> in swift? - swift

What is the difference between:
guard let json_data = Data(contentsOf: path) else {return nil}
and
let json_data = try? Data(contentsOf: path)
I dont want to use optional while loading the data into the variable. I want other ways to try it.
Thanks in advance.

The options are:
Your first example, unwrapping it with guard, is missing a try?:
func foo() -> Bar? {
guard let jsonData = try? Data(contentsOf: path) else { return nil }
// if you get here, `jsonData` is not `Optional`
…
}
This will safely unwrap your optional and let you do whatever you want if the unwrapping failed. (In your example, you are returning nil.)
Your second example, yields an optional, which you presumably need to unwrap with an if statement.
func foo() -> Bar? {
let jsonData = try? Data(contentsOf: path)
// jsonData is `Optional` in this example
if let jsonData {
…
} else {
return nil
}
}
We would generally favor the first option over this, where the “early exit” of the guard makes it a little easier to read the code, but there are cases where you might use this pattern.
An option that hasn’t been considered is to actually throw the error (using try instead of try?):
func foo() throws -> Bar {
let jsonData = try Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this only passes the buck of handling the error to the caller (i.e., a do-catch block). But it does have a few virtues over the prior two examples, namely that (a) the useful information of the error object is not just discarded, thereby making it easier to diagnose problems during the development process; and (b) you don’t have to return an optional.
Yet another option (to be used only if you know that this will always succeed, e.g., you are reading a well-known file from your bundle that you know must always succeed) is try!, a “force-try”:
func foo() -> Bar {
let jsonData = try! Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this will crash if the Data(contentsOf:) can ever fail, so only use this in scenarios where you know that this is impossible.
Personally, I would generally favor option 3 (where I capture what went wrong) in cases where the Data(contentsOf:) might ever plausibly fail at runtime, and I might consider option 4 (where it crashes with a meaningful error message) when I know it is impossible for it to ever fail at runtime. That having been said, more than once I found myself using option 4 and I later regretted not using option 3, simply because there was some weird edge-case that I neglected to consider.
In short, nowadays I tend to defensively catch errors, log the full error in the console and show a nice localized message in the UI (i.e., option 3). I almost never use try?, because if something can fail, it’s rarely a good idea to discard the useful diagnostic information.

It will be better to be optional once you are loading the data to avoid application crash in and problems in case there was no data there. This is consider as a safe feature.

Related

How to wrap function try catch so force unwrap nil doesn't crash the app?

I am getting these errors often when a function I call uses something like:
optionalVar!
"Unexpectedly found nil while unwrapping an Optional"
I am unsure how to deal with functions that fail sometimes if I don't always have control over the inner code?
Is there a way to protect around such crashes? In most languages I could put try catches around most things.
When I do something like:
if let result = Blah.someExternalFunction(html: "some bad html") { }
This can still fail inside "someExternalFunction", even after trying to add try? in front of it.
Thanks
What you're looking for is if let
If you have an optional value, you can simply do something like this to "try" it:
if let val = optionalVar{
//val is already unwrapped
}
else{//it was nil}
Another option is to use a guard statement. It works similarly.
guard let val = optionalVar
else{
//the value is nil, so you need to exit the current function
return
}
//'val' is now unwrapped for any code below the guard-else statement

Catching exceptions when unarchiving using NSKeyedUnarchiver

We've got a Swift class which inherits from NSObject and implements NSCoding. We need to change the name of the class in code and in the archives on disk. Fortunately, we don't need to retain the data. Wiping the cached files and returning default data will be fine. The trouble is detecting when it fails and handling it appropriately.
Ok, to start with the data is written out like this (ignore liberties with force unwrapping etc. it's just to keep the question concise):
let someObject: [Int: MyClass] = ...
let data = NSKeyedArchiver.archivedData(withRootObject: someObject)
try! data.write(to: someUrl, options: .atomic)
This works fine. Our existing method of decoding is this (again, our code is safer in practice):
let someObject = NSKeyedUnarchiver.unarchiveObject(withFile: someUrl.path)! as! [Int: MyClass]
Now, when we rename our class, we are going to have to deal with the cases where it fails to decode. With NSKeyedUnarchiver, you can set a delegate which will have a method called if decoding fails. Before changing it, I wanted to re-write our existing decoder to make sure it can decode as is before I make any changes. Unfortunately, it can't. Here is what we've got:
let fileData = fileManager.contents(atPath: fileUrl.path)!
let unarchiver = NSKeyedUnarchiver(forReadingWith: fileData)
guard let myObject = try! unarchiver.decodeTopLevelObject() as? [Int: MyClass] else {
return [Int: MyClass]()
}
Now, I didn't expect any issues with this. However, the call to decodetopLevelObject() fails every time, simply returning nil (it's definitely not the cast that's the problem). I have no idea why. The documentation for this is effectively non-existent. There is a similar call which is decodeObject(), but that also fails for no obvious reason. I've tried setting the delegate and implementing the method, but it never gets called.
Where are we going wrong?
Try using decodeObject(forKey:) with NSKeyedArchiveRootObjectKey or similar API.

Use the try? operator to make the code more concise

I have this code in Swift 2 to move a file to a new destination, overwriting if necessary:
let origin = "...", destination = "..."
do {
try NSFileManager.defaultManager().removeItemAtPath(destination) // remove existing file
} catch {}
do {
try NSFileManager.defaultManager().moveItemAtPath(origin, toPath: destination)
} catch {}
To make the code more concise, and since I don't care about the error thrown, I thought of using the try? operator as follows:
let origin = "...", destination = "..."
try? NSFileManager.defaultManager().removeItemAtPath(destination)
try? NSFileManager.defaultManager().moveItemAtPath(origin, toPath: destination)
This creates a compiler warning that the result of the operation is being unused, so I have to add an unused let and it looks awful:
...
let _ = try? NSFileManager.defaultManager().moveItemAtPath(origin, toPath: destination)
Is it bad to let the warnings there in the sake of conciseness?
In Swift 3 it is legal to ignore the result without the extra assignment. This compiles just fine, with no warning, in the Xcode 8 GM:
try? FileManager.default.removeItem(atPath: destination)
(Earlier, by the way, I asked about this on bugs.swift.org, and was told directly that this _ = try?
syntax is regarded as correct and is a small price to pay for acknowledging to the compiler — and yourself — that you are deliberately ignoring the returned value. So what you are doing is just fine while you remain in the Swift 2 world!)
Note that you have these warnings because you are calling some method and you do not handle returning value.
If you do not need returning value then you should use let _ = syntax to explicitly say to everyone else who reads this code that returning value is ignored intentionally.
Otherwise if you should handle returning value but you did not do that yet because you are too lazy or by some other reason then it's considered to be a bug in your software and you definitely should not suppress it.

Using guard keyword

I have encountered numerous situations where a coder have used the guard keyword. And then later, in a seemingly almost identical situation the same coder in the same code does not use the guard keyword. I am aware that this may be a stupid question, so please don't bash it. When should I use the guard keyword and where shouldn't I?
Here is an example (there are many more). This is part of a script that is requesting data form an API.
//Here I am using guard
guard let json = json else {
//Now I am not using guard
if let error = error {
completion(.Failure(error))
} else {
//Error handling
}
return
}
Why not use the:
if let var1 = var1 {
//Keep on going
} else {
//Don't crash
}
syntax all the time instead of the guard syntax? At first glance it even seems to have more functionality, but I am certain that does not have to be the case.
One great benefit of the guard statement is that you know that if the condition is not satisfied then the execution flow gets stopped.
This is important for several reasons
Unwrapping
You can define unwrapped values which don't need a new scope { ... } to be available
func next(num:Int?) -> Int? {
guard let num = num else { return nil }
return num + 1
}
Readability
When you read the code you know that if the guard condition is not satisfied then the following lines won't be executed.
Semantics
You know a guard statement is there to check conditions required for the following block of code.
But I can replace every guard with an if
Sure. We could also replace every while and for with a goto in some languages. And we could always replace recursion with iteration (and viceversa).
But this doesn't necessarily means it is always a good idea.
Despite we can implement some behaviours with more then one programming "tool", we should still use the one that better fits that specific scenario.

Is it safe to use try! NSDataDetector()

Return Value Returns the newly initialized data detector. If an error
was encountered returns nil, and error contains the error.
That is all the apple docs say about NSDataDetector and it's init.
Is it only wrapped in a do try catch because you might feed it a set of NSTextCheckingType that will fail the init or does it have some dependencies that might make it fail?
Or to put it another way:
If an init works once, will it always work and can I then use try!
This works:
guard let detector = try? NSDataDetector(types: NSTextCheckingType.Date.rawValue) else {
return
}
So will this always work:
let detector = try! NSDataDetector(types: NSTextCheckingType.Date.rawValue)
Not having to deal with optionals would be great in some cases.
Of course the class might one day change and then it might fail. So this question is just about NSDataDetector as it exists today.
Update :
NSDataDetector and all NSTextCheckingType's are available from iOS 4. This makes me think that once an init works, it will always work.
next thing I will check:
Are all init's, or all functions, that accept an OptionSetType fallible?