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

Question #5

Here's a function to divide two optional doubles. There are three preconditions to verify before performing the actual division:

  • The dividend must contain a non nil value.
  • The divisor must contain a non nil value.
  • The divisor must not be zero.
func divide(_ dividend: Double?, by divisor: Double?) -> Double? {
  if dividend == nil {
    return nil
  }
  if divisor == nil {
    return nil
  }
  if divisor == 0 {
    return nil
  }
  return dividend! / divisor!
}

Improve this function by using the guard statement and without using forced unwrapping.

[spoiler title="Answer"]

The guard statement introduced in Swift 2.0 provides an exit path when a condition is not met. It's very helpful when checking preconditions because it lets you express them in a clear way — without the pyramid of doom of nested if statements. Here is an example:

guard dividend != nil else { return nil }

You can also use the guard statement for optional binding, which makes the unwrapped variable accessible after the guard statement:

guard let dividend = dividend else { return .none }

So you can rewrite the divide function as:

func divide(_ dividend: Double?, by divisor: Double?) -> Double? {
  guard let dividend = dividend else { return nil }
  guard let divisor = divisor else { return nil }
  guard divisor != 0 else { return nil }
  return dividend / divisor
}

Notice the absence of the implicitly unwrapped operators on the last line because you've unwrapped both dividend and divisor and stored them in non-optional immutable variables.

Note that the results of the unwrapped optionals in a guard statement are available for the rest of the code block that the statement appears in.

You can can simplify this further by grouping the guard statements:

func divide(_ dividend: Double?, by divisor: Double?) -> Double? {
  guard 
    let dividend = dividend,
    let divisor = divisor,
    divisor != 0 
    else { 
      return nil 
    }
  return dividend / divisor
}

[/spoiler]

Question #6

Rewrite the method from question five using an if let statement.

[spoiler title="Answer"]

The if let statement lets you unwrap optionals and use the value within that code block. Note that you cannot access the unwrapped optional outside the block. You can write the function using an if let statement such as:

func divide(_ dividend: Double?, by divisor: Double?) -> Double? {
  if 
    let dividend = dividend,
    let divisor = divisor,
    divisor != 0 {
      return dividend / divisor
  } else {
    return nil
  }
}

[/spoiler]

Intermediate Verbal Questions

Question #1

In Objective-C, you declare a constant like this:

const int number = 0;

Here is the Swift counterpart:

let number = 0

What are the differences between them?

[spoiler title="Answer"]

A const is a variable initialized at compile time with a value or an expression that must be resolved at compilation time.

An immutable created with let is a constant determined at runtime. You can initialize it with a static or a dynamic expression. This allows a declaration such as:

let higherNumber = number + 5

Note that you can only assign its value once.

[/spoiler]

Question #2

To declare a static property or function, you use the static modifier on value types. Here's an example for a structure:

struct Sun {
  static func illuminate() {}
}

For classes, it's possible to use either the static or the class modifier. They achieve the same goal, but in different ways. Can you explain how they differ?

[spoiler title="Answer"]

static makes a property or a function static and not overridable. Using class lets you override the property or function.

When applied to classes, static becomes an alias for class final.

For example, in this code the compiler will complain when you try to override illuminate():

class Star {
  class func spin() {}
  static func illuminate() {}
}
class Sun : Star {
  override class func spin() {
    super.spin()
  }
  // error: class method overrides a 'final' class method
  override static func illuminate() { 
    super.illuminate()
  }
}

[/spoiler]

Question #3

Can you add a stored property to a type by using an extension? How or why not?

[spoiler title="Answer"]

No, it's not possible. You can use an extension to add new behavior to an existing type, but not to alter either the type itself or its interface. If you add a stored property, you'd need extra memory to store the new value. An extension cannot manage such a task.

[/spoiler]

Question #4

What is a protocol in Swift?

[spoiler title="Answer"]

A protocol is a type that defines a blueprint of methods, properties and other requirements. A class, structure or enumeration can then adopt the protocol to implement those requirements.

A type that adopts the requirements of a protocol conforms to that protocol. The protocol doesn't implement any functionality itself, but rather defines the functionality. You can extend a protocol to provide a default implementation of some of the requirements or additional functionality that conforming types can take advantage of.

[/spoiler]

Advanced Written Questions

Question #1

Consider the following structure that models a thermometer:

public struct Thermometer {
  public var temperature: Double
  public init(temperature: Double) {
    self.temperature = temperature
  }
}

To create an instance, you can use this code:

var t: Thermometer = Thermometer(temperature:56.8)

But it would be nicer to initialize it this way:

var thermometer: Thermometer = 56.8

Can you? How?

[spoiler title="Answer"]

Swift defines protocols that enable you to initialize a type with literal values by using the assignment operator. Adopting the corresponding protocol and providing a public initializer allows literal initialization of a specific type. In the case of Thermometer, you implement ExpressibleByFloatLiteral as follows:

extension Thermometer: ExpressibleByFloatLiteral {
  public init(floatLiteral value: FloatLiteralType) {
    self.init(temperature: value)
  }
}

Now, you can create an instance by using a float.

var thermometer: Thermometer = 56.8

[/spoiler]

Question #2

Swift has a set of pre-defined operators to perform arithmetic or logic operations. It also allows the creation of custom operators, either unary or binary.

Define and implement a custom ^^ power operator with the following specifications:

  • Takes two Ints as parameters.
  • Returns the first parameter raised to the power of the second.
  • Correctly evaluates the equation using the standard algebraic order of operations.
  • Ignores the potential for overflow errors.

[spoiler title="Answer"]

You create a new custom operator in two steps: Declaration and implementation.

The declaration uses the operator keyword to specify the type (unary or binary), the sequence of characters composing the operator, its associativity and precedence. Swift 3.0 changed the implementation of precedence to use a precedence group.

Here, the operator is ^^ and the type is infix (binary). Associativity is right; in other words, equal precedence ^^ operators should evaluate the equation from right to left.

There is no predefined standard precedence for exponential operations in Swift. In the standard order of operations for algebra, exponents should calculate before multiplication/division. So you'll need to create a custom precedence that places them higher than multiplication.

Here's the declaration:

precedencegroup ExponentPrecedence {
  higherThan: MultiplicationPrecedence
  associativity: right
}
infix operator ^^: ExponentPrecedence

The implementation follows:

func ^^(base: Int, exponent: Int) -> Int {
  let l = Double(base)
  let r = Double(exponent)
  let p = pow(l, r)
  return Int(p)
}

Note that since the code doesn't take overflows into account, if the operation produces a result that Int can't represent, such as a value greater than Int.max, then a runtime error occurs.

[/spoiler]