Alex headshot

AlBlue’s Blog

Macs, Modularity and More

Swift - classes

2014, mac, swift

In this episode of the Swift introduction series we’ll be looking at classes in Swift.

Swift is a language that supports both functional and object oriented concepts. Classes are defined with the class keyword and the body is surrounded in braces.

Objects can have instance variables specified as either var or constants with let inside the block. As with other variables, the type can be explicitly declared or can be inferred by an initial value:

Instance variables
1
2
3
4
5
Welcome to Swift!  Type :help for assistance.
  1> class Person {
  2.   var firstName = ""
  3.   var lastName:String?
  4. }

Non-optional values need to be initialized in-line, or in an initializer.

Once the class is defined then can be created using a type constructor. Instance properties can be accessed using dot syntax:

Instantiating classes
1
2
3
4
5
6
7
8
9
10
11
  5> var alex = Person()
alex: Person = {
  firstName = ""
  lastName = nil
}
  6> alex.firstName = "Alex"
  7> alex
$R0: Person = {
  firstName = "Alex"
  lastName = nil
}

Class types use pass-by-reference semantics; two variables can point to the same instance, so if one variable is used to change the value, then it is seen through the other variable as well:

Class types are pass by reference
1
2
3
4
5
6
7
8
  8> var alblue = alex
alblue: Person = {
  firstName = "Alex"
  lastName = nil
}
  9> alblue.firstName = "AlBlue"
 10> alex.firstName
$R1: String = "AlBlue"

Instance functions can be added to a class. As a result, it’s possible to add a fullName method that returns the concatenation of the first and last names:

Instance methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 11> class Person {
 12.   var firstName = ""
 13.   var lastName:String?
 14.   func fullName() -> String {
 15.     return "\(firstName) \(lastName!)"
 16.   }
 17. }

 18> var alex = Person()
$R2: Person = {
  firstName = ""
  lastName = nil
}

 19> alex.firstName = "Alex"
 20> alex.lastName = "Blewitt"
 21> alex.fullName()
$R3: String = "Alex Blewitt"

A special kind of function can be created which is used at class creation time, called the initializer. Fields that are assigned in the initializer do not need to be initialized at the declaration. However, they do need to be declared for a type perspective:

Initializer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 22> class Person {
 23.   var firstName:String
 24.   var lastName:String
 25.   init(first:String, last:String) {
 26.     firstName = first
 27.     lastName = last
 28.   }
 29.   func fullName() -> String {
 30.     return "\(firstName) \(lastName)"
 31.   }
 32. }

 33> Person()
error: missing argument for parameter 'first' in call
Person()
      ^

 33> Person(first:"Alex", last:"Blewitt")
$R4: Person = {
  firstName = "Alex"
  lastName = "Blewitt"
}

The arguments are implicitly named; if you try and call it without passing in names (such as Person("Alex", "Blewitt")) then Swift will complain that the labels are missing. This can be fixed by using an underscore _ in front of the labels of the init, which instructs Swift to not consider them named:

Dealing with positional instance initializers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 34> Person("Alex", "Blewitt")
error: missing argument labels 'first:last:' in call

 34> class Person {
 35.   var firstName:String
 36.   var lastName:String
 37.   init(_ first:String, _ last:String) {
 38.     firstName = first
 39.     lastName = last
 40.   }
 41.   func fullName() -> String {
 42.     return "\(firstName) \(lastName)"
 43.   }
 44. }

 45> Person(first:"Alex", last:"Blewitt")
error: extraneous argument labels 'first:last:' in call
Person(first:"Alex", last:"Blewitt")
      ^~~~~~~        ~~~~~

 45> Person("Alex", "Blewitt")
$R5: Person = {
  firstName = "Alex"
  lastName = "Blewitt"
}

With Swift 1.1 (released as part of Xcode 6.1) the initializers can now be failable, implying that they can return nil if the arguments do not permit creation.

Failable initialzers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 46> class Person {
 47.   var firstName:String
 48.   var lastName:String
 49.   init?(_ first:String, _ last:String) {
 50.     firstName = first
 51.     lastName = last
 52.     if first == “” || last == “” {
 53.       return nil
 54.     }
 55.   }
 56.   func fullName() -> String {
 57.     return "\(firstName) \(lastName)"
 58.   }
 59. }

 60> Person(“”,””)
$R6: Person? = nil
 61> Person("Alex", "Blewitt")
$R7: Person? = {
  firstName = "Alex"
  lastName = "Blewitt"
}

Since the initializer may return nil the return type is treated as an Optional which means that it can be used in an if let construct or equivalent. Note that in Swift 1.1 there is an error message “all stored properties of a class instance must be initialized before returning nil from an initializer” which is a little bit crazy; it’s like saying that you have to be able to fully construct the object anyway even if the arguments don’t make sense. Hopefully that will be fixed in a future version.


We’ll go into more detail about classes in future episodes. In next week’s episode, we’ll look at how to create structs in Swift. To subscribe to this series, add the Swift tag feed to your reader.