Swift Interview Questions and Answers
In this tutorial, you’ll work through a series of Swift-specific interview questions and answers. By Bill Morefield.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Swift Interview Questions and Answers
30 mins
- Beginner Written Questions
- Question #1
- Question #2
- Question #3
- Question #4
- Beginner Verbal Questions
- Question #1
- Question #2
- Question #3
- Question #4
- Question #5
- Intermediate Written Questions
- Question #1
- Question #2
- Question #3
- Question #4
- Question #5
- Question #6
- Intermediate Verbal Questions
- Question #1
- Question #2
- Question #3
- Question #4
- Advanced Written Questions
- Question #1
- Question #2
- Question #3
- Question #4
- Advanced Verbal Questions
- Question #1
- Question #2
- Question #3
- Question #4
- Where to Go From Here?
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:
- 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. - 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?
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]