9

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].

user14492
  • 2,116
  • 3
  • 25
  • 44
  • 2
    How exactly is `app` defined? Could it be an *optional* NSRunningApplication? – Martin R Mar 30 '15 at 16:47
  • We're stumbling for answers because that line of code shouldn't produce that error. If app were optional, you'd get different error. And `localizedName` is not a "double optional" (lol). This line of code looks fine and should not generate that error. Swift, unfortunately, can generate misleading errors, and I wonder if the real problem rests in an earlier line of code (or perhaps you altered or otherwise cleaned up the line of code when preparing question). Also, when I get inexplicable errors, I clean the project, quit Xcode, empty the derived data folder, and restart Xcode. – Rob Mar 30 '15 at 17:10
  • @MartinR Added more info. Cheers for the help. – user14492 Mar 30 '15 at 17:59
  • See http://stackoverflow.com/a/28247921/242933 – ma11hew28 Jan 31 '16 at 14:58

2 Answers2

4

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!
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Xcode shows an error for the code above. You should probably replace `as` with `as!`: `let apps = NSWorkspace().runningApplications as! [NSRunningApplication]` – Chris Jun 04 '15 at 14:29
  • @Chris: You are right, that changed with Swift 1.2 (Xcode 6.3), thanks for letting me know! – Martin R Jun 04 '15 at 14:31
2

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.

ma11hew28
  • 121,420
  • 116
  • 450
  • 651