LoginSignup
21
20

More than 5 years have passed since last update.

Swift's optional is not Objective-c nil

Last updated at Posted at 2014-07-25

皆さんこんにちは!
My Japanese writing skill is limited so I will keep posting in English ^_^.

There are a few articles on Qiita that cover the basic usage of swift's optional type,in case you haven't checked them out:
[Swift] Swiftのoptional valueの便利さ /「?」と「!」でより堅牢なコードへ
Swiftの「?」とか「!」などのOptional Valueの挙動を調べてみた
SwiftのOptionalとType Safety

In this post I want to go a bit further, to explain what optionals really are, and how things work behind the curtain. Since confusing swift's optional type with objective-c's nil type is what trips most people over, I will start here. In addition, I will show you how to use optional chaining and optional binding.

Definition: an Optional is a constant or variable in swift that can or cannot be nil.

optionals.swift
var strValue: String?  //is equal to the following:
var strValue: Optional<String> 

the ? is a syntactic sugar for declaring an optional value type, which means when you write something like String? you are not declaring a String which might be optional, but an Optional which could hold a Sting, or nil.

“Swift’s nil is not the same as nil in Objective-C. In Objective-C, nil is a pointer to a non-existent object. In Swift, nil is not a pointer—it is the absence of a value of a certain type. Optionals of any type can be set to nil, not just object types.” - The Swift Programming Language.

First of all, all value types in swift must be initialized before usage, with a default value. So there's no such thing as "a string that might be empty". Second, optional values cannot be used directly, it must be unwrapped(that's where the !, optional chaining and optional binding comes in handy, I will explain later).

I've seen some posts uses Optionals this way:

optionals.swift
var optional:String?
var mustBeThere = optional! 

Hmmm...not exactly a good idea when you cannot be sure whether the optional holds a value. Be really careful with the ‘!’ operator (which unwraps the optional and exposes the underlying value). Because if the optional is nil, this produces a runtime error.

If you are still confused, let's see what optionals really are, here is its implementation you can find in Xcode6:

optionals.swift
enum Optional<T> : LogicValue, Reflectable {
    case None
    case Some(T)
    init()
    init(_ some: T)

    /// Allow use in a Boolean context.
    func getLogicValue() -> Bool

    /// Haskell's fmap, which was mis-named
    func map<U>(f: (T) -> U) -> U?
    func getMirror() -> Mirror
}

Aha! it's an enum! To be more specific, it's an enum with two options: .Some which chould be the value type it holds, and .None(which is nil).

So let's read this again:

optionals.swift
let optionalTen: Int? = 10

An optional declaration doesn’t say “this is an Int, which is optional”. It says, “this is an Optional, which may or may not hold an Int”.
In fact, opitonals, are values types, just like Int, String, Array...you get the idea. In Objective-C, nil values are acceptable, therefore when you bridge Objective-C code with Swift code, all Objective-C types will be returned as Optional.

Unwrapping rule: you can’t use the value of a variable with an optional type directly.

optionals.swift
class Person {
  var name: String
  init(_ name: String) {
    self.name = name
  }
}
var p: Person? = Person("John")  // p is optional/nil-able
var greeting = "Hello" + p.name // Compile-time error
var greeting = "Hello" + p!.name // “Hello John”

The ‘!’ operator unwraps the optional and exposes the underlying value (in this case the string "John"). If p is nil, this produces a runtime error.

remember how we check whether a value is nil in ObjC?

file.m
if (!testVar) {
    NSLog(@"testVar is nil");
} else { 
    NSLog(@"testVar is equal to %@", testVar);
}

in Swift: use optional binding — if let

optionals.swift
var testVar: String? = myRandomFunction() /*May return nil*/
if let constVar = testVar { 
 println("testVar is equal to \(constVar)")
} else { 
 println("testVar is equal to nil")
}

It’s safer and preferable to use “optional binding” to unwrap an optional.

try one from Objective-C API:

optionals.swift
let formatter = NSDateFormatter()
let now = formatter.dateFromString("not_valid")
let soon = now.dateByAddingTimeInterval(5.0) /*Runtime error.*/
/*In the equivalent Objective-C code, ‘soon’ would be nil, rather than producing a runtime error. */

/*nil check use if statement:*/
if let soon = now.dateByAddingTimeInterval(5.0) {
  // soon is a concrete NSDate
} else {
  // soon is nil
}

Optional Chaining — use ?. to chain multiple optionals together and conditionally check if each value in the chain exists.

optionals.swift
var testVar = varOne?.varTwo?.varThree
if let varOne = optionalFuncOne() { 
    if let varTwo = optionalFuncTwo() { 
        if let varThree = optionalFuncThree() { 
            testVar = varThree
        }
    }
}

Syntactic sugar again, under the hood you can understand it as nested optional bindings.


Now, with your newly developed skill, let's take a look at this function from WWDC session 404:

file.swift
func stateFromPlist(list: Dictionary<String, AnyObject>) -> State? {
  switch (list["name"], list["population"], list["abbr"]) {
      case (.Some(let listName as NSString),
      .Some(let pop as NSNumber),
      .Some(let abbr as NSString))
      where abbr.length == 2:
          return State(name: listName, population: pop, abbr: abbr)
      default:
          return nil
  }
}

Get it? Can you see where the .Some came from?

I hope you enjoyed Swift so far, optionals are really cool safety features.

21
20
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
20