Reputation: 917
I'm trying to rewrite part of ObjC code with Swift, a lot of cocoa object properties return optional value in Swift. Is there better pattern instead of this deep nesting:
if let panel = self.window {
if let screens = NSScreen.screens() {
if let screenRect = screens[0].frame {
let statusRect = CGRectZero
...
Upvotes: 3
Views: 637
Reputation: 229
Please read about optional chaining here: Linking Multiple Levels of Chaining
And you can play with this playground to look how does it work:
import UIKit
class AAA {
var string: String? = nil
}
class AA {
var aaa: [AAA?]? = nil
}
class A {
var aa: AA? = nil
}
var a: A?
a = A()
/// Comment/Uncomment next lines
a?.aa = AA()
//a?.aa?.aaa = [AAA()]
a?.aa?.aaa = []
a?.aa?.aaa?.first??.string = "My String Value"
if let myString = a?.aa?.aaa?.first??.string {
print("myString: \(myString)")
} else {
print("myString does not acceptable")
}
And your code may look like:
if let panel = self.window, let statusRect = NSScreen.screens()?.first??.frame ?? CGRect.zero {
/// your code to work with panel and statusRect
}
Upvotes: 0
Reputation: 597
As of Swift 1.2 you can do unwrapping on one line, as described in this answer:
unwrapping multiple optionals in if statement
Upvotes: 0
Reputation: 70142
I wrote a blog post a while back which explored a few different ways to unwrap multiple optionals.
You can create a function that unwraps multiple optionals in one go:
func if_let<T, U, V>(a: Optional<T>, b: Optional<U>,
c: Optional<V>, fn: (T, U, V) -> ()) {
if let a = a {
if let b = b {
if let c = c {
fn(a, b, c)
}
}
}
}
used as follows:
if_let(a, b, c) {
a, b, c in
println("\(a) - \(b) - \(c)")
}
Someone also proposed a neat solution using tuples, that can be found in this gist.
Upvotes: 1
Reputation: 16558
While Leonardo's answer is a good one, it can lead to exceptions unless you know the objects are non-optional, a better pattern is this, which should be regarded as pseudocode:
if let frame = NSScreen.screens()?.first?.frame {
// Do something with frame.
}
In other words, use optional chaining (?.
), but only use let
with the very last part of the chain.
You could also create an operator like this if you want to chain optionals that are of a different type, in which only the value of the last one will be returned:
infix operator ??? { associativity left }
func ??? <T1, T2>(t1: T1?, t2: @autoclosure () -> T2?) -> T2? {
return t1 == nil ? nil : t2()
}
if let frame = self.window ??? NSScreen.screens()?.first?.frame {
// Do something with frame
}
This is, of course, inspired by Haskell's bind. But as fun as this is, I don't recommend it. I think it's clear as mud. (By the way, the difference between ???
and ??
is that the former does not require the lhs and rhs to be of the same type (or supertype), while the latter does. ???
always returns its rhs or nil
.)
Upvotes: 2
Reputation: 236360
You can force unwrap it like this:
NSScreen.screens()!.first!.frame!
NSScreen.screens()!.first!.frame!.width
NSScreen.screens()!.first!.frame!.height
// safely unwraping it as required an mentioned by Gregory
if let frame = NSScreen.screens()?.first?.frame {
println(frame)
}
if let width = NSScreen.screens()?.first?.frame?.width {
println(width)
}
if let height = NSScreen.screens()?.first?.frame?.height {
println(height)
}
Upvotes: 1