What Learning Swift Looks Like: Structs v Classes

For part three of “What Learning Swift Looks Like” I turn my attention from arrays and static typeing into the world of structs and classes.

Really early on we all must ask ourselves struct or class? Both are general purpose data structures that allow for properties and methods. There are some important differences, but initial advice is easy: start with a struct and move to a class when you aren’t able to do what you need. It's pretty vague, but worked well when defining my data structures.

Let's get the basics of defining and creating struct and class types out the way.

class MyClass {  
  var x: Int = 1
  var y: String?
}
var x = MyClass()  
$ { x 1 nil }

Looks like we can create an instance of a class (or struct) without setting any values if each property is optional or has a default value.

class MyClass {  
  var x: Int = 1
  var y: String?
  init(x: Int, y: String) {
    self.x = x
    self.y = y
  }
}
let y = MyClass(x: 2, y: “String”)  
let x = MyClass()  
$ Missing argument for parameter ‘x’ in call

But if you define any custom constructor that automatic init method is removed so now you can’t use defaults unless you use a convenience constructor which requires you to use another existing constructor.

class MyClass {  
  var x: Int = 1
  var y: String?
  convenience init(x: Int, y: String) {
    self.init()
    self.x = x
    self.y = y
  }
}
let y = MyClass(x: 2, y: “String”)  
$ {x 2 {Some “String”}}
let x = MyClass()  
$ { x 1 nil }

Or you could just add in a default empty constructor again..

class MyClass {  
  var x: Int = 1
  var y: String?
    init() {}
  init(x: Int, y: String) {
    self.x = x
    self.y = y
  }
}
let y = MyClass(x: 2, y: “String”)  
$ {x 2 {Some “String”}}
let x = MyClass()  
$ { x 1 nil }

This is the explicitly manual way of writing the convenience constructor code above, and will be required if you needed a non-convenience constructor anyway.

In nontrivial models I often want to pass through some of the optional parameters, but not all of them because the defaults are totally cool. You can do this by defining a huge number number of constructors… or you may want to spend as little time possible creating boiler plate and use some tricks:

class MyClass {  
  var x: Int = 1
  var y: String?
  var z: Bool
  init(x: Int = 1, y: String? = nil, z: Bool = true) {
    self.x = x
    self.y = y
    self.z = z
  }
}
let y = MyClass(z: false, y: “Something”)  
$ {x 1 {Some “Something”} z false}

By providing a default value for each parameter they all become optional! The order doesn’t matter either, even better.

The more code I wrote the more I found myself relying on immutable properties inside of structs to reduce code errors. This became especially important when communicating data with a JSON API for the project. Everything that comes down should be immutable, as it’s not your data.

struct APIType {  
  let x: Int
  let y: String
  init(x: Int, y: String) {
    self.x = x
    self.y = y
  }
}

Using immutable values within the API type is a great approach that will ensure you can’t accidentally change server provided data while it’s in your system, throwing out synchronisation.

Well that is, until you need mean to update a value and send it to the server and you’re forced to create a new instance manually with 20 properties you need to manually set from an existing instance. Ugh. So, let’s fix that…

extension APIType {  
  init(existing: APIType, x: Int? = nil, y: String? = nil) {
    self.init(x: x ?? existing.x, 
              y: y ?? existing.y)
  }
}

Now we have a new constructor that takes an instance of itself as well as an optional variable for each property. If any of those optional properties are provided they are used, otherwise we use the existing value for it.

let x = APIType(x: 1, y: “String”)  
let y = APIType(existing: x, y: “Another one”)  
$ { x 1, y “Another one” }

Sweet!

Late into the project I was subclassing a type that had an immutable set of numbers (configuration basically) as a property which Swift does not like

class P {  
  var Numbers = [1,2,3,4]
}
class C {  
  var Numbers = [100, 200, 300, 400]
}
$ Cannot override with a stored property ‘Numbers’

Which is pretty frustrating because it would allow some important extensibility that I needed within a subclass and applying that in a constructor becomes pretty nasty when it should be immutable and inherit to all instances the same. Luckily you can work around this with some useful trickery:

class Parent {  
  var Numbers: [Int] {
    return [1,2,3,4]
  }
}
class Child: Parent {  
  override var Numbers: [Int] {
    return [100,200,300,400]
  }
}

By using a computed property you can override the value within a subclass to get the functionality that I needed. Even better is that by not providing a set method it becomes immutable and safe too.

let c = Child()  
c.Numbers = [1,2,3,4]  
$ Cannot assign to ‘Numbers’ in ‘c’

With that little trick I’ve reached the end of my Playground. I’m still using Playgrounds as a fast way of running small pieces of code so I don’t need to compile a large application each time. It’s only a matter of time until the next part of this series comes out.

What Learning Swift Looks Like: Typing

Continuing on from part one I started to get a lot deeper into immutability and static type system, especially when involving structs and classes.

I’d argue that a large part of why Swift comes across being modern and ‘script like’ to read is due to type inference. This system allows Swift programs to define variables without being explicit about the type.

let an_int = 10  
let a_double = 10.0  
let a_string = “This is a string”  

Even though I haven’t specified the type explicitly (let an_int: Int = 10) the compiler absolutely does. This can lead to some really confusing errors if you’re just starting with statically typed languages:

let velocity: CGFloat = 234  
let distance = 1223.00  
let duration = distance / velocity  
$ Binary operator ‘/‘cannot be applied to operands of type ‘Double’ and ‘CGFloat’

Type inference will declare distance as a Double which is not compatible with a CGFloat

let velocity: CGFloat = 234  
let duration = 1223.00 / velocity  
$ 5.226495726495727

In this example the compiler will ensure that duration is a CGFloat because it infers that you can only divide by velocity if 1223.00 is also a CGFloat. You can even do let duration = velocity / 1223 without the decimal and get the same results.

You could also typecast to make the first example work.

let velocity: CGFloat = 234.00  
let distance = 1223.00  
let duration = distance / Double(velocity)  
$ 5.226495726495727

This would make sense if you didn’t define the initial types yourself (such as a return from an existing function), but in this trivial example you should have just typed them correctly in the first place…

You can also create your own type aliases which simply allows you to give an existing type a new name

typealias MyType = Double  
let velocity: MyType = 234  
let distance: Double = 1223  
let duration = distance / velocity  

This is super useful in cases where you want complex types to be named for clean code. You’ll see this a lot with dictionary types:

typealias Response = [String: AnyObject]  
let response: Response = [ “id”: 1, “is_valid”: true ]  

This type alias makes code surrounding web responses much clearer than seeing [String: AnyObject] throughout your codebase.

Super useful tip: You can quickly debug the type of any variable by using toString(the_variable.dynamicType) which will print out the type as a string. e.g. toString(10.dynamicType) will be ”Swift.Int”.

So how does immutability effect struct and class structures?

struct YourFace {  
  var pretty = false
}
let x = YourFace()  
let y = YourFace()  
x = y  
$ Cannot assign to ‘let’ value ‘x’

Okay that’s fair..

x.pretty = true  
x  
$ { pretty true }

This shows that each variable defines it’s own mutable state. x is immutable but pretty is mutable, thus you can change it regardless of the mutability of the instance.

let x = YourFace()  
let y = YourFace()  
let a = [x, y]  
var b = a.reverse()  

In this case we’re using one of the many Array methods available which return a “copy” of an Array which is changed from the original.

I’m specifically looking for deep / shallow copy details.

b.pop()  
b  
$ [{pretty false}]
a  
$ [{pretty false}, {pretty false}]

Okay so they are copies, not references or slices so that’s useful. It turns out there was a lot of concern with this in early versions of Swift language in betas.

Next step is to check how the objects within the Array are copied over.

b[0].pretty = false  
a[1].pretty  
$ true

Deep copies, that’s good to know. So I’m assuming that functions are all pass by value, not pass by reference?

func makeItOppositeDay(faces: [YourFace]) -> [YourFace] {  
    return faces.map { face in
        YourFace(pretty: !face.pretty)
    }
}
let c = makeItOppositeDay(a)  
a  
$ [{pretty false}, {pretty false}]
c  
$ [{pretty true}, {pretty true}]

Yep, that makes sense. So passing a huge array to a function could be a pretty big hit I suppose.

I can say with confidence the hardest part of moving to Swift from dynamic languages has been the type system. It’s been a learning curve, but nothing to be scared of. I’m convinced that Dealing with these issues as you program from compiler failures is a lot better than dealing with them in a production system or that == v === mess…

Having said that I wish I never had to see another horrible compilation error like this again…

func makeItOppositeDay(faces: [YourFace]) {  
    return faces.map { face in
        YourFace(pretty: !face.pretty)
    }
}
$ ‘Array<YourFace>’ is not convertible to ‘()’

Wat. This is a horribly useless way to explain that the function makeItOppositeDay has a return of Void and you’re attempting to return an Array of YourFace structs…