At WWDC 2014, Apple shocked everyone by introducing a new programming language called Swift. Swift isn't an extension to the Objective-C language but is a completely new high-level, high-performance language. Swift takes inspiration from many different programming languages including JavaScript, C#, and Go. In this article, I'm going to take a look at the various features provided by the Swift language.
Swift is a new language and features are constantly added or removed with each new release of the iOS framework. The code implemented for this article used Xcode 6 Beta 5. You can download Xcode 6 from the . Xcode 6 introduces a new project template called Playgrounds. Playgrounds allows developers to be productive instantly by making the result appear immediately. Playgrounds also allow developers to watch progress in the timeline assistant. The timeline is capable of displaying variables in a graph, draws each step when composing a view, and can play an animated SpriteKit scene. When you are satisfied with the code, simply move the code from Playgrounds into your project.
Why Swift?
Have you ever wanted to create an iOS application but were never able to understand the alienating syntax of the Objective-C language? Don't worry; you're not alone! Many new developers have trouble getting started with the Objective-C language and some even give up on Objective-C and switch to tools like Xamarin and RubyMotion, neither of which are officially supported by Apple.
Fortunately, now you can write your iOS applications using Swift. Swift borrows many concepts from different languages including C#, Java, and Go. This means that you can use your existing skills and knowledge when developing iOS applications in Swift. The new operators and language features introduced in Swift also allow you to be more productive compared to its Objective-C counterpart.
Swift can be used interchangeably with the Objective-C language. This means that any third-party library implemented using Objective-C can be used in new projects implemented in Swift.
The main reason you should learn Swift is that it indicates Apple's future direction. By releasing Swift to the world, Apple has made it clear that this will serve as the default language for building future iOS applications. Although it's highly unlikely, in few years' time, Apple might restrict submissions of applications built using the Objective-C language.
Declarations
Swift allows multiple ways to declare your instances. You can use the let
keyword to define a constant or the var
keyword to define a variable.
let johndoe :String = "John Doe" // declares a constant
var marykate :String = "Mary Kate" // declare a variable
In the above code, I've indicated that the instances johndoe
and marykate
are of type String. One of the great powers of Swift is that it uses type inference. So, the above code can be simplified like this:
let johndoe = "John Doe"
var marykate = "Mary Kate"
Because the instance johndoe
is declared using the let
keyword, changing the value after the initialization results in a compile-time error. In terms of performance, Apple recommends using the let
keyword for instances that don't change during the lifetime of the application.
Functions
Methods in Objective-C are replaced by Functions in Swift. Swift
functions are declared using the func
keyword. Functions can return single or multiple values. A simple function that doesn't take any input parameter and returns a String
can be represented by the following Swift implementation:
func greet() -> String
{
return "Hello!"
}
// calling the method greet
let greeting = greet()
Often, you'll be interested in passing arguments to the Swift function. Swift uses the argument naming convention to pass the arguments. The function below shows how a string parameter can be passed to the Greet
function.
func greet(name :String) -> String
{
return "Hello! " + name
}
// calling greet
let greeting = greet("John Doe")
As, indicated earlier, Swift also allows you to return multiple values from a function. This wasn't supported in the Objective-C language. Consider a simple example where you pass a stock ticker to a method and it returns the relevant information about the stock.
func getStockInformation(ticker :String) -> (name:String,current :Double,
high:Double, low:Double)
{
return ("Apple",95.65,120.0,90.56)
}
let (name,current,high,low) = getStockInformation("APPL")
In the above code, I used a tuple variable to catch the return values from getStockInformation
function. Although the above code works as expected, it's error prone because the returned values are dependent on the specified position of the tuple variables. A much better approach would be to read the returned value into an object that consists of the typed information. Like this:
func getStockInformation(ticker :String) -> (name:String,current :Double,
high:Double, low:Double)
{
return ("Apple",95.65,120.0,90.56)
}
let stockInfo = getStockInformation("AAPL")
stockInfo.name
stockInfo.current
stockInfo.high
stockInfo.low
All the examples I've discussed so far performed an action on a single value. Swift also provides excellent support for arrays and dictionaries, which is covered in the next section.
Collection Types
Swift provides two collection types, arrays and dictionaries, for storing collection of values. The collections can be mutable or immutable depending on how they were created.
Arrays
Arrays in Swift behave differently from their counterpart NSArray
in Objective-C. In Objective-C NSArray
and NSMutableArray
can hold any type of item. Swift arrays are strongly typed and declared through explicit type annotation or type inference.
Array Literals
Arrays can be initialized using the array literal, which provides a shorthand approach to write one or more values as an array collection.
You can create a simple String array in Swift using the following implementation:
let names :[String] = ["John Doe","Mary Kate","Alex Lowe"]
You can use the power of Swift type inference and reduce the array declaration to the following:
let names = ["John Doe","Mary Kate","Alex Lowe"]
You can use the append
function to add a new item to the array like this:
names.append("David Lewis")
Unfortunately, the above code results in a compile-time error. The reason is that I declared the names
array using the let
keyword that makes it immutable. If you want to change the number of items in the array, you need to declare it using the var
keyword that makes the array mutable.
Iterating Over an Array
Along with the classic for
, while
, and do-while
loop, you can use the for-in
loop. The for-in
loop allows you to iterate over the entire set of values:
for name in names
{
println(name)
}
You might also be interested in the index of the item being iterated over. The enumerate
function takes an array as an argument and returns a tuple that consists of the index and the value.
for (index,name) in enumerate(names)
{
println("item \(index):\(name)")
}
Dictionaries
A dictionary is a container that stores multiple values of the same type. Each dictionary value is associated with a unique key that acts as an identifier for that value in the dictionary. Swift dictionaries differ from Objective-C NSDictionary
and NSMutableDictionary
classes that can use any kind of object as their keys and values. In Swift, the type of keys and values that a dictionary can store is indicated through an explicit type annotation or through type inference.
Dictionary Literals
Dictionaries can be initialized using dictionary literals. Dictionary literals allow a key-value pair syntax to form a dictionary. The key-value pairs are separated by a colon and can form a list separated by commas, surrounded by a pair of square brackets.
let stocks :[String:String] = ["AAPL":"Apple","GOOG":"Google"]
In that code, I declared a dictionary using a key-value type defined as String. Once again, you can use the power of Swift's type inference to reduce the above initialization to the following:
let stocks = ["AAPL":"Apple","GOOGL":"Google"]
Iterating Over a Dictionary
In Objective-C, iterating over a dictionary was cumbersome, but Swift makes it a pleasant experience. The same for-in
loop can be used to iterate over the dictionary, which returns a tuple.
for (symbol,name) in stocks
{
println("\(symbol):\(name)")
}
Apart from iterating over the key-value pairs in the dictionary, you can also retrieve the keys and values collection separately, as shown in the following implementation.
let stocks = ["AAPL":"Apple","GOOG":"Google"]
for ticker in stocks.keys
{
println("Ticker :\(ticker)")
}
for companyName in stocks.values
{
println("Company Name: \(companyName)")
}
Optionals
At the start of the article, I indicated that all the variables you declare, whether using the let
or var
keyword, must be accompanied with the initial value or you get a compile time error. The only exception to this rule is the optionals. Optionals allow you to indicate an absence of a value for any type at all.
var message :String? // this is valid
var anotherMessage :String // this
Consider a function that returns the age of the customer under certain conditions and then performs actions using the age as the parameter. The implementation is shown below:
func getAge(name :String) -> Int
{
if(age > 65) return age
return -1;
}
let age = getAge("John Doe")
if(age != -1)
{
// do something
}
In the above method, I use the -1 value to indicate the alternate case. The above works as expected but I can certainly use the power of optionals to make my intentions much clearer.
func getAge(name :String) -> Int?
{
if(age > 65) return age
return nil;
}
let age = getAge("John Doe")
if(age != nil)
{
println("The age is \(age!)") // forced
// unwrapping of optional
}
Furthermore, you can use the forced unwrapping operator ! to access the optional value. In the above code, you use it to unwrap the age
, which returns the actual age.
Closures
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in Objective-C.
Consider a scenario where you want to sort the elements on an array and return the sorted array. You can use the sorted
function, passing it the array to be sorted, and the function performs the actual sorting.
func backwards(s1 :String, s2 :String) -> Bool
{
return s1 > s2
}
let names = ["Azam","Mary","Alex","John"]
var reversed = sorted(names, backwards) // Mary
, John, George, Alex
The above solution works as expected, but creating a separate function to perform the actual sorting definitely looks like overhead. Fortunately, Swift closures can provide an easy solution. Instead of defining a separating backward
function, you can use closure, which takes in two parameters and returns a Bool
value.
let names = ["Azam","Mary","Alex","John"]
var reversed = sorted(names,{(s1:String,
s2:String) -> Bool in return s1 > s2})
The above code produces the exact same result as the previous one, but it's more compact and eliminates the need to declare the separate function to perform the sorting. The body of the closure starts after the in keyword. In the above example, the body comprises the comparison between the two arguments. You learned earlier that Swift uses type inference, which means that you don't have to specify the type of parameters and the return values when using closures. The sorting code above can be simplified to the following:
let names = ["Azam","Mary","Alex","John"]
var reversed = sorted
(names,{(s1, s2) in s1 > s2})
If the body of the closure only consists of one line, the return keyword can also be eliminated. You can go one step further and use the shorthand inline closure arguments, as shown below:
let names = ["Azam","Mary","Alex","John"]
var reversed = sorted(names,{ $0 > $1 })
As, you can see, that closure allowed you to express your intent with less code and more expressive implementation. Closures are used throughout the iOS SDK and understanding and using them is an important factor to being productive in the iOS development.
Generics
Swift borrows the concept of generics from the Java and the C# programming languages. Generics allow you to declare strongly typed, flexible, and reusable functions. Consider a scenario where you need to find the index of an item in an Int
array. You could implement the function using the following implementation:
func findIndexForInt(list :[Int],
valueToFind :Int) -> Int?
{
for(index,value) in enumerate(list)
{
if(value == valueToFind) {
return index
}
}
return nil
}
let numbers = [5,6,7,8,9,2,3,4]
let numberToFind = 7
let index = findIndexForInt
(numbers,numberToFind)
The above function works as expected and finds the index of the Int
value specified by the user. Unfortunately, this function only works with integer values and won't compile if you try to send values of a different type. This is where generics come in handy. Using generics, you can implement a single function that works for all of the types. The implementation below uses generics to make it more flexible.
func findIndex<T>(list :[T], valueToFind :T)
-> Int?
{
for(index,value) in enumerate(list)
{
if(value == valueToFind)
{
return index
}
}
return nil
}
Unfortunately, when you compile the above function you are greeted with an error indicating that the comparison isn't legal. The Swift compiler is complaining that you can't perform an equality operator between types, including your custom types. This issue can easily be resolved by specifying that the generic type must implement the Equatable
protocol. Types implementing the Equatable
protocol are guaranteed to implement the “==” operation.
func findIndex<T :Equatable>(list :[T],
valueToFind :T) -> Int?
{
for(index,value) in enumerate(list)
{
if(value == valueToFind)
{
return index
}
}
return nil
}
In the Collections section, you learned that Swift Arrays and Dictionaries can hold values of the same type. If you dig deeper into the implementations of the Swift Arrays and Dictionaries, you'll notice that they use the power of generics to restrict the types of elements they can contain.
Real-World Example
In this section, I'm going to use the Swift features I discussed earlier to create a simple Tip calculator application. The application consists of two user-input TextFields
that represent the total billed amount and the tip percentage. The user can press a button to calculate the tip, which is displayed in a UILabel
control on the screen.
Open Xcode 6 and create a new single-view-based application. Make sure that you have Swift
as the language choice. You will immediately notice that now the application consists of the .swift
files instead of the Objective-C .h
and .m
files.
Using the Storyboard designer, drag and drop two TextFields
, a UILabel
, and a UIButton
to the screen. Set the controls in any manner you desire. Now, that you have the controls on the view, you need to connect them to the view controller. Start by declaring the IBOutlets
in your ViewController
, as indicated below:
import UIKit
class ViewController: UIViewController {
@IBOutlet var totalAmountTextField :UITextField?;
@IBOutlet var tipPercentageTextField :UITextField?;
@IBOutlet var tipAmountTextField :UILabel?;
override func
viewDidLoad() {
super.viewDidLoad()
}
override func
didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The outlets are marked as optionals, which means that the outlets are nullable. The reason for making outlets nullable is that their initial value is null
. The next step is to connect the outlets to their counterparts on the screen. You can perform this using Storyboards. Simply drag an outlet connection from the view controller and drop it onto the control on the storyboard.
The next step is to create an action in the view controller that will be invoked by the button click on the screen. The action is indicated by the @IBAction
keyword, as shown in the implementation below:
@IBAction func calculateTip
(sender :AnyObject)
{
let totalAmount = totalAmountTextField!.text.toInt()
let tipPercentage = tipPercentageTextField!.text.toInt()
tipAmountTextField!.text = String((totalAmount! * tipPercentage!) / 100)
}
An important thing to notice is the use of the AnyObject
type. The AnyObject
type in Swift is identical to the id
in Objective-C, which represents an object of any type.
Another thing to notice is the use of the forced unwrapping operator !. The unwrapping operator is used to unwrap the value from the nullable type. If the value is null
, the app throws an error. Inside the calculateTip
function, I'm certain that the value of totalAmountTextField
will not be null
and will be hooked up to the control on the screen. Run the app, enter the total amount and the desired tip percentage, and press the calculate button. You'll see the total tip displayed on the screen.
You can extend the example by adding a UISlider
control for tip percentage selection instead of the UITextField
.
The purpose of this section was to use the new Swift language to create a very simple app. You also saw that in most scenarios, Swift allowed you to be more explicit and achieve the same results with fewer lines of code.
Conclusion
In this article, I covered some of the basic features provided by the Swift language. As, you saw, Swift either improved the existing APIs or introduced completely new APIs to your arsenal. In addition to the readability and more fluent syntax, Swift also provides major improvements in the performance of the application. The introduction of Swift clearly reflects the future direction of the Apple in building next-generation iOS applications.