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 4 of 4 of this article. Click here to view the first page.

Question #3

Consider the following code that defines Pizza as a struct and Pizzeria as a protocol with an extension that includes a default implementation for makeMargherita():

struct Pizza {
  let ingredients: [String]
}

protocol Pizzeria {
  func makePizza(_ ingredients: [String]) -> Pizza
  func makeMargherita() -> Pizza
}

extension Pizzeria {
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "mozzarella"])
  }
}

You'll now define the restaurant Lombardi’s as follows:

struct Lombardis: Pizzeria {
  func makePizza(_ ingredients: [String]) -> Pizza {
    return Pizza(ingredients: ingredients)
  }

  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "basil", "mozzarella"])
  }
}

The following code creates two instances of Lombardi's. Which of the two will make a margherita with basil?

let lombardis1: Pizzeria = Lombardis()
let lombardis2: Lombardis = Lombardis()

lombardis1.makeMargherita()
lombardis2.makeMargherita()

[spoiler title="Answer"]

They both do. The Pizzeria protocol declares the makeMargherita() method and provides a default implementation. The Lombardis implementation overrides the default method. Since you declare the method in the protocol in both cases, you'll invoke the correct implementation at runtime.

What if the protocol doesn't declare the makeMargherita() method but the extension still provides a default implementation, like this?

protocol Pizzeria {
  func makePizza(_ ingredients: [String]) -> Pizza
}

extension Pizzeria {
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "mozzarella"])
  }
}

Here, only lombardis2 would make the pizza with basil, whereas lombardis1 would make a pizza without it, because it would use the method defined in the extension.

[/spoiler]

Question #4

The following code has a compile time error. Can you spot it and explain why it happens? What are some ways you could fix it?

struct Kitten {
}

func showKitten(kitten: Kitten?) {
  guard let k = kitten else {
    print("There is no kitten")
  }   
  print(k)
}

Hint: There are three ways to fix the error.

[spoiler title="Answer"]

The else block of a guard requires an exit path, either by using return, throwing an exception or calling a @noreturn. The easiest solution is to add a return statement.

func showKitten(kitten: Kitten?) {
  guard let k = kitten else {
    print("There is no kitten")
    return
  }
  print(k)
}

Here's a version that throws an exception.

enum KittenError: Error {
  case NoKitten
}

struct Kitten {
}

func showKitten(kitten: Kitten?) throws {
  guard let k = kitten else {
    print("There is no kitten")
    throw KittenError.NoKitten
  }
  print(k)
}

try showKitten(kitten: nil)

Finally, here's an implementation calling fatalError(), which is a @noreturn function.

struct Kitten {
}

func showKitten(kitten: Kitten?) {
  guard let k = kitten else {
    print("There is no kitten")
    fatalError()
  }
  print(k)
}

[/spoiler]

Advanced Verbal Questions

Question #1

Are closures value or reference types?

[spoiler title="Answer"]

Closures are reference types. If you assign a closure to a variable and you copy the variable into another variable, you also copy a reference to the same closure and its capture list.

[/spoiler]

Question #2

You use the UInt type to store unsigned integers. It implements the following initializer to convert from a signed integer:

init(_ value: Int)

However, the following code generates a compile time error exception if you provide a negative value:

let myNegative = UInt(-1)

An unsigned integer by definition cannot be negative. However, it's possible to use the memory representation of a negative number to translate to an unsigned integer. How can you convert an Int negative number into an UInt while keeping its memory representation?

[spoiler title="Answer"]

There's an initializer for that:

UInt(bitPattern: Int)

making the implementation:

let myNegative = UInt(bitPattern: -1)

[/spoiler]

Question #3

Can you describe a circular reference in Swift? How can you solve it?

[spoiler title="Answer"]

A circular reference happens when two instances hold a strong reference to each other, causing a memory leak because neither of the two instances will ever be deallocated. The reason is that you cannot deallocate an instance as long as there's a strong reference to it, but each instance keeps the other alive because of its strong reference.

You'd solve the problem by breaking the strong circular reference by replacing one of the strong references with a weak or an unowned reference.

[/spoiler]

Question #4

Swift allows the creation of recursive enumerations. Here's an example of such an enumeration with a Node case that takes two associated value types, T and List:

enum List<T> {
  case node(T, List<T>)
}

This returns a compilation error. What is the missing keyword?

[spoiler title="Answer"]

It's the indirect keyword that allows for recursive enumeration cases like this:

enum List<T> {
  indirect case node(T, List<T>)
}

[/spoiler]

Where to Go From Here?

Congratulations on making it to the end of this Q&A, and don't feel bad if you didn't know all the answers!

Some of these questions are pretty complicated. Swift is a rich, expressive language and there's a lot to learn about it. Apple keeps improving — and changing — it so even the most experienced developers don't know everything.

To get to know Swift or build upon what you already know, be sure to check out Swift Apprentice.

The ultimate resource for all aspects of Swift is the official documentation, The Swift Programming Language, by Apple.

At the end of the day, using a language is the best way to learn it. Play with Swift in a playground or adopt it in a real project. Swift works (almost) seamlessly with Objective-C, so adding it to an existing project you already know is an excellent way to learn its ins and outs.

Thanks for visiting and working through these questions! Feel free to chime in below with your questions, problems and discoveries. I wouldn't mind if you wanted to pose some of your own challenges, too. See you in the forums!