Swift Interview Questions and Answers

In this tutorial, you’ll work through a series of Swift-specific interview questions and answers. By Bill Morefield.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Question #2

Summarize the main differences between a structure and a class.

[spoiler title="Answer"]

You can summarize the differences as:

  • Classes support inheritance; structures don't.
  • Classes are reference types; structures are value types.

[/spoiler]

Question #3

What are generics and which problem do they solve?

[spoiler title="Answer"]

In Swift, you can use generics in both functions and data types, e.g. in classes, structures or enumerations.

Generics solve the problem of code duplication. When you have a method that takes one type of parameter, it's common to duplicate it to accommodate a parameter of a different type.

For example, in the following code the second function is a "clone" of the first, except it accepts strings instead of integers.

func areIntEqual(_ x: Int, _ y: Int) -> Bool {
  return x == y
}

func areStringsEqual(_ x: String, _ y: String) -> Bool {
  return x == y
}

areStringsEqual("ray", "ray") // true
areIntEqual(1, 1) // true

By adopting generics, you can combine the two functions into one and keep type safety at the same time. Here's the generic implementation:

func areTheyEqual<T: Equatable>(_ x: T, _ y: T) -> Bool {
  return x == y
}

areTheyEqual("ray", "ray")
areTheyEqual(1, 1)

Since you're testing equality in this case, you restrict the parameters to any type that implements the Equatable protocol. This code achieves the intended result and prevents passing parameters of a different type.

[/spoiler]

Question #4

In some cases, you can't avoid using implicitly unwrapped optionals. When? Why?

[spoiler title="Answer"]

The most common reasons to use implicitly unwrapped optionals are:

  1. When you cannot initialize a property that is not nil by nature at instantiation time. A typical example is an Interface Builder outlet, which always initializes after its owner. In this specific case — assuming it's properly configured in Interface Builder — you've guaranteed that the outlet is non-nil before you use it.
  2. To solve the strong reference cycle problem, which is when two instances refer to each other and require a non-nil reference to the other instance. In such a case, you mark one side of the reference as unowned, while the other uses an implicitly unwrapped optional.

[/spoiler]

Question #5

What are the various ways to unwrap an optional? How do they rate in terms of safety?

var x : String? = "Test"

Hint: There are seven ways.

[spoiler title="Answer"]

Forced unwrapping — unsafe.

let a: String = x!

Implicitly unwrapped variable declaration — unsafe in many cases.

var a = x!

Optional binding — safe.

if let a = x {
  print("x was successfully unwrapped and is = \(a)")
}

Optional chaining — safe.

let a = x?.count

Nil coalescing operator — safe.

let a = x ?? ""

Guard statement — safe.

guard let a = x else {
  return
}

Optional pattern — safe.

if case let a? = x {
  print(a)
}

[/spoiler]

Intermediate Written Questions

Now, to step up the difficulty a bit. Are you ready?

Question #1

What's the difference between nil and .none?

[spoiler title="Answer"]

There is no difference, as Optional.none (.none for short) and nil are equivalent.

In fact, this statement outputs true:

nil == .none

The use of nil is more common and is the recommended convention.

[/spoiler]

Question #2

Here's a model of a thermometer as a class and a struct. The compiler will complain about the last line. Why does it fail to compile?

Tip: Read the code carefully and think about it before testing it in a playground.
public class ThermometerClass {
  private(set) var temperature: Double = 0.0
  public func registerTemperature(_ temperature: Double) {
    self.temperature = temperature
  }
}

let thermometerClass = ThermometerClass()
thermometerClass.registerTemperature(56.0)

public struct ThermometerStruct {
  private(set) var temperature: Double = 0.0
  public mutating func registerTemperature(_ temperature: Double) {
    self.temperature = temperature
  }
}

let thermometerStruct = ThermometerStruct()
thermometerStruct.registerTemperature(56.0)

[spoiler title="Answer"]

The ThermometerStruct is correctly declared with a mutating function to change its internal variable temperature. The compiler complains because you've invoked registerTemperature on an instance created via let, which is therefore immutable. Change let to var to make the example compile.

With structures, you must mark methods that change the internal state as mutating, but you cannot invoke them from immutable variables.

[/spoiler]

Question #3

What will this code print and why?

var thing = "cars"

let closure = { [thing] in
  print("I love \(thing)")
}

thing = "airplanes"

closure()

[spoiler title="Answer"]

It'll print: I love cars. The capture list creates a copy of thing when you declare the closure. This means that captured value doesn't change even if you assign a new value to thing.

If you omit the capture list in the closure, then the compiler uses a reference instead of a copy. Therefore, when you invoke the closure, it reflects any change to the variable. You can see this in the following code:

var thing = "cars"

let closure = {    
  print("I love \(thing)")
}

thing = "airplanes"

closure() // Prints: "I love airplanes"

[/spoiler]

Question #4

Here's a global function that counts the number of unique values in an array:

func countUniques<T: Comparable>(_ array: Array<T>) -> Int {
  let sorted = array.sorted()
  let initial: (T?, Int) = (.none, 0)
  let reduced = sorted.reduce(initial) {
    ($1, $0.0 == $1 ? $0.1 : $0.1 + 1)
  }
  return reduced.1
}

It uses sorted, so it restricts T to types that conform to Comparable.

You call it like this:

countUniques([1, 2, 3, 3]) // result is 3

Rewrite this function as an extension method on Array so that you can write something like this:

[1, 2, 3, 3].countUniques() // should print 3

[spoiler title="Answer"]

You can rewrite the global countUniques(_:) as an Array extension:

extension Array where Element: Comparable {
  func countUniques() -> Int {
    let sortedValues = sorted()
    let initial: (Element?, Int) = (.none, 0)
    let reduced = sortedValues.reduce(initial) { 
      ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) 
    }
    return reduced.1
  }
}

Note that the new method is available only when the generic Element type conforms to Comparable.

[/spoiler]