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.