From Objective-C to Swift: Swift Goodies

Swift comes with a lot of really nice features that make it hard to go back to Objective-C. The main feature is safety, but that may be seen as just a bonus side effect. There are a lot of other reasons to use Swift.

Strong Typing with Type Inference

Swift has strong typing, meaning that Swift will not convert between types for you, unless you ask it to. So you cannot just assign e.g. an Int to a Double. You have to typecast it first:

Or you have to extend the Int class with a method to implicitly convert to Double (this is generally a bad idea, and may not be possible when Xcode 6 goes out of beta):

Strong typing is really, really beneficial for safety. But it could become a bit of a daunting task if it wasn’t for Type Inference adding a lot of type information for you, almost like writing in a scripting language.

If you want to create an array containing several types (with no common ancestor), you should use an Enumeration (which can hold values, see below). If you would like it to be able to hold any type at all, you can use the Any type. To make it hold any Objective-C type, use the AnyObject type.

Please note that Type Inference won’t add the types for you when declaring functions. You have to explicitly state the type of functions you declare.

Blocks

Blocks in Swift look a lot like blocks in Objective-C, with two main exceptions: Type Inference and avoiding the weakify dance.

With Type Inference you don’t have to include the full type each time you write a block:

To read more about blocks, please visit: Closures.

On top of that, the Objective-C weakify dance gets a lot simpler, by just including e.g.   [unowned self]  or  [weak self]  at the start of the block:

Please note that Optionals (like the callback above) was explained in the Introduction post.

To read all about ARC in Swift, please refer to the ARC chapter.

Enumerations Supercharged

Enumerations in Swift are a major step up from Objective-C.

Fixing Enums

Apple has been advocating explicit sizes for enumeration types for a while, this is a mess in Objective-C:

This is fixed in Swift:

Extending Enums

Enums in Swift goes further than just being a list of unique choices. You can add methods (and computed properties):

Using type extensions, you can add methods to any enum you like:

Adding value to Enums

Enumerations in swift goes even further by allowing each unique choice to have an associated value:

You can consider this as a safe  union type, if you want. Or just the way enumerations should be.

For Apples take on it, go read the Enumerations chapter.

Swift Switches

As you just witnessed, switch statements in Swift have been improved in several ways.

The implicit fall-through behavior has been changed to be explicit:

Switch statements can access the associated values of enumerations, as you saw earlier, but they can do more than that:

It even works for strings:

And if you think it makes your code more readable, you could overload the  ~= operator to alter the behavior of switch statements!

You can read more about switch statements under Conditional Statements.

Classes and Structs

Like in C++, Swift classes and structs look the same at first:

The main difference is that classes are reference types (along with blocks), while structs are value types (along with enumerations). So two variables can point to the same (class) object, while assigning a struct to another variable will make a (lazy) copy of the struct. The ‘ mutating ‘ keyword tells callers that the enripen()  method cannot be invoked on constant structs. Mutating a constant reference to a class object is all fine.

Most built-in types are actually structs in Swift, and can be extended by user code. Even  Int. You can see the declarations for built-in stuff by Cmd-clicking e.g. an Int in Swift source code (or in a Playground). The Array and Dictionary types are structs too, so assigning an Array variable to another will copy the array and all its elements (this is done lazily on demand, though).

You can read more about the Collection Types in Apple’s documentation.

 The Life of an Object

Another major difference between classes and structs is that classes can be subclassed. Both classes and structs can be extended, and they can implement protocols, but only classes can inherit from other classes.

As you can see, inheritance adds a bit more fun Swift stuff to learn. For starters, you need to be explicit about your intention to override a method declared in a parent class. If you want to prevent children overriding something, you can add the attribute  final in front of either a single declaration or the entire class. For more attributes, see Apple’s documentation.

Initialization

Swift objects are initialized in two steps: First the object has to be valid, then it can be tweaked.

Making the object valid entails invoking one of the super class’s designated init() methods. Classes can have both designated initializers and convenience initializers (marked with the ‘convenience’ keyword). The convenience initializers call other initializers in the same class (ultimately a designated initializer), and designated initializers call the super class’s initializers.

If you add initializers for all the super class’s designated initializers, then your class will automatically inherit all the convenience initializers too. If you do not add any designated initializers at all, then your class will inherit all the initializers (designated and convenience) of the super class.

Please refer to Initialization for further reading.

Type casting

For casting between classes, especially down-casting,  you can use “is”, “as?”, and “as”:

To read all about it, visit the Type Casting chapter.

Generics

Generics are a major plus for Swift. They look a bit like C++ templates, but are stronger typed and simpler (simpler to use, and less capable).

The above was inspired by the Design Rationale for the Boost.Geometry C++ library. Generics in Swift are not as capable, but makes for much more readable source code than the C++ version.

Generics in Swift are parameterized by types. Each parameter type can be required to implement a specific protocol or inherit from a specific base class. After declaring the parameter types, an optional “where” clause can add requirements to the types (or their internal types, like the typealias ‘T’ in the PointTraits protocol above). A “where” clause can also require two types to be equal.

Apple has a much more thorough chapter on Generics.

Adopting Swift

You are now ready to go read all sorts of Swift source code, and even write your own :-)

A couple of final notes, before I leave you to the new and largely undiscovered land of Swift:

  • Apple recommends that you use Int for all integer types, even where you previously used an unsigned type. “Use UInt only when you specifically need an unsigned integer type with the same size as the platform’s native word size.”
  • Apropos Ints, you can now add underscores in number literals for greater legibility, e.g.
    The compiler simply ignores the underscores.
  • The String class has some nice initializers. E.g.
    As of this writing, this hasn’t been documented yet, so it may not go into the final release of Xcode 6.
  • If you create a Swift module, you can Cmd-click the name of your module to see the auto-generated Swift header for your module.
  • Swift modules are actually namespaces, so prefixing everything like CF, NS, UI, etc. isn’t strictly necessary anymore when creating third party libraries.

Enjoy using Swift!

Leave a Reply