An Overview of Apple’s Swift Programming Language

The jaws of iOS developers everywhere dropped with the announcement at WWDC yesterday that Apple was introducing a new programming language to eventually replace Objective-C, called Swift. The surprise wasn’t that they did it (everyone knew it had to be done), but that it happened so soon. Apple has so much institutional knowledge of Objective-C that to replace it is truly a massive sea change. The problem with Objective-C, being a 25-year old mashup of C and Smalltalk, is that is was never quite fully-baked. And it was not suited for high-performance code, causing developers to revert to C or C++ in many cases. Here are my initial thoughts and observations on how Swift compares to Objective-C and other modern languages. There’s a LOT to cover, so I just picked out the interesting bits:

It has nothing to do with the Swift Parallel Scripting Language

No more .h and .m files

Header files were baggage from C. Swift keeps all code is contained in .swift files. Right away, this means a 50% reduction in file count for your projects!

No more brackets

Objective-C has a tendency to scare away new developers due to its unique bracket-based syntax. It also doesn’t nest nicely. Swift uses parentheses and named parameters (much like C#).

No more pluses and minuses

In Objective-C, instance methods are prefixed with a minus, while class methods are prefixed with a plus. In Swift, methods are prefixed with a func keyword, and class methods (“type methods”) are additionally prefixed with the class keyword.

No more semicolons

In Swift, you can still use semicolons to terminate statements, but they are not required. (They are only required if you have multiple statements on a single line).

No more asterisks

C pointer syntax is an area that often scares away newer developers. Why do you sometimes use them, and other times not (i.e. NSInteger vs NSNumber*)? Swift avoids giving you direct access to pointers. However, there are still various pointer types available under the hood when you need direct access to memory, such as CMutablePointer<Type>.

Type inference

Type inference is an easy way to specify a variable’s type at compile-time without being explicit. This is done using the var keyword, just like C#! However, Swift does offer flexible “catch-all” data types: AnyObject can represent an instance of any class type, and Any can represent an instance of any type at all (just not function types).

Type safety

Objective-C plays fast and loose with data types. For example, an NSArray can contain multiple types of objects at once. This seems easy and flexible, but it makes the code much more error-prone, and relegates much type checking to runtime. Swift brings the safety of strongly typed variables so that as many of these errors can be caught at compile-time. Generics, for example, allow collections such as arrays to be strongly typed so they can only contain a particular type of object. This is enforced at compile-time.

Strings are value types

In Swift, strings are value types, not reference types as in Objective-C. They can easily be compared using the == operator or concatenated using the + operator. String values are also copied when passed into a function. In Objective-C, it is already a recommended practice to use the copy keyword when declaring string properties. This prevents the property from accepting mutable strings that can unexpectedly change during execution.

A taste of functional programming

In Swift, functions are first-class citizens, meaning that functions can be nested and used as parameters and return types (very much like Javascript). Swift embraces closures (like blocks in Objective-C, but with the power of lambdas in C#. Consistent with the functional programming paradigm, Swift also encourages the usage of immutable “constant” variables via the let keyword to make code safer. If a variable should never be changed, the compiler will enforce it. This is unlike Objective-C where classes have their own mutable and immutable versions.

Multiple return types

In Objective-C and other C-based languages, functions can only return one value. Swift allows for any number of return types called tuples, opening up possibilities for cleaner, leaner code.

A different kind of nil

In Objective-C, nil represents a pointer to nothing. Since only reference types (objects) use pointers, this means that nil cannot be used for value types such as ints, structs, or enums. Swift introduces optionals, which are akin to nullable types in C#. Any type can be made “nullable” or “optional” by appending a question mark to the end of the type name. The variable can then be checked for a value before being used.

If statements require booleans

In C-based if statements, the nil or the number 0 evaluates to false and any other value to true. This allows developers to perform shortcuts like if (array.count), where any value other than zero will return true. In Swift, in an if statement, the conditional must be a bona-fide Boolean expression like if (array.count > 0). This may eliminate certain code shortcuts but makes code more explicit and safer.

Switch statements are different

Switch statements are great branching mechanisms, and much better than if/else if there are more than a few conditions. However, Objective-C limits case comparisons to integer comparisons and equality tests. Swift allows for string and complex object comparisons. Also, by default, switch statements in Swift always exit as soon as they find a matching case, making the break keyword no longer necessary. Many developers will tell you that forgetting the break keyword is the cause of many bugs! However, this also prevents fallthroughs, a neat little trick that allows for very concise code. To activate fallthroughs, you must use the explicit fallthrough keyword.

A new range matching syntax

Swift comes with a convenient shorthand for range matching. 0...3 matches the values 0 through 3 (inclusive), while 0..3 matches the values 0 through 2 only.

Enums and structs can contain code

Although they are still value types, these types can contain functions, which is extremely helpful when converting an enum value to a “friendly” string.

No more boxing and unboxing

Ever try putting a BOOL or into an NSArray in Objective-C? Since NSArrays only accept objects (pointers), you have to convert it to an NSNumber first. Swift arrays do away with this major annoyance by natively supporting value types as well as reference types.

Only two types of collections

Unlike Objective-C, with many different collection types (NSSet, etc) as well as their mutable variants, Swift only has 2 collection types: arrays and dictionaries, and are mutable by default. They can be made immutable by use of the let keyword. Immutable arrays can have their contents changed as long as the array size does not change. Immutable dictionaries cannot have their values changed.

Generics

Generics are a powerful way to write code that is strongly typed, yet flexible. Like C#, Swift uses where after the type name to specify a list of requirements such as requiring the type to inherit from a particular class or implement a certain protocol.

Namespace collisions

Perhaps the worst “feature” of Objective-C is its lack of support for namespaces. In order to reduce the chance of a name collision, class names must be made “unique,” often by prefixing the class name with 2 or 3 letters. This makes code unnecessarily hard to read and is a huge turnoff for new developers. In Swift, if multiple imported modules share a member of the same name, it can be disambiguated by prefixing it with the module name:

import FrameworkA
import FrameworkB
FrameworkA.foo()

Properties and instance variables

Swift unifies properties and instance variables into a single class-level variable declaration, ending a long-standing debate among Objective-C developers. A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. Explicit getters and setters are only used for computed/derived properties. However, Swift does offer property observers via the new willSet and didSet hooks that run before and after setting a new value.

No access control mechanisms

For all its modern features, Swift strikingly lacks access modifiers (public/private/protected), which restrict access to a classes’ members. Currently, all code in Swift is public, which does not allow for proper encapsulation. But there are plans to add access modifiers in the future: “We don’t usually promise anything for the future, but in this case we are making an exception. Swift will have access control mechanisms.”

Inheritance

In Objective-C, most classes ultimately inherit from NSObject. In Swift, this is not needed. Like Objective-C, Swift supports single inheritance only. Subclasses that override functionality from their superclass must use the override keyword. However, you can prevent a method, property, or subscript from being overridden by marking it as final.

Initialization

Unlike Objective-C initializers, Swift initializers do not return a value. A Swift initializer is like an instance method with no parameters, written using the init keyword. Every class must have at least one designated initializer. Convenience initializers are secondary, supporting initializers for a class and are declared using the convenience keyword.

Protocols

Protocols are hugely important for low coupling between classes. Swift extends the same privilege to value types as well: classes as well as enumerations and structs can all adopt protocols. Protocols are also bona-fide types and can be used in return types and parameters.

Categories are called extensions

Objective-C categories are known as extension methods in most languages. Swift brings back this nomenclature with the extension keyword. A big difference is that Swift extensions are not named as they are in Objective-C, making their purpose a little less clear.

Memory gotchas are still there

Swift does not eliminate the retain cycles that plague Objective-C developers. The weak keyword is still there, as well as a new unowned keyword. Just as with Objective-C blocks, special care must be taken to avoid retain cycles in closures. Swift provides an elegant solution to this problem, known as a closure capture list. Each item in a capture list is a pairing of the weak or unowned keyword with a reference to a class instance (such as self):

Final Thoughts

Swift is clearly a much-needed replacement for Objective-C, and adds many modern features inspired by more recent languages. It is definitely “safer” in the sense that more errors can be caught at compile time vs. run time. It’s especially amazing how many object-level features Apple was able to carry over to value types. However, because of its backwards compatibility with Objective-C, I feel like it hasn’t been able to make a completely clean break (initializers, retain cycles). It is still sorely missing access modifiers. But it certainly is a step in the right direction. More importantly however, developers should be keenly aware of the vendor lock-in they are potentially getting themselves into by embracing Swift. Apple pulled the same trick by appropriating Cocos2D as SpriteKit, and more recently with CloudKit: build something beautiful and enticing, lure developers into learning it, and then make it unattractive to leave Apple’s warm embrace.

Share this:

Like this:

LikeLoading...

Related

About Martin Rybak

I am a New York area software developer and MBA with 10+ years of server-side experience on the Microsoft stack. I've also been a native iOS developer since before the days of ARC. I architect and develop full-stack web applications, iOS apps, database systems, and backend services.

Thank you very much! So nice to see namespaces finally, but it feels like a kludge on a kludge with no hope for the future. We will continue to write our back-ends in Java (j2objc) with light Objective-C UIs on top.