How can I use try with the coalescing operator? - swift

I'm trying to assign a value to x from a function f that takes one parameter (a string) and throws.
The current scope throws so I believe a do...catch isn't required.
I'm trying to use try with the coalescing operator ?? but I'm getting this error: 'try' cannot appear to the right of a non-assignment operator.
guard let x = try f("a") ??
try f("b") ??
try f("c") else {
print("Couldn't get a valid value for x")
return
}
If I change try to try?:
guard let x = try? f("a") ??
try? f("b") ??
try? f("c") else {
print("Couldn't get a valid value for x")
return
}
I get the warning Left side of nil coalescing operator '??' has non-optional type 'String??', so the right side is never used and the error: 'try?' cannot appear to the right of a non-assignment operator.
If I put each try? in brackets:
guard let x = (try? f("a")) ??
(try? f("b")) ??
(try? f("c")) else {
print("Couldn't get a valid value for x")
return
}
It compiles but x is an optional and I would like it to be unwrapped.
if I remove the questions marks:
guard let x = (try f("a")) ??
(try f("b")) ??
(try f("c")) else {
print("Couldn't get a valid value for x")
return
}
I get the error Operator can throw but expression is not marked with 'try'.
I'm using Swift 4.2 (latest in Xcode at time of writing).
What is the correct way to do this to get an unwrapped value in x?
Update:* f()'s return type is String?. I think the fact that it's an optional string is important.

A single try can cover the entire expression, so you can just say:
guard let x = try f("a") ?? f("b") ?? f("c") else {
print("Couldn't get a valid value for x")
return
}
Same goes for try?:
guard let x = try? f("a") ?? f("b") ?? f("c") else {
print("Couldn't get a valid value for x")
return
}
Although note that in Swift 4.2 x will be String? due to the fact that you're applying try? to an already optional value, giving you a doubly-wrapped optional which guard let will only unwrap one layer of.
To remedy this, you could coalesce to nil:
guard let x = (try? f("a") ?? f("b") ?? f("c")) ?? nil else {
print("Couldn't get a valid value for x")
return
}
But in Swift 5 this is unnecessary due to SE-0230, where try? f("a") ?? f("b") ?? f("c") will be flattened into a single optional value automatically by the compiler.

Related

How to unwrap optional value in swift?

code correct
print(error!.localizedDescription)
}else{
return
To replace a nil value we need to unwrap optional value this done according to type of value.
i.e. if value is string then we need to add ?? "" or if value is double we need to add ?? 0.0
As in case of the above scenario (EmailTextField.text ?? "") this is the format to replace unwrap optional value .
Auth.auth().createUser(withEmail: (txtEmail.text ?? ""), password: (txtPass.text ?? "")) { (result, error) in
if let _eror = error {
//something bad happning
print(_eror.localizedDescription )
}else{
//user registered successfully
print(result)
}
}
This error clearly indicates that you have forced unwrap the optional somewhere in your code.
Remove force unwrapping (!) by optional binding in your code.
Ways to optional binding...
if let
guard let
nil coalescing i.e ??
Look for exclaimation mark in your code, where you have force
unwrapped and replace it with one of the above way.

please give me some solution to this error Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 3 years ago.
func doGetLocalDataUser() -> logInResponse {
var localData : logInResponse? = nil
if let userData = UserDefaults.standard
.data(forKey: ConstantStrings.KEY_USER_LOGIN_DATA),let user = try? JSONDecoder()
.decode(logInResponse.self ,from: userData){
localData = user
}
return localData!
}
I would change the method to this:
func doGetLocalDataUser() -> logInResponse? {
guard let userData = UserDefaults.standard.data(forKey: ConstantStrings.KEY_USER_LOGIN_DATA), let user = try? JSONDecoder().decode(logInResponse.self ,from: userData) else {
return nil
}
return user
}
Keep in mind JSON decoding can fail (maybe has wrong format), therefore user will be nil, and this method can return nil
localData is nil, so you are force unwrapping localData (as a logInResponse) illegally.
If your optional chaining finds nil at any point (for example your userData doesn't exist in UserDefaults / has the wrong type) then it won't execute.
You are declaring this doGetLocalDataUser() function to return a non optional logInResponse type and force unwrapping a nil value to try and retrieve one. Best practice is to avoid the force unwrap "bang" operator "!" because it can lead to fatal errors like this.
Simple solution is to change your method to return an optional logInResponse? type, and eliminate the bang operator:
func doGetLocalDataUser() -> logInResponse? {
if let userData = UserDefaults.standard.data(forKey: ConstantStrings.KEY_USER_LOGIN_DATA), let user = try? JSONDecoder().decode(logInResponse.self ,from: userData){
return user
} else {
return nil
}
}

Trying to do deal with errors and optionals the right way

I am attempting to use SwiftSoup to scrape some HTML. This example, based on the SwiftSoup github documentation, works fine…
func scrape() throws {
do {
let htmlFromSomeSource = "<html><body><p class="nerp">HerpDerp</p><p class="narf">HoopDoop</p>"
let doc = try! SwiftSoup.parse(htmlFromSomeSource)
let tag = try! doc.select("p").first()!
let tagClass = try! tag.attr("class")
} catch {
print("oh dang")
throw Abort(.notFound)
}
print(tagClass)
}
… Up until I mess with the selector or attribute targets, at which point everything crashes thanks to the implicitly unwrapped optionals (which I assume was just quick-and-dirty code to get smarter people started). That do/catch doesn't seem to help at all.
So what's the Right way? This compiles...
print("is there a doc?")
guard let doc = try? SwiftSoup.parse(response.body.description) else {
print("no doc")
throw Abort(.notFound)
}
print("should halt because there's no img")
guard let tag = try? doc.select("img").first()! else {
print("no paragraph tag")
throw Abort(.notFound)
}
print("should halt because there's no src")
guard let tagClass = try? tag.attr("src") else {
print("no src")
throw Abort(.notFound)
}
... but again if I mess with the selector or attribute it crashes out, "Unexpectedly found nil while unwrapping an Optional value" (after "is there a doc?"). I thought guard would halt the process when it encountered a nil? (If I convert "try?" to "try" the compiler complains that "initializer for conditional binding must have Optional type"…)
If you declare the function as throws you don't need a do - catch block inside the function. Just remove the block and the exclamation marks after try to pass through the errors to the caller function.
func scrape() throws { // add a return type
let htmlFromSomeSource = "<html><body><p class="nerp">HerpDerp</p><p class="narf">HoopDoop</p>"
let doc = try SwiftSoup.parse(htmlFromSomeSource)
guard let tag = try doc.select("p").first() else { throw Abort(.notFound) }
let tagClass = try tag.attr("class")
// return something
}

Swift :Initializer for conditional binding must have Optional type, not 'String.SubSequence' (aka 'Substring')

I'm working on a little project and I came across this error which I can't find on the Internet.
This is my code:
let bufferRecieved = String(bytesNoCopy: buffer, length: length, encoding: .utf8, freeWhenDone: true)
guard let delimiterFirstIndex = bufferRecieved?.index(of: ":")!,
let name = bufferRecieved![..<delimiterFirstIndex],
let message = bufferRecieved![delimiterFirstIndex...] else {
return nil
}
I get:
Initializer for conditional binding must have Optional type, not 'String.SubSequence' (aka 'Substring') for the two instructions
let name = bufferRecieved![..<delimiterFirstIndex],
let message = bufferRecieved![delimiterFirstIndex...]
What does this error mean and how can I fix it?
name and message aren't Optionals, so there's no need for them to be part of the conditional binding. Just put them after the guard statement:
guard let bufferRecieved = String(bytesNoCopy: buffer, length: length, encoding: .utf8, freeWhenDone: true),
let delimiterFirstIndex = bufferRecieved.index(of: ":") else {
return nil
}
let name = bufferRecieved[..<delimiterFirstIndex]
let message = bufferRecieved[delimiterFirstIndex...]
use(name, message)

Should we do nil check for non-optional variables?

I have a function (abc) as follows and I should throw an error when the arguments passed are empty or nil, should I check for nil too or only empty is enough?
public func abc(forURL serviceUrl:String,serviceID:String, error:inout Error? )throws ->[AnyHashable : Any]{
guard serviceUrl != nil, !serviceUrl.isEmpty else {
let argError:Error = MapError.emptyArgumentUrl.error()
error = argError
throw argError
}
guard !serviceID.isEmpty else {
let argError:Error = MapError.emptyArgumentServiceId.error()
error = argError
throw argError
}
serviceID is not an optional.
That means it can't be nil.
So no, there is no need for that check.