Understanding Optionals and Types (again)

I have a real problem trying to get to grips with the whole Optionals/wrapping thing, and associated type issues.

Here's my code:

import Quartz
import Foundation

func listFilters() -> Void {
	let theFilters = QuartzFilterManager.filters(inDomains: nil)!
	for eachFilter in theFilters as! [QuartzFilter] {
		print(eachFilter.localizedName()!, ":", eachFilter.url().path)
	}
}
listFilters()

So, in the first line of the function, I have to explicitly force unwrap the result of the method that returns a list of QuartzFilters. (Why?)

The very next line, I have to force them to be of type QuartzFilters, (because why wouldn't the method return the actual type of thing that they are?)

And then on the third line, I still have to force unwrap one of the properties of something that I've already unwrapped (otherwise I get optionals), but not the other one.

Yes, I understand it's all about trying to prevent a null condition, but nearly everything is never null.

Even if I use if let on the first line, I'm still unwrapping stuff inside that if.

Replies

Declaration of filters (MacOS) is:

class func filters(inDomains domains: [Any]!) -> [Any]!

So, normally domain should not be nil (you risk a crash in filters). Result is optional, but cannot be nil, it is forced unwrapped

So, you can write

	let theFilters = QuartzFilterManager.filters(inDomains: nil)

you get the same result, because of the cast as!.

func listFilters() -> Void {
    let theFilters = QuartzFilterManager.filters(inDomains: nil)
    for eachFilter in theFilters as! [QuartzFilter] {
        print(eachFilter.localizedName()!, ":", eachFilter.url().path)
    }
}
print("ListFilters")
listFilters()

ListFilters

  • Blue Tone : /System/Library/Filters/Blue Tone.qfilter
  • Black & White : /System/Library/Filters/Black & White.qfilter
  • Reduce File Size : /System/Library/Filters/Reduce File Size.qfilter
  • Lightness Decrease : /System/Library/Filters/Lightness Decrease.qfilter
  • Sepia Tone : /System/Library/Filters/Sepia Tone.qfilter
  • Gray Tone : /System/Library/Filters/Gray Tone.qfilter
  • Lightness Increase : /System/Library/Filters/Lightness Increase.qfilter
  • Create Generic PDFX-3 Document : /System/Library/Filters/Create Generic PDFX-3 Document.qfilter

Consider also the small example below:

class SomeArray {
    var array = [Int] ()
}

func testIt(value: SomeArray!) -> Any! {
    return 10
}
print("unwrapped", testIt(value: nil)!)
print("direct", testIt(value: nil))                  // You get an optional
print("direct cast", testIt(value: nil) as! Int)     // You get an Int

You get:

  • unwrapped 10
  • direct Optional(10)
  • direct cast 10

Now, test this:

func testItToo(value: SomeArray!) -> Int! {
    return value.array.count
}
print("testItToo direct", testItToo(value: nil))

It crashes, because of value.array

"normally domain should not be nil (you risk a crash in filters)"

What should it be?

"you get the same result, because of the cast as!."
The same result as what?

"Result is optional, but cannot be nil, it is forced unwrapped"
If it cannot be nil, then why does it need to be optional? And why is it Any, and not 'actual' QuartzFilter?

And what about the two different types of property?

In your function, the result is a force unwrapped Any -- so why does it return an Optional?

I guess this is just a complaint about the way that Apple has chosen to do things, which doesn't really seem to have any benefit, but I suppose I'll have to live with it.

More exclamation marks!!!