Function Throws and Returns Optional.. Possible to Conditionally Unwrap in One Line

Function throws AND returns optional.. possible to conditionally unwrap in one line?

Update: As of Swift 5, try? applied to an optional expression does not add another level of optionality, so that a “simple” optional binding is sufficient. It succeeds if the function did not throw an error and did not return nil. val is then bound to the unwrapped result:

if let val = try? getSomething() {
// ...
}

(Previous answer for Swift ≤ 4:) If a function throws and returns an optional

func getSomething() throws -> Value? { ... }

then try? getSomething() returns a "double optional" of the
type Value?? and you have to unwrap twice:

if let optval = try? getSomething(), let val = optval {

}

Here the first binding let optval = ... succeeds if the function did
not throw, and the second binding let val = optval succeeds
if the return value is not nil.

This can be shortened with case let pattern matching to

if case let val?? = try? getSomething() {

}

where val?? is a shortcut for .some(.some(val)).

Unwrap optional in inline conditional statement

Yes. map can be applied to an optional and the closure is executed if the value is not nil, otherwise nil is returned. You can then use the nil coalescing operator to unwrap the result of map:

let a = b.map { f($0) } ?? false

As @Alexandar noted in the comments, since your function takes just one argument, you can write map just passing the function name:

let a = b.map(f) ?? false

Complete example

func f(_ a:Int) -> Bool {
return a > 17
}

var b:Int? = 18

let a1 = b.map(f) ?? false

print(a1) // true

b = nil

let a2 = b.map(f) ?? false

print(a2) // false

func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?

Description

Evaluates the given closure when this Optional instance is not nil, passing the unwrapped value as a parameter.
Use the map method with a closure that returns a nonoptional value.


Using nil-coalescing operator with try? for function that throws and returns optional

Essentially, this has to do with the grammar of the try operator. When used with a binary expression without brackets, try applies to the whole binary expression, so this:

try? machine.itemCode(code: 0) ?? "Unknown"

is the same as:

try? (machine.itemCode(code: 0) ?? "Unknown")

Since itemCode throws an error, the latter part of the expression ?? "Unknown is ignored, and the try? expression evaluates to nil.

On the other hand, the second expression is like this:

(try? machine.itemCode(code: 0)) ?? "Unknown"

The try? expression is evaluated first (to nil), then the ?? is applied, evaluating the whole expression to "Unknown".

Swift 3 better way to unwrap an optional

The proper way would be:

if let msg = myStr.removingPercentEncoding {
print("msg \(msg)")
}

Here, msg is only valid inside the if statement and only if myStr.removingPercentEncoding isn't nil.

If you wish to print something if myStr.removingPercentEncoding is nil, then you could and an else:

if let msg = myStr.removingPercentEncoding {
print("msg \(msg)")
} else {
print("msg has no value")
}

Read up on Optional Binding in the The Swift Programming Language book.

How can I use try with the coalescing operator?

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.

Map with Optional Unwrapping in Swift

compactMap() can do this for you in one step:

let paths:[String?] = ["test", nil, "Two"]

let nonOptionals = paths.compactMap{$0}

nonOptionals will now be a String array containing ["test", "Two"].

Previously flatMap() was the proper solution, but has been deprecated for this purpose in Swift 4.1

Force passing a single parameter, to a multi-optional-parameter function

You want something of Swift it can not do. In particular, as far as I am aware, Swift has no union types. If you want something like that, you may want to look at functional or functional-inspired languages.


Interlude: If both types are structs or classes (i.e. not protocols) you can use this pattern:

protocol DataOrError {}
extension Data: DataOrError {}
extension ErrorClass: DataOrError {}

Assuming there are no other implementers of the protocol, you now have something that approaches a union type. You can switch-case over types, too:

switch doe {
case let e as Error: print("Error \(e)")
case let d as Data: print("Data \(d)")
default: assert(false, "thou shalt not implement my dummy!")
}

Anyway, we can fake it using enumerations with associated values, which can (with limitations) be used as a poor-man's union type.

Define an enum like this:

enum DataOrError {
case Data(Data)
case Error(Error)
}

Now you can use DataOrError whereever you would like to, well, have (exactly) one of Data or Error, and DataOrError? for at most one.

Caller-site, you get something like this:

extension String: Error {} // low-effort errors, don't judge me!
func myFunction(completion: (DataOrError) -> ()) {
completion(.Data(Data(bytes: [1,2,3,4,5])))
completion(.Error("this won't work!"))
}

And callee-site:

var myCompletion = { (doe: DataOrError) in
switch doe {
case .Data(let d): print("Data \(d)")
case .Error(let e): print("Error \(e)")
}
}

myFunction(completion: myCompletion)
// > Data 5 bytes
// > Error this won't work!

Design note: You may be looking for a generalization in another direction, especially if you have many different types you want to OR with Error. In that case, an explicit wrapper may be a good solution, even though you sacrifice nice syntax.

struct TalkativeOptional<T> {
private(set) var rawValue: T?
private(set) var error: Error?

init(value: T) {
self.rawValue = value
}

init(error: Error) {
self.error = error
}
}

Note how exactly one of the two properties can be non-nil. There are two more combinations; here, you can control which you want by your choice of initializers.

Example callee-site:

func convert(_ number: String) -> TalkativeOptional<Int> {
if let int = Int(number) {
return TalkativeOptional(value: int)
} else {
return TalkativeOptional(error: "'\(number)' not a valid integer!")
}
}

Example caller-site:

var a = convert("dsa2e2")

if let val = a.rawValue {
print("a + 1 = \(val + 1)")
} else { // we know by design that error is set!
print("ERROR: \(a.error!)")
}

How to unwrap double optionals?

Given a double optional such as this doubly wrapped String:

let a: String?? = "hello"
print(a as Any) // "Optional(Optional("hello"))\n"

@Leo, showed that you could use optional binding twice:

if let temp = a, let value = temp {
print(value) // "hello\n"
}

or force unwrap twice:

print(value!!)  // don't do this - you're just asking for a crash

Here are 5 more methods you can use to safely unwrap a double optional:

Method 1:

You can also use pattern matching:

if case let value?? = a {
print(value) // "hello\n"
}

As @netigger noted in their answer, this can also be written as:

if case .some(.some(let value)) = a {
print(value) // "hello\n"
}

which while less concise might be a bit easier to read.


Method 2:

Alternatively, you can use the nil coalescing operator ?? twice:

print((a ?? "") ?? "")  // "hello\n"

Note: Unlike the other methods presented here, this will always produce a value. "" (empty String) is used if either of the optionals is nil.


Method 3:

Or you can use the nil coalescing operator ?? with optional binding:

if let value = a ?? nil {
print(value) // "hello\n"
}

How does this work?

With a doubly wrapped optional, the value held by the variable could be one of 3 things: Optional(Optional("some string")), Optional(nil) if the inner optional is nil, or nil if the outer optional is nil. So a ?? nil unwraps the outer optional. If the outer optional is nil, then ?? replaces it with the default value of nil. If a is Optional(nil), then ?? will unwrap the outer optional leaving nil. At this point you will have a String? that is nil if either the inner or outer optional is nil. If there is a String inside, you get Optional("some string").

Finally, the optional binding (if let) unwraps Optional("some string") to get "some string" or the optional binding fails if either of the optionals is nil and skips the block.


Method 4:

Also, you can use flatMap with optional binding:

if let value = a.flatMap({ $0 }) {
print(value) // "hello\n"
}

Method 5:

Conditionally cast the value to the type. Surprisingly, this will remove all levels of optionals:

let a: String?? = "hello"
let b: String??????? = "bye"

if let value = a as? String {
print(value) // "hello\n"
}

print(b as Any) // "Optional(Optional(Optional(Optional(Optional(Optional(Optional("bye")))))))\n"

if let value = b as? String {
print(value) // "bye\n"
}


Related Topics



Leave a reply



Submit