Essential Swift for Objective-C Programmers
In this tutorial we will aim to learn essential parts of the Apple's Swift programming language in order to start programming iOS applications with it. The aim of this tutorial is not to learn every feature of the language. Syntax and features wise Swift is a much bigger language compared to Objective-C. In this first part we will explore the syntax of Swift which is more or less parallel to the constructs which are provided in Objective-C. In a later tutorial we will explore more advanced features of Swift.
To explain the core syntax better we will develop a command line OS X Application.
Creating the project
- Using XCode, start a new project. Under OS X Application Category, choose Command Line Tool.
- For the product name, provide SwiftExamples and choose Swift as the language.
This should create a project, with a main.swift
file, which contains only a "Hello, World" print statement.
You can also to download the source code for this example .
Define a class
Let's create a class. Right click on the SwiftExamples
folder and choose "New File". Then choose "Swift File" under Mac OS X Source category. Name it as Rect.swift
Edit the file so that it loos like the following:
File: SwiftExamples/Rect.swift
import Foundation
class Rect {
var height: Int = 0;
var width = 0
}
- Unlike Objective-C, Swift allows you declare and define a class in a single
.swift
file. - The first line imports the
Foundation
framework. - To create a class, you use the keyword
class
. In the above example, we have created the classRect
and added 2 properties to it:height
andwidth
. - As you can see from the code, semicolons are optional. The preferred style is to omit the semicolons if they are not required.
- A variable is declared with keyword
var
. To specify the type of the variable, you can insert a:
and then mention the variable type after that. - When you are immediately assigning a value to the variable, then specifying the type of the variable is not required as the complier can detect the type of the variable from the assigned value. As a stylistic preference, the variable type should not be mentioned if it is evident from the assigned value.
- Please note that Swift is a statically typed language. It's not a dynamically typed language like PHP or JavaScript, in which you can assign values of different types to the same variable.
- Also note that unlike many other programming languages, the even primitive type names start with capital letters, for example,
Int
.
Now open 'main.swift' and remove the print
'Hello, World' statement and make it look like the following:
File: SwiftExamples/main.swift
import Foundation
var rect = Rect()
- Here we have created one object from the class
Rect
. Please note that unlike Java or PHP, you don't use the keywordnew
to instantiate a class.
Now let's add a describe
method to the Rect class.
File: SwiftExamples/Rect.swift
import Foundation
class Rect {
...
func describe() {
print("Rect - Height: \(height) Width: \(width)")
}
}
- To define a function, you need to use the keyword
func
. - Here we are using the String Interpolation to substitue the variable values in the string.
Now add the following line in the main.swift
file.
File: SwiftExamples/main.swift
...
rect.describe()
Run the program.
output:
Rect - Height: 0 Width: 0
Now add the following line in the main.swift
file and run the program.
File: SwiftExamples/main.swift
...
rect.height = 30
rect.width = 30
rect.describe()
output:
Rect - Height: 0 Width: 0
Rect - Height: 30 Width: 30
Inheritance
Let's say that we want to create a Shape
class which will be the parent class for all the shapes. So we want to extend the Rect
class from the Shape
class. Create a new file Shape.swift
file in your project and add the following code in it.
File: SwiftExamples/Shape.swift
import Foundation
class Shape {
func describe() {
print("The shape has no properties yet.")
}
}
Then modify your Rect.swift
file so that it looks like the following:
File: SwiftExamples/Rect.swift
import Foundation
class Rect: Shape {
var height: Int = 0;
var width = 0
override func describe() {
print("Rect - Height: \(height) Width: \(width)")
}
}
- Here to extend the Rect class from the Shape class, we have append the
: Shape
on the class declaration line. - Also since we are overriding the describe function in the Rect class we need use the keyword
override
while providing the definition of thedescribe()
method.
Append the following to your main.swift
file.
File: SwiftExamples/main.swift
...
var shape = Shape()
shape.describe()
var rect1 = Rect()
rect1.describe()
output:
...
The shape has no properties yet.
Rect - Height: 0 Width: 0
Methods and Functions
- Swift allows stand alone functions outside classes. So you can mix procedural as well as object oriented code. In this tutorial we will focus on instance and class methods.
Methods that return value
Let's add an area method to the Rect class.
File: SwiftExamples/Rect.swift
import Foundation
class Rect: Shape {
...
func area() -> Int {
return width * height
}
}
File: SwiftExamples/main.swift
...
var rectArea = rect.area()
print("Rect area is: \(rectArea)")
output:
...
Rect area is: 900
Methods with parameters
Since Swift is designed to be compatible with Objective C, it supports external parameter names. However Swift uses round brackets to specify the parameters to the functions, so rules to specify external parameter names are a little convoluted in Swift. The main rule to remember is:
By default, the first parameter omits its external name, and the second and subsequent parameters use their local name as their external name.
If you follow this you can create methods without typing out the saperate external parameter names and yet your code follows the standard Swift naming conventions.
File: SwiftExamples/Rect.swift
import Foundation
class Rect: Shape {
...
func increaseHeight(height: Int, width: Int) {
self.height += height;
self.width += width;
}
}
File: SwiftExamples/main.swift
...
rect1.increaseHeight(30, width: 40)
rect1.describe()
output:
...
Rect - Height: 30 Width: 40
Initializers
Initializers look like methods but they have keyword init
as their name. Initializers in Swift are similar to constructors in programming languages like Java and C#. They allow you to initialise the default state of the object.
Here is the rewritten Rect
class which uses 2 initializers. The fitst one overrides the default no parameter initilizer whereas the other one accepts height
and width
parameters so that it is convenient to set when the object is being created.
File: SwiftExamples/Rect.swift
import Foundation
class Rect: Shape {
var height: Int;
var width: Int;
override init() {
height = 0
width = 0
}
init(height h: Int, width w: Int) {
height = h
width = w
}
override func describe() {
print("Rect - Height: \(height) Width: \(width)")
}
func area() -> Int {
return width * height
}
func increaseHeight(height: Int, width: Int) {
self.height += height;
self.width += width;
}
}
File: SwiftExamples/main.swift
...
var rect2 = Rect()
rect2.describe()
var rect3 = Rect(height:50, width:60)
rect3.describe()
output:
...
Rect - Height: 0 Width: 0
Rect - Height: 50 Width: 60
Class/Type Methods and Static/Type Properties
Type methods in Swift are similar to static methods in Java and C#. You can call these methods without creating an instance of the class.
Type properties are similar to static properties in Java and C#.
To define a type method, you use the class
keyword, whereas to define a type property you use the static
keyword.
In the following example, we use a type property instancesCreated
to keep a count of total instances created using the Rect
class. Also we have defined a type method printInstancesCreated
which allows us to print the instancesCreated
property.
One more thing to note that in type methods of the same class you can use the type properties with using class reference. But in the instance methods and initializers you need to use the class reference, for example, Rect.instancesCreated
to use the type properties.
File: SwiftExamples/Rect.swift
import Foundation
class Rect: Shape {
static var instancesCreated: Int = 0;
class func printInstancesCreated() {
print("Total instances created from Rect: \(instancesCreated)")
}
var height: Int;
var width: Int;
override init() {
height = 0
width = 0
Rect.instancesCreated += 1
}
init(height h: Int, width w: Int) {
height = h
width = w
Rect.instancesCreated += 1
}
override func describe() {
print("Rect - Height: \(height) Width: \(width)")
}
func area() -> Int {
return width * height
}
func increaseHeight(height: Int, width: Int) {
self.height += height;
self.width += width;
}
}
File: SwiftExamples/main.swift
...
Rect.printInstancesCreated()
output:
...
Total instances created from Rect: 4
Now that we have looked at essentials of Object Oriented Programming in Swift, let's take a quick tour of some other essential features of the language.
Constants
The constants are declared with the keyword: let
.
File: SwiftExamples/main.swift
...
let PI = 3.14
let BASE_URL: String = "http://mysite.com/api/"
print("PI:\(PI), BASE_URL: \(BASE_URL)")
output:
...
PI:3.14, BASE_URL: http://mysite.com/api/
Please note that as a best practice, you will notice that Swift progammmers are encouraged to use constants a lot more than variables.
If you need to initialize the value of a vairable only once in your function or a class, it is much better to use a contstant than a variable. This leads to more maintaible code as there is no chance of accidently modifying the value of a constant.
Optionals
Swift has a concept of optionals which is not present in C or Objective-C. If you have variable which might or might not have a value at a given time during the execution of a program, then you can declare it as optional by providing a ?
after the typename.
To quote Apple's Swift Programming Language book:
An optional says:
There is a value, and it equals x
or
There isn’t a value at all
Swift's nil is also not same as nil in Objective-C. In Objective-C, nil can be set to only to variables of object type. In Swift, nil indicates absence of a value and can be set to variable of any type which is declared as optional.
File: SwiftExamples/main.swift
...
var num:Int? = 35
print("num: \(num)")
num = nil
print("num: \(num)")
output:
...
num: Optional(35)
num: nil
If Statements, Forced Unwrapping and Optional Chaining
If statements can be used to determine if an optional contains a value.
Note: Swift doesn't use round brackets in control flow statements, aroud the control conditions. This can be seen in the following if
statement.
...
var rect4:Rect? = Rect(height:5, width:5)
if rect4 != nil {
print("rect4.height: \(rect4!.height)")
} else {
print("rect4 is nil")
}
output:
...
rect4.height: 5
Here in the if statement you have first made sure that rect4 is not null. Then to access the value, you need to use !
operator in the code that follows. So to access the height property, you need to write:
print("rect4.height: \(rect4!.height)")
The same line can also be written as:
print("rect4.height: \(rect4?.height)")
If you use !
operator (called as forced unwrapping) and the value of rect4 is nil, then there will be a runtime error. If you use ?
operator (called as optional chaining) and the value of rect4 is nil, then the operation will fail gracefully.
However as a best practice, in case of optional variables, you should always make sure that optional variables are not nil using a if
staetment and then only access the vlaue with forced unwrapping.
If you need to use the optional variable multiple times then you can use following tactic to convert the optional to a variable. Check how the instance variable window
is converted into a constant in the if
block. Once this is done, inside the if block you can use the window constant, without the !
operator.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
if let window = window {
window.backgroundColor = UIColor.whiteColor()
window.makeKeyAndVisible()
}
return true
}
...
}
As a best practice, while designing your own classes and APIs, try to use optionals as little as possible. Unfortunately, UIKit and other core iOS development frameworks are designed to use nil
values at quite a few places. So you will see a lot of optionals being used in your code that depends on core iOS frameworks.
Protocols
Protocols in Swift have a lot of features compared to protocols in Objective-C. In Swift, the protocols can define propeties, static methods, regular methods, mutating methods, optional methods and required initilizers. To understand full capabilities of Protocols in Swift, please check the Protocols Section to the Swift Programming Language Book.
If you want to use optional methods in a protocol, then it must be marked with the @objc
attribute. Then to use this protocol in a class, that class must be marked with the @objc
attribute. Only classes that extend from NSObject
can be marked with the @objc
attribute.
File: SwiftExamples/Game.swift
import Foundation
@objc protocol GameDelegate {
var moves : Int {get}
func gameStarted()
optional func gameMoved()
}
class Game {
var delegate: GameDelegate?
func startGame() {
print("Game Started")
delegate?.gameStarted()
}
func makeMove() {
print("Made the move")
delegate?.gameMoved?()
}
}
@objc class GameDelegateImpl : NSObject, GameDelegate {
var moves = 0;
func gameStarted() {
print("GameDelegateImpl : gameStarted")
}
}
File: SwiftExamples/main.swift
...
var game = Game()
game.delegate = GameDelegateImpl()
game.startGame()
game.makeMove()
output:
...
Game Started
GameDelegateImpl : gameStarted
Made the move
In this example, we have created a class Game
. Also we have created the protocol GameDelegate
. Since we want to provide an optional method in the protocol we need to mark it with @objc
attribute.
In the Game
class the delegate
property is marked as optional. The programmer who is using the Game
class will decide if he wants to provide a delegate or not. Hence in the implemetation of the Game class you need to use optional chaning delegate?
while calling the methods on the delegate. This way if the delegate is nil
, the operation will fail gracefully. Also since the method gameMoved
is optional in the delegate, while calling the method on the delegate, you need to call the method via optional chaining ( delegate?.gameMoved?()
) so as to allow it to fail gracefully, if in case the delegate is provided, but the method is not implemented.
Finally, the class GameDelegateImpl
implementes the GameDelegate
protocol. Since the GameDelegate
protocal has an optional method, the GameDelegateImpl
needs to extend from NSObject
and it needs to be marked with attribute @objc
.
Conditionals
if...else Statement
In Swift, the brackets around the condition for the if
statement (and in case of the loop statements too) are not required.
File: SwiftExamples/main.swift
...
var testNum = 13
if testNum % 2 == 1 {
print("\(testNum) is an odd number.")
} else if testNum == 0 {
print("testNum is zero.")
} else {
print("\(testNum) is an even number but not zero.")
}
output:
...
13 is odd number.
Also you should note that the if
statement in Swift expects the condition to evaluate to a boolean value. If you try to use Int
as the type for the conditional the compiler will flag an error.
switch Statement
switch
Statement in Swift is improved than that of C. Unlike C, in Swift in switch statement can use String values for comparison.
File: SwiftExamples/main.swift
...
var language = "English"
switch language {
case "English":
print("You chose English language")
case "French":
print("You chose French language")
default:
print("You didn't choose a language")
}
output:
...
You chose English language
Also note the absence of the break statements in the above example. Unlike C, in Swift, the control doesn't fall through to the next case if the break statement is missing.
If you need to match one of the multiple options to the variable provided to the switch statement, then you can use comma separated values in the case statement.
File: SwiftExamples/main.swift
...
var cardType = "Master"
var interestRate: Float? = nil
switch cardType {
case "Visa", "Master":
interestRate = 3.0
case "American Express":
interestRate = 2.75
default:
interestRate = 3.25
}
print("Interest Rate is \(interestRate)")
output:
...
Interest Rate is 3.0
Range Matching in switch Statement
You can also use ranges instead of constant values in the case statements.
File: SwiftExamples/main.swift
...
var dwarfs = 4
switch dwarfs {
case 0...6:
print("Some dwarf brothers are missing!")
case 7:
print("All dwarf brothers are present.")
default:
print("You got it all wrong dude!")
}
output:
Some dwarf brothers are missing!
Control Flow Statements : for, while and do...while
In Swift, these loop constructs are similar that of C.
for Loop
Warning : In the 3.0 verion of the Swift programming language, the counter based C style for loop might be removed or deprecated . So do not use it for new code. Use the for...in
loop instead.
File: SwiftExamples/main.swift
...
for var counter = 0; counter < 10; counter++ {
print("counter: \(counter)")
}
output:
counter: 0
counter: 1
counter: 2
counter: 3
counter: 4
counter: 5
counter: 6
counter: 7
counter: 8
counter: 9
while Loop
File: SwiftExamples/main.swift
...
var index = 35.3
while index < 45.0 {
print("index: \(index)")
if index > 35.5 {
break
}
index += 0.125
}
output:
...
index: 35.3
index: 35.425
index: 35.55
repeat...while Loop
File: SwiftExamples/main.swift
...
var card = 0
repeat {
card++
if card == 5 {
continue
}
print("card: \(card)")
} while card < 10
output:
...
card: 1
card: 2
card: 3
card: 4
card: 6
card: 7
card: 8
card: 9
card: 10
Collection Types
Arrays
This is how you can declare arrays like this:
File: SwiftExamples/main.swift
...
var greatActors: [String] = ["Marlon Brando", "Jack Nicholson", "Morgan Freeman"]
var emptyArray = [Double]()
var arrayWithThreeValues = [Double](count: 3, repeatedValue: 0.0)
Here specifying the type is optional, if you are initializing the array with some values, as the compiler can detect the type for you.
File: SwiftExamples/main.swift
...
var greatDirectors = ["Steven Spielberg", "Francis Ford Coppola", "Quentin Tarantino"]
for...in Loop for Arrays
File: SwiftExamples/main.swift
...
print("Output all array elements:")
for director in greatDirectors {
print("\(director)")
}
output:
...
Output all array elements:
Steven Spielberg
Francis Ford Coppola
Quentin Tarantino
Changing Array Elements
In Swift arrays (and other collections) are mutable if you use them as a variable. You can change, add and remove elements from an array as required if it is assigned to a variable. However, if you assign a collection to a constant then it is immutable, meaning its size and contents can't be changed.
It's a good practice to use collections as constants when you know beforehand that they are not going to change. The Swift compilar can add some optimizations based on this information.
Here are some examples of how you can manipulate arrays.
File: SwiftExamples/main.swift
...
greatDirectors[0] = "James Cameron"
You can also change the multiple array items with the range syntax:
File: SwiftExamples/main.swift
...
greatDirectors[1...2] = ["Robert Zemeckis", "Peter Jackson"]
print("Output all array elements:")
for director in greatDirectors {
print("\(director)")
}
output:
...
Output all array elements:
James Cameron
Robert Zemeckis
Peter Jackson
Append Items to Array
File: SwiftExamples/main.swift
...
greatDirectors += ["Coen Brothers"]
greatDirectors += ["David Fincher", "Ang Lee"]
print("Output all array elements with indexs:")
for (i, director) in greatDirectors.enumerate() {
print("\(i) : \(director)")
}
output:
...
Output all array elements with indexs:
0 : James Cameron
1 : Robert Zemeckis
2 : Peter Jackson
3 : Coen Brothers
4 : David Fincher
5 : Ang Lee
Note : Here the eumerate method of the array returns something called as "Tupal". Tupals are explained below.
Remove Items from Array
File: SwiftExamples/main.swift
...
greatDirectors.removeAtIndex(3)
print("Output all array elements with indexs:")
for (i, director) in greatDirectors.enumerate() {
print("\(i) : \(director)")
}
output:
...
Output all array elements with indexs:
0 : James Cameron
1 : Robert Zemeckis
2 : Peter Jackson
3 : David Fincher
4 : Ang Lee
Array: Other Importnant Properties and Methods
File: SwiftExamples/main.swift
...
var directorsCount = greatDirectors.count
print("Number of Great Directors count: \(directorsCount)")
var arrayCapacity = greatDirectors.capacity
print("arrayCapacity: \(arrayCapacity)")
greatDirectors.removeAll(keepCapacity: false)
arrayCapacity = greatDirectors.capacity
print("arrayCapacity: \(arrayCapacity)")
greatDirectors.reserveCapacity(10)
arrayCapacity = greatDirectors.capacity
print("arrayCapacity: \(arrayCapacity)")
output:
...
Number of Great Directors count: 5
arrayCapacity: 6
arrayCapacity: 0
arrayCapacity: 10
Since arrays are mutable and indexed, sometimes it is useful to pay attention to the capacity of the array to ensure that you don't endup requesting too many memeory allocations which can hamper performance. For example, if you know beforehand that an array is going to require space for a hundred elements then you can reserve the capacity in advance.
Dictionaries
Dictionary is a key value data structure. Example:
File: SwiftExamples/main.swift
...
var movieActors: [String: String] = [
"The Godfather": "Marlon Brando",
"The Shawshank Redemption": "Morgan Freeman"
]
Like arrays if you are providing the initial values then you neednot specify the datatype as the compiler can detect it for you.
File: SwiftExamples/main.swift
var capitals = [
"United States": "Washington, D.C.",
"United Kingdom": "London"
]
Iterating over a Dictionary
File: SwiftExamples/main.swift
...
for(country, capital) in capitals {
print("\(country) : \(capital)")
}
output:
...
United Kingdom : London
United States : Washington, D.C.
Dictionary: Accessing and Modifying Values
File: SwiftExamples/main.swift
...
movieActors["The Shawshank Redemption"] = "Tim Robbins"
movieActors["Green Lantern"] = "Ryan Reynolds"
print("Actor in Green Lantern: " + movieActors["Green Lantern"]!)
movieActors.removeValueForKey("Green Lantern")
print("movieActors count \(movieActors.count)")
for movie in movieActors.keys {
print("\(movie) : \(movieActors[movie])")
}
output:
...
Actor in Green Lantern: Ryan Reynolds
movieActors count 2
The Godfather : Optional("Marlon Brando")
The Shawshank Redemption : Optional("Tim Robbins")
Tupals
Tupal is a concept found in many functional programming languages . Tupal is basically a sequence of data. It can contain items of different types. In Swift you can access tupal elements by indexes with "." syntax. You can also give names to the tupal elements and access the elements by names.
You can use tupals to return multiple values from fuctions.
File: SwiftExamples/main.swift
...
var aTupal = (2, "Mother", 3.5, "This example is pointless.")
print("second element of tupal : \(aTupal.1)")
var aCat = (name: "Kitty", kittens: 3)
print("Name of the cat: \(aCat.name)")
output:
second element of tupal : Mother
Name of the cat: Kitty
Characters and Strings
Characters and Strings datatyes in Swift support unicode from ground up.
Characters
File: SwiftExamples/main.swift
...
var aLetter: Character = "\u{03c0}"
print("aLetter: \(aLetter)")
output:
aLetter: π
Note: Here note that characters are defined using double quotes which is different from C and Objective-C. In Swift, since strings are also defined using double quotes to define a character variable you need to explictly specify the datatype.
Strings
File: SwiftExamples/main.swift
...
var aString: String = "this is a string"
print("aString : \(aString)")
aString += " with some words"
print("aString : \(aString)")
var anotherString = "This is another string"
for letter in anotherString.characters {
print(letter)
}
print("the number of characters in '\(anotherString)' is \(anotherString.characters.count)")
output:
aString : this is a string
aString : this is a string with some words
T
h
i
s
i
s
a
n
o
t
h
e
r
s
t
r
i
n
g
the number of characters in 'This is another string' is 22
That's it! Here we have completed the tour of the essential syntax of the Swift programming language. Armed with this information, now you should be able to jump start an iOS application using the Swift programming language.