Hire the top 3% of freelance Swift developers.

Toptal is a marketplace for top Swift developers, engineers, programmers, coders, architects, and consultants. Top companies and start-ups choose Toptal Swift freelancers for their mission critical software projects.
  • Trusted by:

Hire Freelance Swift Developers and Engineers

Jon Fabris, United States

Freelance Swift Developer at Toptal since July 2, 2014
Jon is a freelance front-end developer with extensive experience developing applications on desktop, web, and mobile platforms. His products include eLearning, web apps, games, and mobile (Android and iOS) applications, and his team experience includes architecture a... Click to continue

Marco Mustapic, Belgium

Toptal Engineering Blog Author
Marco's work has been published on the Toptal Engineering Blog. Check out Marco's latest post.
Toptal Author
Freelance Swift Developer at Toptal since January 3, 2013
Marco is a Senior iOS developer specializing in game development with a particular affinity for coding conceptually original apps. He has coded extensively using Objective-C, and is proficient in designing architecture, algorithmic and performance problems, and slick... Click to continue

Antonio Bello, Poland

Toptal Global Mentor
Antonio is committed to mentoring new developers as a Toptal Global Mentor.
Global Mentor
Toptal Engineering Blog Author
Antonio's work has been published on the Toptal Engineering Blog. Check out Antonio's latest post.
Toptal Author
Freelance Swift Developer at Toptal since April 8, 2013
Antonio is an experienced, self-motivated, results-driven, flexible freelance professional who specializes in turning business ideas into software solutions. He covers the entire software development life cycle, from concept to final product, using the best technolog... Click to continue

Nick Kanellopoulos, Greece

Freelance Swift Developer at Toptal since November 21, 2013
Nick excels as a general problem solver in heterogeneous environments. His current focus is on native mobile apps for iOS. He feels comfortable architecting C# web services on Windows, programming an iPhone application on his Mac to consume these web services, or dev... Click to continue

The Vital Guide to Swift Interviewing

The Challenge

Swift has been diligently working on overtaking Objective-C since its introduction in 2014. As Objective-C’s successor to the iOS throne, it has all that a new generation programming language should have. It’s intuitive, interactive, safer, faster, more reliable, time saving, and free. Swift is designed to work with all Apple devices and to be fully compatible with Cocoa and Cocoa Touch frameworks.

Swift - One language to rule them all

Swift - One language to rule them all.

The original strategy was to make developers migrate to the new language on their own just by showcasing all its perks. However, the first version of Swift was rather unstable, buggy, and not fully compatible with Xcode.

The breaking point was in December 2015 when Apple’s strong preference for its newborn child was demonstrated by making it open source, and releasing the new big version with patches and updates. Swift 2 has been stable enough to be used safely in applications, which secured its place as the number one language of choice for iOS development.

Although Swift saw the light of day only two years ago, there is already a large community of developers who are considered to be experts on the topic. However, the difference between a developer and a great developer is their ability to adapt, learn, and keep pace with the rest of the world. Since Swift is facing a bright future, and there will certainly be many new updates coming each year, a great developer has to be on top of the evolutionary curve all the time. Finding such a talent to hire is not an easy task. So, this article offers a sampling of questions that are crucial to evaluating the breadth and depth of a candidate’s mastery of Swift.

Questions and Answers

Q: Describe Swift. What kind of a language is it? What are its main perks?

Swift is a compiling language whose source code is translated to machine code. It’s fully compatible with its predecessor Objective-C, and with Apple’s Cocoa framework. By open-sourcing the language, Apple made its vision clear that Swift is to be much more than only a language for mobile applications. New frameworks, like Perfect, are first attempts to make Swift a server side language too. The result will be that Swift developers and full-stack developers will have the power to create client and server side applications using the same language.

To mention some of the main advantages of Swift:

  • Optional Types, which make applications crash-resistant
  • Built-in error handling
  • Closures
  • Much faster compared to other languages
  • Type-safe language
  • Supports pattern matching

Q: Explain the main differences between classes and structures in Swift.

Classes and structures are the very basic building blocks for the majority of programming languages. However, there is a difference between them. To leverage them fully in Swift, we need to be aware of all the possibilities they offer. There are certain principles every developer should know and adhere to.

Let’s start with attributes that classes and structures have in common:

  • Both conform to protocols to standardize functionality to a particular purpose, the interface.
  • Both can extend their functionality to add additional methods without modifying the original class or structure.

However, more important than knowing what they have in common is to know how they differ:

  • Classes are reference types - they are not copied when they are assigned to a different property, nor when they are being passed to a function.
  • Structures are much faster. Class instances are allocated on the heap, while structure instances are on the stack.
  • Structures do not support inheritance.
  • Structures provide default initializers for all properties which don’t have a default value:
struct Person {
    let name: String

let patrik = Person(name: "Patrik")

let patrik2 = Person() // does not compile, name property needs a value

Q: Explain generics in Swift.

Complex applications require clean code without any duplication. Here, generics come in handy as they can help us to avoid unnecessary duplication. In general, generics are considered as one of the most sophisticated features of Swift, and they should be used as much as possible. They enable us to write classes and methods without specifying the types they use. Swift uses generics internally wherever it is possible.

Let’s have a look on one of the best example, arrays, as they can store any types. In our example, we will implement a simple data structure, Queue, to demonstrate how powerful and useful generics can be:

class Queue<T> {
    private var data = [T]()
    func enqueue(item: T) {
    func dequeue() -> T? {
        return data.count == 0 ? nil : data.removeFirst()

let q = Queue<Int>()

Here, we removed the need to create Queue for other types that we would use in the application later. Our Queue can contain any type, and we were able to define it in one place. It saved us a lot of time that would be otherwise spent on setting the Queue each time later.

Q: Explain the lazy keyword.

An initial value of the lazy stored properties is calculated only when the property is called for the first time. There are situations when the lazy properties come very handy to developers. Imagine that the property needs a significant time for initialization, like when instantiating an array of controllers for storyboards used for UIPageViewControllers.

class MyController: UIPageViewController {
    lazy var myControllers: [UIViewController] = {
        guard let storyboard = self.storyboard else { return [] }
        let controller1 = storyboard.instantiateViewControllerWithIdentifier("Controller1")
        let controller2 = storyboard.instantiateViewControllerWithIdentifier("Controller2")
        let controller3 = storyboard.instantiateViewControllerWithIdentifier("Controller3")
        return [controller1, controller1, controller1]
    override func viewDidLoad() {
        let _ = myControllers
        let _ = myControllers

In our example, we called myControllers two times in viewDidLoad (it is just for testing purposes, to see how the initialization works). Our lazy variable contains a print statement to demonstrate what’s happening under the hood. After calling two times let _ = myControllers, there is only one message in a console log. That’s because the second time lazy variable is already initialized, and there is no need to do it once again.

Q: Discuss how to pass a variable as a reference.

Before getting into the discussion, we need to mention that there are two types of variables: reference and value types. The difference between these two types is that by passing value type, the variable will create a copy of its data, and the reference type variable will just point to the original data in the memory.

In Swift, all class instances are reference types. A variable with a class object is just a pointer to the memory. The same applies for functions as parameters. Consider the example:

class A: CustomStringConvertible {
    var name = "Patrik"
    var description: String {
        return name

func changeName(object: A) {
    object.name = "Thomas"

let myObject = A()
print(myObject) // Thomas

Here, an instance myObject is passed to the changeName function, and there is no need to mark parameters as reference types. Instead, we simply need to know some rules about how they behave when used with different parameter types. In this case, the print(myObject) will write “Thomas”.

Structures, Arrays, Strings, and Dictionaries are value types. To achieve a similar effect, to pass a variable to a function as a reference type, we need to add inout keyword before the parameter’s declaration and ampersand before the variable. Let’s consider a similar example:

func changeFirstElement(inout input: [Int]) {
    input[0] = 9

var array = [1, 2, 3]
print(array) // [9, 2, 3]

Q: Explain what defer is, and when you should to use it.

Swift 2 introduced the new defer keyword which provides a block of code that will be executed when the outer block is going to be finished. We could say that defer block will be executed in the case when execution is leaving the current scope. Consider the following code, and try to answer what the output will be.

func write() {
    defer {  print("end") }
    print("start for loop")
    for i in 0...5 {

As we previously stated, the defer block will be executed at the end of the function even if it is declared in the first line of the write() function.

The output result will be:

start loop

Now, a little more complicated example:

func write() {
    defer {  print("end") }
    print("start loop")
    for i in 0...2 {
        defer { print("defer ", i) }

The output is:

start loop
defer  0
defer  1
defer  2

It’s worth noting that the defer statement in the “for loop scope” is executed after each iteration of the loop. That’s because defer’s parent scope is a loop scope.

Nesting defer block in if statements will do the same like in a for loop.

func write3(input: Int) {
    if input > 0 {
        print("the number is greater than zero")
        defer { print("defer positive") }
    print("input is ", input)

The output is:

the number is greater than zero
defer positive
input is  10
the number is greater than zero
defer positive
input is  10

Using defers can significantly help programmers to avoid duplication, but it is crucial to know defer’s behavior and when it’s executed. Simple print functions can verify whether the developer understands the issue.

Q: Why it is better to use higher order functions? Explain with examples.

Functions that take another function as a parameter, or return a function, as a result, are known as higher-order functions. Swift defines these functions as CollectionType. The very basic higher order function is a filter. It’s a function on the array which accepts another function that will be used to return a new filtered version of the array. The main purpose of the higher functions is to help us write code with fewer bugs and in less time. At first glance, these functions might be scary, but a good developer should use them as much as possible.

The fact that we can take one function and put it into another function allows us to compose a lot of small functions into bigger functions. If we are aware of that, the readability of the code will be much higher.

Let’s assume that we have an array of numbers. How can we select only those that are bigger than zero? A very simple approach is:

let numbers = [-10, 4, -3, 5]
var positive = [Int]()
for n in numbers {
    if n > 0 {
print(positive) // [4, 5]

However, wouldn’t it be better to write less code and in a more elegant way?

let numbers = [-10, 4, -3, 5]
let positive = numbers.filter { (value: Int) -> Bool in
    return value > 0
print(positive) // [4, 5]

In the code example above, filter function will loop over each value, and for each value, it will pass it to a callback function as the current value. A callback function will then return a Boolean value to determine whether this value should be in the output array or not.

Swift enables us to use a shorter version of closures by inferring the types of its parameters and the type of the value it returns. As a result, there is no need to write a full declaration when the function is directly passed as a parameter.

let numbers = [-10, 4, -3, 5]
let positive = numbers.filter { $0 > 0 }
print(positive) // [4, 5]

Swift also provides the map function which returns a new array containing of all the new mapped values and only with one line of code.

let numbers = [-10, 4, -3, 5]
let strings = numbers.map { String($0) }
print(strings) // ["-10", "4", "-3", "5"]

To sum all values in an array, there is a reduce higher order function to help us achieve it.

let numbers = [-10, 4, -3, 5]
let sum = numbers.reduce(0) { $0 + $1 }
print(sum) // -4

Q: What are the best practices to group application settings to make the project as clean as possible?

Every project includes some custom settings, like API keys, names of NSUserDefault keys, or a palette of used colors in the application. Small projects do not require any precise rules for this to work properly. We can simply use something like the following:

label.textColor = UIColor(red: 34/255, green: 36.0/255, blue: 38/255, alpha: 1)
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isTutorialShown")

However, projects tend to grow, the design of the application will change, and we need a better system to group ‘’shared” data in one place. We need a way to change just one line of the code, like the application color, and have it propagate properly to the whole project.

There are multiple approaches to solving this problem. Let’s examine one possible solution:

struct Color {
    static let appColor = UIColor(red: 34/255, green: 36/255, blue: 38/255, alpha: 1)
    static let greyColor = UIColor(red: 131.8/255, green: 131.8/255, blue: 131.8/255, alpha: 1.0)

struct Settings {
    struct NSUserDefaults {
        static let isTutorialShown = "isTutorialShown"
    struct APIKey {
        static let fabric = "xxx"

By wrapping similar properties together, we can significantly reduce bugs, like using the same key in NSUserDefaults for two various properties. Additionally, this way provides us a spell check if we wrote the key correctly. Also, if we decide to remove some key or color, removing one line will inform us which files used that deprecated key. There are so many benefits for using all the keys in one place.

However, we know that creating Color or Settings struct in a global scope level is not the best approach, as it is like a global variable, which is a bad practice. We should put these settings into a wrapper, which is then directly connected with a type of item.

extension UIColor {
    struct MyProject {
        static let appColor = UIColor(red: 34/255, green: 36/255, blue: 38/255, alpha: 1)
        static let greyColor = UIColor(red: 130/255, green: 130/255, blue: 130/255, alpha: 1)

extension NSUserDefaults {
    struct MyProject {
        static let isTutorialShown = "isTutorialShown"

The result of using the keys will be:

NSUserDefaults.standardUserDefaults().setBool(true, forKey: NSUserDefaults.MyProject.isTutorialShown)
label.textColor = UIColor.MyProject.appColor

Leveraging Swift extensions can give us a sophisticated way how to group all the necessary settings and make the design cleaner. Creating one separate file for all extensions is also a good idea, and then we will get design changes much faster than by going through each of the files and changing colors manually.

Wrap Up

Answering questions we covered in this article should be a piece of cake for any top level Swift developer. The truth is, the language itself is not difficult to learn and use. However, keep in mind that whether your developer is good or not will be revealed during the time when more and more new updates are released and new features incorporated. Swift’s mastery is about being able to understand correctly and apply new principles in the shortest period possible to keep your application’s functionality in the lead.

We hope you find the questions presented in this post to be a solid foundation in your quest for the top Swift developers. Finding such candidates is well worth the effort, as they will undoubtedly have a significant positive impact on your team’s productivity and results.

Hire Swift developers now
See also: Toptal’s growing, community-driven list of essential Swift interview questions.
Alvaro 1506e7

My team is going to personally help you find the best candidate to join your team.

Alvaro Oliveira
VP of Talent Operations