Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift double unwrapping of Optionals

I understand what optional are in Swift but I just encountered a ”Double Wrapped Optional’, where if I don’t use two '!' Xcode gives an complier error

Value of optional type 'String?' not unwrapped; did you mean to use '!' or ‘?'?

I have the following code, where app is of type NSRunningApplication.

let name: String = app.localizedName!

Why should I have to use two !? Isn’t one enough to unwrap the variable because it is of type var localizedName: String?.

Context: Xcode want me to use let name: String = app.localizedName!!, otherwise it gives the compiler error above. The app variable is defined as follow:

var apps = NSWorkspace().runningApplications.filter{$0.activationPolicy == NSApplicationActivationPolicy.Regular}
for app in apps{
    //code posted above
    …
}

So I know that app is not an optional and will always have a value, nor is it an optional application.

P.S. Is there a way to define type when using fast enumeration? Like for Foo(app) in apps where apps = [AnyObject].

like image 605
user14492 Avatar asked Mar 30 '15 16:03

user14492


People also ask

How many ways unwrap optional?

You can unwrap optionals in 4 different ways: With force unwrapping, using ! With optional binding, using if let. With implicitly unwrapped optionals, using !

How do I force unwrap in Swift?

Even though Swift isn't sure the conversion will work, you can see the code is safe so you can force unwrap the result by writing ! after Int(str) , like this: let num = Int(str)! Swift will immediately unwrap the optional and make num a regular Int rather than an Int? .

How do optionals work in Swift?

However there is another data type in Swift called Optional, whose default value is a null value ( nil ). You can use optional when you want a variable or constant contain no value in it. An optional type may contain a value or absent a value (a null value). Non technically, you can think optional as a shoe box.


2 Answers

The problem is that NSWorkspace().runningApplications returns an array of AnyObject which has to be cast to an array of NSRunningApplication:

let apps = NSWorkspace().runningApplications as! [NSRunningApplication]
let filteredApps = apps.filter {
        $0.activationPolicy == NSApplicationActivationPolicy.Regular
}
for app in apps {
    let name: String = app.localizedName!
}
like image 70
Martin R Avatar answered Sep 30 '22 10:09

Martin R


Here's why: app is of type AnyObject (id in Objective-C), and doing any lookup on AnyObject introduces a layer of optionality because of the possibility that the method doesn’t exist on the object. localizedName is itself Optional, so you end up with two levels of optional: the outer level is nil if the object doesn’t respond to localizedName, and the inner is nil if 'localizedName' is nil.

like image 23
ma11hew28 Avatar answered Sep 30 '22 10:09

ma11hew28