This is Part 1 of a series of articles advocating Swift to the often stubborn Objective-C developer.
Some time has passed since WWDC 2014 and the reveal of the Swift programming language. The hype has settled and most of us have gotten on with our lives continuing to write our code in Objective-C, toying with Swift in our free time or using it for small modules within our production code. There have indeed been some early adopters writing new apps from scratch in the language, but I imagine that has more to do with the ability to announce the fact rather than any benefit to the programmer or business. After quite some fanfare, nothing has really changed.
I believe this is a good thing. Objective-C has been validated as a production-ready programming language for decades. Swift... not so much. It was necessary for Apple to release Swift when they did in order to garner the interest of the community and incorporate feedback. Creating a programming language is a process that spans years, and we're only in the very beginning stages. Swift is a language for 2016. This is made painfully obvious by the available tools and the state of the compiler itself. We're priviledged with a very mature toolset as Objective-C developers working with Clang & LLDB. But it wasn't very long ago that we were stuck with GCC and its less-than-perfect compiler errors, and that project was around for twenty years before it was fixed. We must be patient.
Along with patience, we should also be incredibly excited! Swift represents the future for Objective-C developers and that future looks quite bright. Swift may be new, but it is built on a solid foundation of programming paradigms that are much older than most Objective-C developers (certainly me, anyway!). It is a mix and match of features from the best languages that alone are not approachable enough for the demographic that Apple is trying to reach (for example, Haskell).
In the meantime, I'd like to show some of the more stubborn Objective-C developers the benefits they will receive from adopting Swift, be it now or later. To begin, let's discuss the concept of Optional Values, a new feature in Swift that is not available in Objective-C.
In Objective-C, objects are represented by pointers to their respective instances in memory, an implementation detail rooted in its history of being built on top of C. You will never see a statically initialized Objective-C object. That is, an object whose type is not a pointer. Indeed, even the common
id type is essentially a typedef for
Keeping that in mind it becomes clear that this can cause all sorts of problems in practice. The compiler and the language itself provide very little enforcement of where an object variable is pointing to because in the end, it's just another C pointer and C isn't designed to be restrictive of the operations you can perform on a pointer. To many, this is considered a feature and operations such as pointer arithmitic are first-class citizens in the C world. Indexing your C arrays using subscripting,
myCString, is more or less shorthand for the pointer arithmitic equivalent:
Here's another example:
By supporting these sorts of operations, Objective-C sacrifices safety for flexibility. And in many cases, the flexibility is unnecessary. We don't need access to pointers more often than we do. Worse even, we must now keep track of the validity of our objects 100% of the time, even when we as programmers intend on an object to ever be in one state, because we have no way for the compiler to enforce this for us.
For example, let's take a look at one of the constructors of the
From this declaration alone, we cannot be sure what would happen if we pass
nil to either of the arguments of this function. In Objective-C, it's fairly common for this to happen unintentionally because their is no way to distinguish between an object pointer than can be
nil and one that cannot.
And, as many of you probably are aware, this constructor in particular will crash should you pass it
nil to the
string parameter. That's not what we want at all!
Swift provides a solution to these sorts of issues in the form of Optional values. Optionals are a state machine that either represents a value, or nothing. This is fundamentally different from the concept of
nil in Objective-C where it is just another location in memory, albeit an often invalid location. In Swift, optionals provide an abstraction that does not conflate the concept of values existing with low level details like memory addresses. They allow you to write code that mirrors your logic; "does this value exist?" not "is this pointer pointing to a valid location in memory and therefore can I consider this object to exist?". It's simpler.
This also differs from Objective-C because it does not allow
nil messaging. In Objective-C, the runtime will happily accept
nil as the receiver of a message, do nothing, and return 0. There are valid scenarios in which
nil messaging is a useful feature but in general it causes more trouble than it's worth. An entire class of bugs can be attributed to Objective-C allowing
nil messaging. You'll find many Objective-C developers championing
nil messaging and claiming it's exclusion from Swift a step backwards. This is more of a side effect of years spent tailoring the solutions to their problems around the quirks of Objective-C than any inherit correctness in supporting
Swift's Optional values are somewhat similar to the adhoc Objective-C class, NSNull, whose purpose is denote the absense of a value, just like a Optional. However, not being a language feature and instead part of the Foundation framework, it is incredibly cumbersome and frustrating to use. NSNull is just another Objective-C object and by using it we're forced to now reason about two entirely different concepts of a null value, an unnecessary complication of our code. Victims of the NSJSONSerialization class can surely attest to this. In Swift, we promote the purpose of NSNull to a first-class language citizen.
Let's take a look at the syntax.
Optional value types are denoted by a Type, followed immediately by a question mark:
The combination of the two parts as a whole are a new type and can be used in a variable declaration just like any non-optional type.
in the case above,
anIntOrNothing is an Optional value, that wraps an Int whose value is
anIntOrNothing does not have a value.
We can test to see if an Optional value has a value by comparing it to
Or we can use the Conditional Let syntax to test if it has a value and, if it does, bind that value to a non-optional variable.
This can be considered syntatic sugar for the following:
The bang operator,
! is used to "unwrap" an Optional value. That is, if the Optional represents a value, then it will return the value. If the Optional value represents the absense of a value, doing so will throw an exception. It's important to always make sure an optional value is not
nil before attempting to unwrap it. It is generally preferable to favor the conditional let syntax described above.
Optionals can also be chained using the ? operator. For example, the UIViewController property presentedViewController is of type UIViewController?. We can access its properties using optional chaining; the properties are automatically wrapped in an optional themselves in the process.
If we were to access the view property of the presentedViewController without optional chaining, first we need to get a valid reference to the view controller itself, if it exists. Then if it does, we can access its view.
With optional chaining, we can combine these into one step. However, notice the view types are different. In this case it returns a UIView wrapped in an optional.
This works for property setters as well. If both
presentedViewController and its
view property are valid references, then the view's background color would be set to white as expected. If either optional were
nil, it would immediately stop at the first
nil reference and nothing would happen. This can be seen as a bit of a safer, typed approach to
Swift also supports the concept of Implicitly Unwrapped Optionals. They're denoted with the following type annotation:
And in a variable declaration:
You should use an implicitly unwrapped optional when you cannot initialize the value of an optional when whoever owns the variable is created, but during it's use it will never, ever be
nil. The latter is very important because attempting to use the variable without ever assigning it a value will throw an exception.
At first glance, the implicity unwrapped optional may seem to reject the entire purpose of an optional value, and in theory this is true.
In practice, as Mac OS X or iOS developers, the use of implicity unwrapped optionals was included as a necessity. When loading a nib file, the property references aren't set until after the view is loaded, which is no way coupled to when the instance of the owner class is initialized. If the @IBOutlet properties were not optionals, there would be no way to initialize them when the instance is created because the view has not been loaded yet. And if they were regular optionals, we'd have to unwrap them manually everytime we reference them, even though we intended on the connections to always be valid. Implicity unwrapped optionals are a convenience for cases such as these and in your day to day programming in Swift, this is the only time you should use them.
Here's a concrete example:
This also demonstrates an additional benefit of the implicity unwrapped optional: if your forget to make a connection in Interface Builder your app will crash instantly when trying to access one of the unititialized outlets. Despite the nasty word "crash" this is really fantastic in comparison to Objective-C, where many of you have undoubtedly pulled your hair out trying to figure out why a label's
text property silently refuses to change only to realize your forgot to connect the label to its respective property in IB.
nil messaging is a nuisance.
In most other scenarios you should prefer that your instance variables be declared with
private var and be passed as a parameter to an
init constructor. This will come up often when declaring an optional delegate property in Swift.
Then you can use it like so:
This is preferable to declaring the instance variable as a
public var and assigning it after the fact, as it guarantees the state and validity of the delegate property and ensures it cannot be changed without the controller knowing.
Last, one of the biggest benefits of supporting Optionals is much more straightforward and simple. So simple it can be easily overlooked.
By explicity typing your Optional values, you are implying that every single other non-optional type must always have a value and therefore be valid. Duh, you're thinking. But this is serious improvement in comparison to Objective-C and C. Because this implication is built into the language itself, the compiler is able to tell us when we're wrong and enforce this religiously.
Let's revisit the
NSAttributedString example. In Swift, the declaration looks like this:
Using this declaration of the
NSAttributedString constructor, we now must pass in a valid string. Passing nil or a String? (optional String type) will result in a compiler error and fail well before the runtime exception we would have gotten in Objective-C.
This is incredibly powerful. A large part of being a programmer is being able to maintain and reason about the state of your code at any given time. In Objective-C that includes having to reason about the validity of every single object in your code. In Swift, that facet is handled by the compiler, which has a far better memory than you do.
Swift's optional values absorb a significant amount of work that is required of every Objective-C programmer. And while the langauge itself isn't quite ready for production use, iOS and Mac developers should be excited about a future that includes such powerful features built into their primary language.
Next time we'll discuss Generics and Typed Collections in Swift. Check back soon.
If you enjoyed this post, follow me on Twitter.