An iOS Developer’s Guide: From Objective-C to Learning Swift
After 6 years of improving and working on Objective-C, Apple decided to throw another challenge at developers. Once again, iOS developers will need to learn a new programming language: Swift.
Swift 1.0 is already a stable and strong development platform, which is sure to evolve in interesting ways over the coming years. It is a perfect moment to start exploring this new language, as it is the future of iOS development.
After 6 years of improving and working on Objective-C, Apple decided to throw another challenge at developers. Once again, iOS developers will need to learn a new programming language: Swift.
Swift 1.0 is already a stable and strong development platform, which is sure to evolve in interesting ways over the coming years. It is a perfect moment to start exploring this new language, as it is the future of iOS development.
Marco is a Senior iOS developer specializing in game development with a particular affinity for coding conceptually original apps.
Expertise
PREVIOUSLY AT
In 2008 Apple announced and released the iPhone SDK 2.0. This event started another revolution in software development, and a new breed of developer was born. They are now recognized as iOS developers.
Many of these developers had never used Objective-C before, and that was the first challenge Apple threw at them. Despite unfamiliar syntax and manual memory management, it was immensely successful, helping populate the App Store with tens of thousands of apps. Apple continually improved Objective-C with each release, adding blocks and literals, simplified memory management with automatic reference counting, and many other features indicative of a modern programming language.
And after 6 years improving and working on Objective-C, Apple decided to throw another challenge at developers. Once again, iOS developers will need to learn a new programming language: Swift. Swift removes the unsafe pointer management and introduces powerful new features, while maintaining interaction with both Objective-C and C.
Swift 1.0 is already a stable and strong development platform, which is sure to evolve in interesting ways over the coming years. It is a perfect moment to start exploring this new language as it is obviously the future of iOS development.
The purpose of this tutorial is to give Objective-C developers a quick overview of new Swift language features, helping you take the next step and begin adopting Swift in your everyday work. I will not spend too much time explaining Objective-C, and I will assume you are familiar with iOS development.
Trying out Swift vs. Objective-C
In order to start exploring Swift all you need to do is download XCode from the App Store and create a playground to experiment. All examples mentioned in this article are done this way.
Apple’s Swift homepage is the best reference for learning Swift programming. You will find it to be invaluable, and until you are fully up to speed with Swift development I believe that you will come back here often.
Variables and Constants
Declaring a variable in Swift is done using the var
keyword.
var x = 1
var s = "Hello"
You will notice that two variables x
and s
are of different types. x
is an Integer, while s
is a String. Swift is a type safe language, and it will deduce variable types from the assigned value. If you wish to make your code more readable you can optionally annotate the variable’s type:
var y: Int
y = 2
Constants are similar, but you declare them using let
instead of var
. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
let c1 = 1 // Constant known at compile time
var v = arc4random()
let c2 = v // Constant known only at run time
As their name suggests, they are immutable, so the following code will cause a compile-time error.
let c = 1
c = 3 // error
Other types can also be declared as constant. For example, the following code declares an array as a constant, and if you attempt to modify any of the elements, the Swift compiler will report an error:
var arr2 = [4, 5, 6]
arr2[0] = 8
print (arr2) // [8, 5, 6]
let arr = [1, 2, 3]
a[0] = 5 // error
Optionals
Constants need to be initialized when declaring them, and variables need to be initialized before use. So where’s the Objective-C nil
equivalent? Swift introduces optional values. Optional values can have a value or be nil
. If you take a look at the following code, you will notice that x
was assigned an Optional
value of 2014
. This means that Swift compiler was aware that x
might also be nil
.
var s = "2014"
var x = s.toInt()
print(x) // Optional(2014)
If you make a change in this code and assign the value "abc"
to s
, which cannot be converted to an Integer, you will notice that x
is now anil
.
var s = "abc"
var x = s.toInt()
print(x) // nil
The return type of toInt()
function is Int?
, which is an optional Int. Let’s try to call a standard function on x
:
var x = "2014".toInt()
print(x.successor()) // error
The compiler signals an error, since x
is an optional and could potentially be nil. We have to test x
first, and make sure that the successor
function is invoked on a real number, and not a nil
value:
var x = "2014".toInt()
if x != nil
{
print(x!.successor()) // 2015
}
Note that we have to unwrap x
by appending an exclamation mark (!). When we are sure that x
contains a value, we can access it. Otherwise we will get a runtime error. We can also do what Swift calls optional binding, converting the optional into a non-optional variable
let x = "123".toInt()
if let y = x
{
print(y)
}
The code in the if
statement will only execute if x
has a value, and assign it to y
. Note that we don’t need to unwrap y
, it’s type is not optional since we know x
is not nil
.
Check Apple’s Swift tutorial to read more details on optionals and nice features like optional chaining
String Interpolation
In Objective-C formatting strings is usually done with the stringWithFormat:
method:
NSString *user = @"Gabriel";
int days = 3;
NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];
Swift has a feature called string interpolation to do the same, but it is more compact and easier to read:
let user = "Gabriel"
let days = 3
let s = "posted by \(user) \(days) ago"
You can also use expressions:
let width = 2
let height = 3
let s = "Area for square with sides \(width) and \(height) is \(width*height)"
To learn more about Swift’s string interpolation and other new features, go here.
Functions
Function definition in Swift is different from C. A sample function definition is below:
func someFunction(s:String, i: Int) -> Bool
{
... // code
}
Swift functions are first-class types. This means that you can assign functions to variables, pass them as parameters to other functions, or make them return types:
func stringLength(s:String) -> Int
{
return countElements(s)
}
func stringValue(s:String) -> Int
{
if let x = s.toInt()
{
return x
}
return 0
}
func doSomething(f:String -> Int, s:String) -> Int
{
return f(s).successor()
}
let f1 = stringLength
let f2 = stringValue
doSomething(f1, "123") // 4
doSomething(f2, "123") // 124
Again, Swift infers the types of f1
and f2
(String
-> Int
), although we could have defined them explicitly:
let f1:String -> Int = stringLength
Functions can also return other functions:
func compareGreaterThan(a: Int, b: Int) -> Bool
{
return a > b
}
func compareLessThan(a: Int, b: Int) -> Bool
{
return a < b
}
func comparator(greaterThan:Bool) -> (Int, Int) -> Bool
{
if greaterThan
{
return compareGreaterThan
}
else
{
return compareLessThan
}
}
let f = comparator(true)
println(f(5, 9))
A guide to functions in Swift can be found here.
Enumerations
Enumerations in Swift are much more powerful than in Objective-C. As Swift structs, they can have methods, and are passed by value:
enum MobileDevice : String
{
case iPhone = "iPhone", Android = "Android", WP8 = "Windows Phone8", BB = "BlackBerry"
func name() -> String
{
return self.toRaw()
}
}
let m = MobileDevice.Android
print(m.name()) // "Android"
Unlike Objective-C, Swift enumerations can assign Strings, characters or floats as values for each member, besides integers. The convenient toRaw()
method returns the value assigned to each member.
Enumerations can also be parameterized:
enum Location
{
case Address(street:String, city:String)
case LatLon(lat:Float, lon:Float)
func description() -> String
{
switch self
{
case let .Address(street, city):
return street + ", " + city
case let .LatLon(lat, lon):
return "(\(lat), \(lon))"
}
}
}
let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco")
let loc2 = Location.LatLon(lat: 23.117, lon: 45.899)
print(loc1.description()) // "2070 Fell St, San Francisco"
print(loc2.description()) // "(23.117, 45.988)"
More information about enumerations is available here.
Tuples
Tuples group multiple values into a single compound value. The values within a tuple can be of any type and do not have to be of the same type as each other.
let person = ("Gabriel", "Kirkpatrick")
print(person.0) // Gabriel
You can also name the individual tuple elements:
let person = (first: "Gabriel", last: "Kirkpatrick")
print(person.first)
Tuples are extremely convenient as return types for functions that need to return more than one value:
func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int)
{
return (a/b, a%b)
}
print(intDivision(11, 3)) // (3, 2)
let result = intDivision(15, 4)
print(result.remainder) // 3
Unlike in Objective-C, Swift supports pattern matching in a switch statement:
let complex = (2.0, 1.1) // real and imaginary parts
switch complex
{
case (0, 0):
println("Number is zero")
case (_, 0):
println("Number is real")
default:
println("Number is imaginary")
}
In the second case we don’t care about the real part of the number, so we use an _
to match anything. You can also check for additional conditions in each case. For that we need to bind the pattern values to constants:
let complex = (2.0, 1.1)
switch complex
{
case (0, 0):
println("Number is zero")
case (let a, 0) where a > 0:
println("Number is real and positive")
case (let a, 0) where a < 0:
println("Number is real and negative")
case (0, let b) where b != 0:
println("Number has only imaginary part")
case let (a, b):
println("Number is imaginary with distance \(a*a + b*b)")
}
Note how we need to bind only the values we are going to use in the comparison or in the case statement.
To read more about tuples, go here.
Classes and Structures
Unlike Objective-C, Swift does not require you to create separate interface and implementation files for custom classes and structures. As you learn Swift, you will learn to define a class or a structure in a single file, and the external interface to that class or structure is automatically made available for other code to use.
Defining Classes
Class definitions are very simple:
class Bottle
{
var volume: Int = 1000
func description() -> String
{
return "This bottle has \(volume) ml"
}
}
let b = Bottle()
print(b.description())
As you can see, declaration and implementation are in the same file. Swift no longer uses header and implementation files. Let’s add a label to our example:
class Bottle
{
var volume: Int = 1000
var label:String
func description() -> String
{
return "This bottle of \(label) has \(volume) ml"
}
}
The compiler will complain because label is a non-optional variable and will not hold a value when a Bottle is instantiated. We need to add an initializer:
class Bottle
{
var volume: Int = 1000
var label:String
init(label:String)
{
self.label = label
}
func description() -> String
{
return "This bottle of \(label) has \(volume) ml"
}
}
Or, we could use Optional
type for a property, which does not to be initialized. In the following example we made volume
an Optional Integer
:
class Bottle
{
var volume: Int?
var label:String
init(label:String)
{
self.label = label
}
func description() -> String
{
if self.volume != nil
{
return "This bottle of \(label) has \(volume!) ml"
}
else
{
return "A bootle of \(label)"
}
}
}
Structures
The Swift language also has structs
, but they are much more flexible than in Objective-C. The following code tutorial defines a struct
:
struct Seat
{
var row: Int
var letter:String
init (row: Int, letter:String)
{
self.row = row
self.letter = letter
}
func description() -> String
{
return "\(row)-\(letter)"
}
}
Like classes in Swift, structures can have methods, properties, initializers, and conform to protocols. The main difference between classes and structures is that classes are passed by reference, while structs are passed by value.
This example demonstrates passing classes by reference:
let b = Bottle()
print(b.description()) // "b" bottle has 1000 ml
var b2 = b
b.volume = 750
print(b2.description()) // "b" and "b2" bottles have 750 ml
If we try similar case with struct
, you will notice that variables are passed by value:
var s1 = Seat(row: 14, letter:"A")
var s2 = s1
s1.letter = "B"
print(s1.description()) // 14-B
print(s2.description()) // 14-A
When should we use struct
and when should we use class
? As in Objective-C and C, use structs when you need to group a few values, and expect them to be copied rather than referenced. For example, complex numbers, 2D or 3D points, or RGB colors.
An instance of a class is traditionally known as an object. However, Swift classes and structures are much closer in functionality than in other languages, and much functionality can apply to instances of either a class or a structure type. Because of this, the more general term used in Swift reference is instance
, which applies to any of these two.
Learn the basics of Swift classes and structures here.
Properties
As we saw earlier, properties in Swift are declared with the var
keyword inside a class or struct definition. We can also declare constants with a let
statement.
struct FixedPointNumber
{
var digits: Int
let decimals: Int
}
var n = FixedPointNumber(digits: 12345, decimals: 2)
n.digits = 4567 // ok
n.decimals = 3 // error, decimals is a constant
Also keep in mind that class properties are strongly referenced, unless you prefix them with the weak
keyword. However there are some subtleties with weak non-optional properties, so read the automatic reference counting chapter in Apple’s Swift guide.
Computed Properties
Computed properties do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
The following code provides a sample of a computed value sign
:
enum Sign
{
case Positive
case Negative
}
struct SomeNumber
{
var number:Int
var sign:Sign
{
get
{
if number < 0
{
return Sign.Negative
}
else
{
return Sign.Positive
}
}
set (newSign)
{
if (newSign == Sign.Negative)
{
self.number = -abs(self.number)
}
else
{
self.number = abs(self.number)
}
}
}
}
We can also define read-only properties by just implementing a getter:
struct SomeNumber
{
var number:Int
var isEven:Bool
{
get
{
return number % 2 == 0
}
}
}
In Objective-C, properties are usually backed by an instance variable, declared explicitly or automatically created by the compiler. In Swift, on the other hand, a property doesn’t have a corresponding instance variable. That is, the backing store of a property cannot be accessed directly. Suppose we have this in Objective-C
// .h
@interface OnlyInitialString : NSObject
@property(strong) NSString *string;
@end
// .m
@implementation OnlyInitialString
- (void)setString:(NSString *newString)
{
if (newString.length > 0)
{
_string = [newString substringToIndex:1];
}
else
{
_string = @"";
}
}
@end
Since, in Swift, computed properties don’t have a backing store, we need to do something like this:
class OnlyInitialString
{
var initial:String = ""
var string:String
{
set (newString)
{
if countElements(newString) > 0
{
self.initial = newString.substringToIndex(advance(newString.startIndex, 1))
}
else
{
self.initial = ""
}
}
get
{
return self.initial
}
}
}
Properties are explained in more detail here
To Be Continued
There are many more important new things to learn about in Swift, like Generics, interaction with Objective-C libraries, closures, optional chaining, and operator overloading. A single tutorial cannot describe thoroughly a new language, but I have no doubt much more will be written about learning Swift programming. However, I believe that this quick read will help many Objective-C developers, who haven’t managed to find time and learn details of Swift language, get on track and let the Swift bird take them to a new heights.
Marco Mustapic
Gent, Belgium
Member since March 1, 2013
About the author
Marco is a Senior iOS developer specializing in game development with a particular affinity for coding conceptually original apps.
Expertise
PREVIOUSLY AT