At WWDC 2014, Apple has introduced one of the biggest updates to iOS since 2008 from a developer's point of view. They introduced HomeKit, HealthKit, CloudKit, and Extensions, just to name a few. But the biggest surprise out of WWDC 2014 was the introduction of a brand new programming language, Swift.
Swift is a wonderful programming language that has been built from the ground up to be efficient and safe. It uses the same APIs that Objective-C does. Or, what you can do in Objective-C, you can do in Swift. It also introduces some new concepts longtime programmers will appreciate and some of which I will cover in this introductory series on Swift.
In this series, I'm going to assume you're already familiar with Objective-C. In the first article of this series, I talk about Swift's philosophy, file structure, and syntax. In the second article, I zoom in on more advanced aspects of Swift's syntax, such as optionals and memory management. Hang on to your hats, folks, it's going to be a doozy.
1. Philosophy
To better understand Swift, Apple has been conditioning us with structural improvements in Objective-C over the last few years. Objective-C improvements like code blocks, literal array and dictionary definitions, and ARC (Automatic Reference Counting) are but a few things Apple added to Objective-C that ease the transition to Swift.
One important pillar of Swift's philosophy is code initialization. All objects and variables when defined in Swift must be initialized in code. An uninitialized object or variable will result in a compile time error in Swift. This ensures an object or variable always has a value. There's one special case in Swift for when an initial value cannot be defined. In this special case, your variable is called an optional. We will cover optionals in the second part of this series.
Another important pillar is branch completeness. All conditional branches, be it if
or switch/case
, must cover all conditions. This way, no code can fall through without it being covered. Missing a condition in your branch statement will be caught and generate a compile time error.
One last element of Swift's philosophy is its preference to constants over variables. Swift defines variables and constants in the following manner:
let someConstant : String = "This is a constant" var someVariable : String = "This is a variable"
In the above example, the let
keyword is used to define a constant while the var
keyword defines a variable. By making the definition of constants this easy, Apple encourages the use of constants whenever possible. This leads to safer code in a multithreaded environment and better code optimization as the compiler knows a constant's value won't change.
Now, there's much more to Swift than a few syntax and formatting enhancements. Swift was built from the ground up to fix many common C/C++, and inherently Objective-C, sources of crashes. Issues like:
- out-of-bound indexes in arrays
- uninitialized data
- unchecked return types
- unchecked pointer access
- implicit fall through
- goto errors
As someone who programs for both iOS and Android, I know first-hand how much more fun coding for the iOS platform really is with Cocoa and UIKit. This series will show you how much more fun coding can be by adding Swift to the mix.
2. File Structure
In Objective-C, we have header files (.h) and implementation files (.m). This is something Objective-C inherited from the C language.
In Swift, a class is defined in a single implementation file (.swift) that includes all the definitions and implementations of the class. This is reminiscent of other languages like Java and C#.
Gone is the need for juggling header files and adding the pesky #IFNDEF
at the top of header files.
3. Syntax
The first thing you'll notice about Swift is the disappearance of the semicolon at the end of each statement. In Swift, each line is considered a statement and we don't have to add a semicolon at the end of each line.
I emphasize have to, because nothing stops you from adding semicolons at the end of your statements. I will continue to add semicolons at the end of each statement as I think it increases readability. Also, it's really difficult to get rid of the habit of adding semicolons like Cocoa developers have done for years.
Another important change in Swift is that curly braces are mandatory for if
statements. That means no more Heartbleed bugs.
Syntax can be a complex subject to write about. Swift has a lot of subtleties that can take very long to go over, but that isn't the goal of this article. For brevity, I will concentrate on what changes an Objective-C developer would notice.
4. Similarities with Objective-C
I'll start off by showing you three code snippets that illustrate some of the similarities with Objective-C. It will help you in your understanding of the Swift language.
// Objective-C for (int index = 0; index < 5; i++) { NSLog(@"%d",index); } // Swift for index in 1..<5 { plrintln("\(index)"); }
// Objective-C switch(index) { case 0: break; case 1: break; default: break; } // Swift switch(index) { case 0: case 1: default: } // no break statement
// Objective-C if (index == 0) { } // Swift if index == 0 { } // parentheses are optional // curly braces are required
Objective-C programmers will find that Swift has the same branch and iteration statements you're already familiar with, such as if/else
, for
loops, for..in
loops, and switch
statements.
Swift includes two range operators, ..<
and ...
, to specify a range of values. In the above for
loop, we use the half-closed range operator, ..<
, to specify a range of values that includes 1, 2, 3, and 4, but it excludes 5. The other range operator is the closed range operator, ...
. It specifies a range of values that includes the value on both sides of the closed range operator. For example, 1...5
. specifies a range of values from 1 through 5, including 5.
5. Defining Variables and Constants
Let's revisit the example I showed you earlier.
let someConstant : String = "This is a constant"; var someVariable : String = "This is a variable";
In Swift, we define constants using the let
keyword and variables using the var
keyword. The colon, :
,is a marker to define types. In the above example, we're creating a constant and a variable of type String
.
We're also initializing the constant and the variable with a string. In Swift, strings are defined just like C strings in Objective-C, they're not preceded by an @
symbol.
Objective-C is a strongly typed language, which means that the type of a variable or parameter must always be specified. Swift is also a strongly typed language, but Swift is a bit smarter as the compiler will infer a variable's type. The compiler also makes sure that no incorrect casting of variables occurs without your expressed knowledge and intervention.
If we rewrite the above example letting type inference do its work, then the code snippet looks as follows:
let someConstant = "This is a constant"; var someVariable = "This is a variable"; let someInt = 1; let someFloat = 1.0;
This is much better and the code is much cleaner. The compiler is smart enough to understand that someInt
is an Int
and someFloat
is a Double
.
6. Strings
One way to get an idea of the strength of a language is by exploring how it handles string manipulation. Objective-C has a lot of functions and features that let us handle strings, better than most languages, but they tend to be verbose and confusing from time to time.
Let's start with an Objective-C example. To concatenate two strings in Objective-C, we do the following:
NSString *string = @"Hello "; NSString *greeting = [string stringByAppendingString:@"World!"];
In Swift, to append a string to another string, we use the +
operator. It's that simple.
let string = "Hello " let greeting = string + "World!"
Strings in Swift are Unicode, which means that we can write:
let string = "你好世界"
We can iterate the characters of a string using a for..in
statement as shown in the following example. We can use a for..in
loop to iterate Unicode strings too. It's really that cool.
let str = "Hello"; for char in str { println(char); } // outputs // H // e // l // l // o
One last thing I'd like to cover about strings before moving on is string interpolation. In Objective-C, if we want to output a string with variables, we invoke [NSString stringWithFormat:]
. In Swift, variables can be embedded. Take a look at the following example.
let x = 4; let y = 5; println( "\(x) x \(y) = \(x*y)") // outputs // 4 x 5 = 20
To use string interpolation, you wrap the variable or function call in parentheses and put a backslash in front of it, \( expression)
.
7. Arrays & Dictionaries
Array
& Dictionary
As an Objective-C programmer, you're already familiar with arrays and dictionaries. Swift also has collection classes and adds a few additional features to them.
How do you use collections in Swift? In Swift, they're called Array
and Dictionary
. Declaring an array in Swift is similar to declaring an array literal in Objective-C, using a set of square brackets, [ ]
, but without an @
symbol preceding them.
let someArray:[String] = ["one","two","three"]; var someOtherArray:[String] = ["alpha","beta","gamma"];
The same goes for dictionaries. However, instead of using curly braces, you use square brackets.
let someDictionary:[String:Int] = ["one":1, "two":2, "three":3]; var someOtherDictionary:[String:Int] = ["cats":1, "dogs":4, "mice":3];
Mutability
If an Array
object is equivalent to an NSArray
object and a Dictionary
object is equivalent to an NSDictionary
object, then how do we create mutable arrays and dictionaries in Swift?
The answer is very simple, declare the object as a variable. In other words, in the previous example someArray
is the equivalent of an NSArray
instance and someOtherArray
that of an NSMutableArray
instance. This applies to dictionaries too. In the previous example, someDictionary
is the equivalent of an NSDictionary
instance and someOtherDictionary
that of an NSMutableDictionary
instance. That's neat, right?
While Objective-C arrays and dictionaries can only contain objects, in Swift, collections can contain objects as well as primitive data types, such as integers and floats. Another important difference with Objective-C is that collections in Swift are typed, explicitly or through type inference at compile time. By specifying the type of the objects in a collection, Swift adds extra safety to these collections.
Even though we can omit a variable's type when declaring it, it doesn't alter the fact that the compiler will assign types to the objects in a collection. Using type inference helps to keep the code legible and light.
We can redeclare the Array
and Dictionary
objects we declared earlier as follows:
let someArray = ["one","two","three"]; var someOtherArray = ["alpha","beta","gamma"]; let someDictionary = ["one":1, "two":2, "three":3]; var someOtherDictionary = ["cats":1, "dogs":4, "mice":3];
The compiler will inspect the collections during their initialization and infer the correct type. In other words, it understands that someArray
and someOtherArray
are a collection of String
objects and someDictionary
and someOtherDictionary
are dictionaries with keys of type String
and values of type Int
.
Collection Manipulation
Adding an object or another array to an array is very similar to string concatenation in that we also use the +
operator.
var array = ["one","two","three"]; // mutable array array += "four"; // add element to array array += ["five","six"]; // add array to array
Manipulating dictionaries is just as easy.
var dictionary = ["cat": 2,"dog":4,"snake":8]; // mutable dictionary dictionary["lion"] = 7; // add element to dictionary dictionary += ["bear":1,"mouse":6]; // add dictionary to dictionary
Typed Collections
Earlier, I mentioned that collections in Swift are typed, but what does that mean? If we define a collection that contains String
objects, you can only add String
objects to that collection. Failing to do so will result in an error.
Let's look at an example to clarify this. We define a collection of Car
objects. The following code snippet shows the Car
class definition and a mutable array cars
, containing three Car
instances.
// Car class class Car { var speed = 0.0 func accelerate(by: Double = 1.0) -> Bool { speed += by; return true; } } var cars = [ Car(), Car(), Car()];
Behind the scenes, the compiler will infer the type of the array. If we want to add a new Car
instance to the collection, we can simply use the +
operator as shown below.
cars += Car();
However, adding an object of a different type will result in an error.
cars += "Some String"; // this will cause a compiler error
This has the added advantage of type safety for fetching objects from collections. It also eliminates the need for casting collection objects before using them.
In our example, a Car
object has an accelerate
method. Because a collection is typed, we can grab an item from the array and immediately invoke a method on the object, in one line of code. We don't need to worry about the element's type since the collection only contains Car
objects.
cars[0].accelerate(by: 2.0);
To accomplish the same thing in Objective-C with the same level of safety, we'd need to write the following:
id pointer = cars[0]; if([pointer isKindOfClass:[Car class]]) { Car* car = (Car*) pointer; [car accelerateBy:2.0]; }
Finally, to iterate an array, we use a for..in
loop:
for car in cars { car.accelerate(by:2.0); }
To iterate a dictionary, we also use a for..in
loop:
for (key,value) in someDictionary { println("Key \(key) has value \(value)" }
As you can see, typed collections are a powerful feature of the Swift language.
Conclusion
We've already learned quite a bit about the Swift language and you should take the time to let it sink in. I recommend that you download Xcode 6 as soon as possible and start applying what you've learned in this article in Xcode's new Playground feature. Playgrounds let you play with Swift in real time.
That's it for this article. In the next instalment of this series, we take a look at tuples, functions, closures, classes and last but not least, optionals. You'll also learn how memory management works in Swift. Stay tuned.
Comments