Main Swift Coding Challenges

Swift Coding Challenges

0 / 0
How much do you like this book?
What’s the quality of the file?
Download the book for quality assessment
What’s the quality of the downloaded files?
Year:
2018
Edition:
1st Edition
Language:
english
Pages:
300
Series:
Hacking With Swift
File:
PDF, 7.11 MB
Download (pdf, 7.11 MB)
Conversion to is in progress
Conversion to is failed

Most frequently terms

 
0 comments
 

To post a review, please sign in or sign up
You can write a book review and share your experiences. Other readers will always be interested in your opinion of the books you've read. Whether you've loved the book or not, if you give your honest and detailed thoughts then people will find new books that are right for them.
Paul Hudson

CODING CHALLENGES
SWIFT

CODING CHALLENGES
SWIFT

HACKING WITH SWIFT

REAL PROBLEMS, REAL SOLUTIONS

Prepare for iOS interviews,
test yourself against friends,
and level up your skills.



Contents
Introduction 4

Welcome

Strings 11
Challenge 1: Are the letters unique?
Challenge 2: Is a string a palindrome?
Challenge 3: Do two strings contain the same characters?
Challenge 4: Does one string contain another?
Challenge 5: Count the characters
Challenge 6: Remove duplicate letters from a string
Challenge 7: Condense whitespace
Challenge 8: String is rotated
Challenge 9: Find pangrams
Challenge 10: Vowels and consonants
Challenge 11: Three different letters
Challenge 12: Find longest prefix
Challenge 13: Run-length encoding
Challenge 14: String permutations
Challenge 15: Reverse the words in a string

Numbers 54
Challenge 16: Fizz Buzz
Challenge 17: Generate a random number in a range
Challenge 18: Recreate the pow() function
Challenge 19: Swap two numbers
Challenge 20: Number is prime
Challenge 21: Counting binary ones
Challenge 22: Binary reverse
Challenge 23: Integer disguised as string
Challenge 24: Add numbers inside a string
Challenge 25: Calculate a square root by hand
Challenge 26: Subtract without subtract

Files 87
Challenge 27: Print last lines
Challenge 28: Log a message
Challenge 29: Documents directory

www.hackingwithswift.com 2



Challenge 30: New JPEGs
Challenge 31: Copy recursively
Challenge 32: Word frequency
Challenge 33: Find duplicate filenames
Challenge 34: Find executables
Challenge 35: Convert images
Challenge 36: Print error lines

Collections 119
Challenge 37: Count the numbers
Challenge 38: Find N smallest
Challenge 39: Sort a string array by length
Challenge 40: Missing numbers in array
Challenge 41: Find the median
Challenge 42: Recreate index(of:)
Challenge 43: Linked lists
Challenge 44: Linked list mid-point
Challenge 45: Traversing the tree
Challenge 46: Recreate map()
Challenge 47: Recreate min()
Challenge 48: Implement a deque data structure
Challenge 49: Sum ; the even repeats
Challenge 50: Count the largest range
Challenge 51: Reversing linked lists
Challenge 52: Sum an array of numbers
Challenge 53: Linked lists with a loop
Challenge 54: Binary search trees

Algorithms 185
Challenge 55: Bubble sort
Challenge 56: Insertion sort
Challenge 57: Isomorphic values
Challenge 58: Balanced brackets
Challenge 59: Quicksort
Challenge 60: Tic-Tac-Toe winner
Challenge 61: Find prime numbers
Challenge 62: Points to angles
Challenge 63: Flood fill
Challenge 64: N Queens

Appendix: Be the Compiler 236

www.hackingwithswift.com 3



Introduction
About this book

www.hackingwithswift.com 4



Welcome
This is not your average book about Swift coding. This is a book where I expect you to do 
most of the work. Sure, I’ll be helping you along with hints, and I’ll also be providing my own 
solutions and explanations along the way, but if you haven’t already put the work in then 
you’ll be missing out.

You see, this book is called Swift Coding Challenges because I really want to challenge you. 
There is no learning without struggle, so if you don’t take the time to read each challenge and 
try it for yourself in Xcode, you’ll never know how you would have fared.

So, please follow these instructions to the letter if you want to reap the full benefit of what this 
book offers. It’s not a cheatsheet, a guide book, or even a tutorial like I would normally write. 
Instead, this is a book designed to make you think, to make you work, and to make you learn 
by doing.

Note: this book is not for Swift beginners, so you should have at least six months of Swift 
behind you. If you’ve completed Hacking with Swift you ought to be able to handle all the 
Easy and some of the Tricky problems. If you’ve completed Pro Swift you ought to be able to 
handle most of the Taxing problems too. If you consistently struggle with challenges, or if 
you’ve never seen the rethrows keyword before, you should definitely watch my Pro Swift 
videos.

Where I thought it was useful, I have used Big O notation to describe the computational 
complexity of an algorithm. The order of a function (the “O”) tells you how fast it grows, and 
provides a worst-case scenario of how much work must be done to run some code. An O(n) 
function means “the function takes as correspondingly longer as you add items.” So, if it takes 
one second with one item, it would take 10 seconds with ten items. In comparison, an O(1) 
function runs in constant time, which means a function that takes 1 second with one item 
would take 1 second with 100 items.

How to use this book
I’ve organized this book into chapters so that it’s roughly grouped by different kinds of 
problem. There is, inevitably, some crossover between chapters, but I’ve tried to place things 
where I thought best. Inside each chapter are individual challenges sorted by difficulty, so you 

www.hackingwithswift.com 5



ought to be able to flip to a chapter that interests you and just start working your way through 
from the beginning.

Each challenge is broken down into three parts. First, I state the problem as clearly as possible 
in one sentence. When necessary I’ll provide some extra clarification. When working with 
strings, I’ve made it clear whether you should ignore letter case (so “CAT” is equal to “cat”) or 
whether you should take letter case into account (so “CAT” is not equal to “cat”).

Second, I provide some example input and output so you should be able to write test cases to 
validate that your code is correct. I’m not saying that you need to use test-driven development, 
but I would suggest that if you’re going to a job interview TDD is a good skill to be able to 
show.

After these two steps, I encourage you to fire up Xcode and start coding. How long it takes to 
solve each challenge depends on your skill level, but I would suggest the following as a rough 
guide:

1. Novice developers, i.e. under one year of coding experience, Swift or otherwise: 15 
minutes for an Easy challenge, 30 minutes for a Tricky challenge, and 1 hour for a 
Taxing challenge.

2. Intermediate developers, i.e. under three years of coding experience: 10 minutes for an 
Easy challenge, 20 minutes for a Tricky challenge, and 30 minutes for a Taxing 
challenge.

3. Senior developers, i.e. five years or more of coding experience: 5 minutes for an Easy 
challenge, 10 minutes for a Tricky challenge, and 15 minutes for a Taxing challenge.

If you fall somewhere between those groups, I’m sure you’re smart enough to extrapolate a 
rough goal for yourself. If you’re way beyond five years of experience then I would expect the 
times might come down further – perhaps as low as five minutes for a taxing challenge if 
you’re confident.

Obvious warning: the groupings are very broad, so don’t worry if you go over the suggested 
times. I personally would count writing tests and commenting your code in those times, but 
that’s down to you.

www.hackingwithswift.com 6



As you work, you might find you hit problems – and that’s OK. Remember the whole “no 
learning without struggle” thing? If you hit a brick wall and you’re not sure how to continue, 
every challenge comes with hints from me to try to point you in the right direction. You should 
read these only if you’ve tried and failed to complete the solution, and even then read only one 
hint at a time – you might find just reading the first one is enough to help you advance.

Once you have completed the challenge – which might be when you’ve written a solution that 
passes your tests, or might be when your self-allotted time target has lapsed – then it’s time for 
you to read my solution. I’ve tried to make sure every solution is as clear as possible, so 
sometimes I’ve used three lines of code rather than one to allow me to add more comments or 
discussion. Remember, interview environments are stressful enough without you striving for 
the perfect answer to a question – get something that works then improve on it, rather than 
trying to get it into a magic one liner in your first pass!

Many challenges come with more than one solution. This is sometimes to help you compare 
performance characteristics, sometimes because I can never resist the opportunity to teach new 
things, but sometimes also because I want to encourage you to be open-minded – there are lots 
of ways of making things work, and I hope you can appreciate the variety!

Passing a challenge
I’m afraid there’s no certificate when you complete all the challenges, but if you tweet me 
@twostraws I’ll high five you over the internet.

Since I’m not on hand to mark your answers, it’s down to you to self-regulate – you’re only 
cheating yourself, after all. I suggest you write tests for your challenges; something like this 
ought to work, replacing the “X” with your current challenge number:

func yourFunctionName(input: String) -> Bool {
   return true
}

assert(yourFunctionName(input: "Hacking with Swift") == true, 
"Challenge X did not return true")

www.hackingwithswift.com 7



I frequently use challengeX() for the names of my functions and methods, but only 
because it makes it easier for you to remember where they came from if you copy them into 
your own playgrounds.

You should be able to complete most of the challenges in a Swift playground. The Files 
chapter requires you to work with external files on your computer, so for those challenges you 
should use a macOS Command Line Tool project.

Note: sometimes it’s possible you’ll fly through a challenge graded as taxing, or struggle with 
a problem that’s graded easy. When this happens, please don’t send me angry emails:

1. Things that are easy for you aren’t always easy for others, and things that are hard for 
you aren’t always hard for others.

2. The nature of strings make them easier to work with than collections, so you’ll find 
many more easy problems with strings than with collections.

3. What seems easy over a glass of wine on a Sunday evening can seem insurmountable 
when faced with three interviewers staring at you as you pick up a whiteboard pen!

Often I refer to a solution as being naïve. This is not an insult! If your solution is the same as 
my naïve solution, it means you totally solved the problem and deserve a pat on the back. It 
does, however, mean that there are more efficient or cleaner solutions available, and I hope 
everyone will learn at least a little bit while reading this book.

A note on algorithms
Some of you reading this won’t have had formal computer science education, and perhaps 
might not have had the chance to fill any gaps on the job. If this is you, it’s very likely you 
might find the algorithms chapter particularly difficult – if you can’t tell a bubble sort from an 
insertion sort, then it’s going to be hard to answer interview questions about them.

Take heart! I do my best to explain algorithms in particular detail, and hope my explanations 
will help you understand how they work. If you’re able to try these challenges that’s awesome, 
but don’t feel discouraged if you struggle and end up relying on my solutions particularly 
heavily. We all have things we don’t know – even me.

www.hackingwithswift.com 8



Frequent Flyer Club
You can buy Swift tutorials from anywhere, but I'm pleased, proud, and very grateful that you 
chose mine. I want to say thank you, and the best way I have of doing that is by giving you 
bonus content above and beyond what you paid for – you deserve it!

Every book contains a word that unlocks bonus content for Frequent Flyer Club members. The 
word for this book is COBALT. Enter that word, along with words from any other Hacking 
with Swift books, here: https://www.hackingwithswift.com/frequent-flyer

Dedication
This book is dedicated to Taylor Smith, who frequently reads my Taylor Swift-inspired coding 
examples and imagines they are about him. Well, this time it really is about you:

import Foundation

let TAYLOR = { (t: Int) in return -(~t) }
let tayloR = { return TAYLOR(TAYLOR($0)) }
let taylOr = { return tayloR(tayloR($0)) }
let tayLor = { return taylOr(taylOr($0)) }
let taYlor = { return tayLor(tayLor($0)) }
let tAylor = { return taYlor(taYlor($0)) }
let Taylor = { return tAylor(tAylor($0)) }
let taylor = { (t: Int) -> Int in print(UnicodeScalar(t)!, 
terminator: ""); return 0 }

taylor (tayloR (tayLor (taylor (TAYLOR (tAylor (taylor (taYlor 
(tAylor (taylor (tayloR (taYlor (tAylor (taylor (taylOr (tAylor 
(taylor (tAylor (taylor (TAYLOR (taylOr (tAylor (Taylor (taylor 
(TAYLOR (taylOr (tayLor (tAylor (Taylor (taylor (tAylor (taylor 
(TAYLOR (taylOr (tAylor (Taylor (taylor (TAYLOR (tayloR (taylOr 
(taYlor (tAylor (Taylor (taylor (TAYLOR (tayloR (taylOr (tayLor 
(tAylor (Taylor (taylor (tAylor (taylor (TAYLOR (taylOr (taYlor 
(tAylor (Taylor (taylor (TAYLOR (tayloR (taylOr (tayLor (tAylor 

www.hackingwithswift.com 9



(Taylor (taylor (TAYLOR (tayLor (taYlor (Taylor 
(0) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) 
) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) 
) ) ) ) ) ) )

Copyright
Swift, the Swift logo, Xcode, Instruments, Cocoa Touch, Touch ID, AirDrop, iBeacon, iPhone, 
iPad, Safari, App Store, Mac and macOS are trademarks of Apple Inc., registered in the U.S. 
and other countries.

Swift Coding Challenges, Practical iOS 10, Pro Swift, and Hacking with Swift are copyright 
Paul Hudson. All rights reserved. No part of this book or corresponding materials (such as text, 
images, or source code) may be reproduced or distributed by any means without prior written 
permission of the copyright owner.

www.hackingwithswift.com 10



Chapter 1
Strings

www.hackingwithswift.com 11



Challenge 1: Are the letters unique?
Difficulty: Easy

Write a function that accepts a String as its only parameter, and returns true if the string has 
only unique letters, taking letter case into account.

Sample input and output
• The string “No duplicates” should return true.
• The string “abcdefghijklmnopqrstuvwxyz” should return true.
• The string “AaBbCc” should return true because the challenge is case-sensitive.
• The string “Hello, world” should return false because of the double Ls and double Os.

For this initial challenge I’ll write some test cases for you, so that you have something to use in 
the future. These four assert() statements should all evaluate to true, and therefore not 
trigger an error:

assert(challenge1(input: "No duplicates") == true, "Challenge 1 
failed")
assert(challenge1(input: "abcdefghijklmnopqrstuvwxyz") == true, 
"Challenge 1 failed")
assert(challenge1(input: "AaBbCc") == true, "Challenge 1 
failed")
assert(challenge1(input: "Hello, world") == false, "Challenge 1 
failed")

Hints
Remember, read as few hints as you can to help you solve the challenge, and only read them if 
you’ve tried and failed. (This reminder won’t be repeated again.)

Hint #1: You should treat the input string like an array that contains Character elements.

Hint #2: You could use a temporary array to store characters that have been checked, but it’s 

www.hackingwithswift.com 12



not necessary.

Hint #3: Sets are like arrays, except they can’t contain duplicate elements.

Hint #4: You can create sets from arrays and arrays from sets. Both have a count property.

Solution
There are two ways to solve this, both of which are perfectly fine given our test cases. First, 
the brute force approach: create an array of checked characters, then loop through every letter 
in the input string and append the latter to the list of checked characters, returning false as soon 
as a call to contains() fails.

Here’s how that code would look:

func challenge1a(input: String) -> Bool {
   var usedLetters = [Character]()

   for letter in input {
      if usedLetters.contains(letter) {
         return false
      }

      usedLetters.append(letter)
   }

   return true
}

That solution is correct with the example input and output I provided, but you should be 
prepared to discuss that it doesn’t scale well: calling contains() on an array is an O(n) 
operation, which means it gets slower as more items are added to the array. If our text were in 
a language with very few duplicate characters, such as Chinese, this might cause performance 
issues.

www.hackingwithswift.com 13



issues.

The smart solution is to use Set, which can be created directly from the input string. Sets 
cannot contain duplicate items, so if we create a set from the input string then the count of the 
set will equal the count of the input if there are no duplicates.

In code you would write this:

func challenge1b(input: String) -> Bool {
   return Set(input).count == input.count
}

www.hackingwithswift.com 14



Challenge 2: Is a string a palindrome?
Difficulty: Easy

Write a function that accepts a String as its only parameter, and returns true if the string 
reads the same when reversed, ignoring case.

Sample input and output
• The string “rotator” should return true.
• The string “Rats live on no evil star” should return true.
• The string “Never odd or even” should return false; even though the letters are the 

same in reverse the spaces are in different places.
• The string “Hello, world” should return false because it reads “dlrow ,olleH” 

backwards.

Hints
Hint #1: You can reverse arrays using their reversed() method.

Hint #2: Two arrays compare as equal if they contain the same items in the same order. They 
are value types in Swift, so it doesn’t matter how they were created, as long as their values are 
the same.

Hint #3: String aren’t really arrays, but you can make them one using Array().

Hint #4: You need to ignore case, so consider forcing the string to either lowercase or 
uppercase before comparing.

Solution
This is one of the most common interview questions you’ll come across, and it has a particular 
quirk in Swift that might have caught you out: strings might look like arrays, but trying to 
index into them is hard.

www.hackingwithswift.com 15



index into them is hard.

Fortunately, you can create an array from it like this:

let characterArray = Array(input)

Once you figure that out, you’ll just need to use reversed() to reverse the string’s 
characters, then compare the reversed array with the character view-as-array, like this:

func challenge2(input: String) -> Bool {
   return input.reversed() == Array(input)
}

Remember, strings are value types in Swift, which means they compare as equal as long as 
their contents are identical - it doesn’t matter how they are created. As an analogy, we all know 
that 2 times 2 is equal to 2 + 2, even though the number 4 was created using different methods. 
The same is true of Swift’s string: even though one is reversed, the == operator just compares 
the current value.

Finally, make sure you remember that your comparison should ignore string case. This can be 
done with the lowercased() method on the input string, like this:

func challenge2(input: String) -> Bool {
   let lowercase = input.lowercased()
   return lowercase.reversed() == Array(lowercase)
}

Done!

www.hackingwithswift.com 16



Challenge 3: Do two strings contain the same 
characters?
Difficulty: Easy

Write a function that accepts two String parameters, and returns true if they contain the 
same characters in any order taking into account letter case.

Sample input and output
• The strings “abca” and “abca” should return true.
• The strings “abc” and “cba” should return true.
• The strings “ a1 b2 ” and “b1 a2” should return true.
• The strings “abc” and “abca” should return false.
• The strings “abc” and “Abc” should return false.
• The strings “abc” and “cbAa” should return false.

Hints
Hint #1: This task requires you to handle duplicate characters.

Hint #2: The naive way to check this is to loop over the characters in one and check it exists in 
the other, removing matches as you go.

Hint #3: A faster solution is to convert both strings to character arrays.

Hint #4: If you sort two character arrays, then you will have something that is the same length 
and identical character for character.

Solution
You could write a naïve solution to this problem by taking a variable copy of the second input 
string, then looping over the first string and checking each letter exists in the second. If it does, 
remove it so it won’t be counted again; if not, return false. If you get to the end of the first 

www.hackingwithswift.com 17



string, then return true if the second string copy is now empty, otherwise return false.

For example:

func challenge3a(string1: String, string2: String) -> Bool {
   var checkString = string2

   for letter in string1 {
      if let index = checkString.index(of: letter) {
         checkString.remove(at: index)
      } else {
         return false
      }
   }

   return checkString.count == 0
}

That solution works, but is less than ideal because you’re having to look up letter positions 
repeatedly using index(of:), which is O(n). Worse, the remove(at:) call is also O(n), 
because it needs to move other elements down in the array once the item is removed.

A more efficient solution is to make arrays out of both sets of string characters, then sort them. 
Once that’s done, you can do a direct comparison using ==. This ends up not only being faster 
to run, but also involving much less code:

func challenge3b(string1: String, string2: String) -> Bool {
   let array1 = Array(string1)
   let array2 = Array(string2)
   return array1.sorted() == array2.sorted()
}

For bonus interview points, you might be tempted to write something like this:

return array1.count == array2.count && array1.sorted() == 

www.hackingwithswift.com 18



array2.sorted()

However, there’s no need – the == operator for arrays does that before doing its item-by-item 
comparison, so doing it yourself is just redundant.

www.hackingwithswift.com 19



Challenge 4: Does one string contain another?
Difficulty: Easy

Write your own version of the contains() method on String that ignores letter case, and 
without using the existing contains() method.

Sample input and output
• The code "Hello, world".fuzzyContains("Hello") should return true.
• The code "Hello, world".fuzzyContains("WORLD") should return true.
• The code "Hello, world".fuzzyContains("Goodbye") should return 

false.

Hints
Hint #1: You should write this as an extension to String.

Hint #2: You can’t use contains(), but there are other methods that do similar things.

Hint #3: Try the range(of:) method.

Hint #4: To ignore case, you can either uppercase both strings, or try the second parameter to 
range(of:).

Solution
If you were already familiar with the range(of:) method, this one should have proved 
straightforward. If not, you were probably wondering why I gave it an easy grade!

The range(of:) method returns the position of one string inside another. As it’s possible 
the substring might not exist in the other, the return value is optional. This is perfect for us: if 
we call range(of:) and get back nil, it means the substring isn’t contained inside the check 
string.

www.hackingwithswift.com 20



Ignoring letter case adds a little complexity, but can be solved either by collapsing the case 
before you do your check, or by using the .caseInsensitive option for range(of:).

The former looks like this:

extension String {
   func fuzzyContains(_ string: String) -> Bool {
      return self.uppercased().range(of: string.uppercased()) !
= nil
   }
}

And the latter like this:

extension String {
   func fuzzyContains(_ string: String) -> Bool {
      return range(of: string, options: .caseInsensitive) != 
nil
   }
}

In this instance the two are identical, but there’s a benefit to collapsing the case if you had to 
check through lots of items.

www.hackingwithswift.com 21



Challenge 5: Count the characters
Difficulty: Easy

Write a function that accepts a string, and returns how many times a specific character appears, 
taking case into account.

Tip: If you can solve this without using a for-in loop, you can consider it a Tricky 
challenge.

Sample input and output
• The letter “a” appears twice in “The rain in Spain”.
• The letter “i” appears four times in “Mississippi”.
• The letter “i” appears three times in “Hacking with Swift”.

Hints
Hint #1: Remember that String and Character are different data types.

Hint #2: Don’t be afraid to go down the brute force route: looping over characters using a 
for-in loop.

Hint #3: You could solve this functionally using reduce(), but tread carefully.

Hint #4: You could solve this using NSCountedSet, but I’d be suspicious unless you could 
justify the extra overhead.

Solution
You might be surprised to hear me saying this, but: this is a great interview question. It’s 
simple to explain, it’s simple to code, and it has enough possible solutions that it’s likely to 
generate some interesting discussion – which is gold dust in interviews.

This question is also interesting, because it’s another good example where the simple brute 

www.hackingwithswift.com 22



force approach is both among the most readable and most efficient. I suggested two 
alternatives in the hints, and I think it’s an interesting code challenge for you to try all three.

First, the easy solution: loop over the characters by hand, comparing against the check 
character. In code, it would be this:

func challenge5a(input: String, count: Character) -> Int {
   var letterCount = 0

   for letter in input {
      if letter == count {
         letterCount += 1
      }
   }

   return letterCount
}

There’s nothing complicated there, but do make sure you accept the check character as a 
Character to make the equality operation smooth.

The second option is to solve this problem functionally using reduce(). This has the 
advantage of making for very clear, expressive, and concise code, particularly when combined 
with the ternary operator:

func challenge5b(input: String, count: Character) -> Int {
   return input.reduce(0) {
      $1 == count ? $0 + 1 : $0
   }
}

So, that will start with 0, then go over every character in the string. If a given letter matches the 
input character, then it will add 1 to the reduce counter, otherwise it will return the current 
reduce counter. Functional programming does make for shorter code, and the intent here is 
nice and clear, however this is not quite as performant – it runs about 10% slower than the first 

www.hackingwithswift.com 23



solution.

A third solution is to use NSCountedSet, but that’s wasteful unless you intend to count 
several characters. It’s also complicated because Swift bridges String to NSObject well, 
but doesn’t bring Character, so NSCountedSet won’t play nicely unless you convert the 
characters yourself. So, your code would end up being something like this:

func challenge5c(input: String, count: String) -> Int {
   let array = input.map { String($0) }
   let counted = NSCountedSet(array: array)

   return counted.count(for: count)
}

That creates an array of strings by converting each character in the input string, then creates a 
counted set from the string array, and finally returns the count – for a single letter. Wasteful, 
for sure, and inefficient too – a massive ten times slower than the original.

There’s actually a fourth option you might have chosen. It’s the shortest option, however it 
requires a little lateral thinking: you can calculate how many times a letter appears in a string 
by removing it, then comparing the lengths of the original and modified strings. Here it is in 
Swift:

func challenge5d(input: String, count: String) -> Int {
   let modified = input.replacingOccurrences(of: count, with: 
"")
   return input.count - modified.count
}

www.hackingwithswift.com 24



Challenge 6: Remove duplicate letters from a string
Difficulty: Easy

Write a function that accepts a string as its input, and returns the same string just with 
duplicate letters removed.

Tip: If you can solve this challenge without a for-in loop, you can consider it “tricky” 
rather than “easy”.

Sample input and output
• The string “wombat” should print “wombat”.
• The string “hello” should print “helo”.
• The string “Mississippi” should print “Misp”.

Hints
Hint #1: Sets are great at removing duplicates, but bad at retaining order.

Hint #2: Foundation does have a way of forcing sets to retain their order, but you need to 
handle the typecasting.

Hint #3: You can create strings out of character arrays.

Hint #4: You can solve this functionally using filter().

Solution
There are three interesting ways this can be solved, and I’m going to present you with all three 
so you can see which suits you best. Remember: “fastest” isn’t always “best”, not least because 
readability is important, but also particularly because “memorizability” is important too – the 
perfect solution is often easily forgotten when you’re being tested.

Let’s look at a slow but interesting solution first: using sets. Swift’s standard library has a 

www.hackingwithswift.com 25



built-in Set type, but it does not preserve the order of its elements. This is a shame, because 
otherwise the solution would have been as simple as this:

let string = "wombat"
let set = Set(string)
print(String(set))

However, Foundation has a specialized set type called NSOrderedSet. This also removes 
duplicates, but now ensures items stay in the order they were added. Sadly, it’s not bridged to 
Swift in any pleasing way, which means to use it you must add typecasting: once from 
Character to String before creating the set, then once from Array<Any> to 
Array<String>.

This function does just that:

func challenge6a(string: String) -> String {
   let array = string.map { String($0) }
   let set = NSOrderedSet(array: array)
   let letters = Array(set) as! Array<String>
   return letters.joined()
}

That passes all tests, but I think you’ll agree it’s a bit ugly. I suspect Swift might see a native 
OrderedSet type in the future.

A second solution is to take a brute-force approach: create an array of used characters, then 
loop through every letter in the string and check if it’s already in the used array. If it isn’t, add 
it, then finally return a stringified form of the used array. 

This is nice and easy to write, as long as you know that you can create a String directly from 
a Character array:

func challenge6b(string: String) -> String {
   var used = [Character]()

   for letter in string {

www.hackingwithswift.com 26



      if !used.contains(letter) {
         used.append(letter)
      }
   }

   return String(used)
}

There is a third solution, and I think it’s guaranteed to generate some interesting discussion in 
an interview or book group!

As you know, dictionaries hold a value attached to a key, and only one value can be attached to 
a specific key at any time. You can change the value attached to a key just by assigning it 
again, but you can also call the updateValue() method – it does the same thing, but also 
returns either the original value or nil if there wasn’t one. So, if you call updateValue() 
and get back nil it means “that wasn’t already in the dictionary, but it is now.”

We can use this method in combination with the filter() method on our input string’s 
character property: filter the characters so that only those that return nil for 
updateValue() are used in the return array.

So, the third solution to this challenge looks like this:

func challenge6c(string: String) -> String {
   var used = [Character: Bool]()

   let result = string.filter {
      used.updateValue(true, forKey: $0) == nil
   }

   return String(result)
}

As long as you know about the updateValue() method, that code is brilliantly readable – 
the use of filter() means it’s clear what the loop is trying to do. However, it’s about 3x 

www.hackingwithswift.com 27



slower than the second solution when using our sample input and output data, so although it 
gets full marks for cleverness it falls short on performance.

www.hackingwithswift.com 28



Challenge 7: Condense whitespace
Difficulty: Easy

Write a function that returns a string with any consecutive spaces replaced with a single space.

Sample input and output
I’ve marked spaces using “[space]” below for visual purposes:

• The string “a[space][space][space]b[space][space][space]c” should return 
“a[space]b[space]c”.

• The string “[space][space][space][space]a” should return “[space]a”.
• The string “abc” should return “abc”.

Hints
Hint #1: You might think it a good idea to use components(separatedBy:) then 
joined(), but that will struggle with leading and trailing spaces.

Hint #2: You could loop over each character, keeping track of a seenSpace boolean that 
gets set to true when the previous character was a space.

Hint #3: You could use regular expressions.

Hint #4: Try using replacingOccurrences(of:)

Solution
As is the case for many other string challenges, we can write a naïve solution or a clever one, 
but here the clever one is dramatically simpler – and it uses regular expressions. (Yes, you did 
just read “simpler” and “regular expressions” in the same sentence.)

But first, let’s look at something you might have tried:

www.hackingwithswift.com 29



func challenge7(input: String) -> String {
   let components = 
input.components(separatedBy: .whitespacesAndNewlines)
   return components.filter { !$0.isEmpty }.joined(separator: " 
")
}

That splits a string up by its spaces, then removes any empty items, and joins the remainder 
using a space, and is the ideal solution – if your goal is to remove any duplicate whitespace 
while also removing leading and trailing whitespace. However, it fails the requirement that 
“[space][space][space][space]a” should return “[space]a“, so you should have rejected it.

Instead, you might have written a loop over the characters in the input string. If the current 
letter was a space and you had already seen one in this run, continue to the next letter. 
Otherwise, mark that you’ve seen a space. If it wasn’t a space, clear the space flag. Regardless 
of whether it was the first space or a letter, append it to an output string.

Transform that into Swift and you get this:

func challenge7a(input: String) -> String {
   var seenSpace = false
   var returnValue = ""

   for letter in input {
      if letter == " " {
         if seenSpace { continue }
         seenSpace = true
      } else {
         seenSpace = false
      }

      returnValue.append(letter)
   }

   return returnValue

www.hackingwithswift.com 30



}

This is a clear solution, and it works great. However, for once, this is a place where regular 
expressions can help: they turn all that into a single line of code:

func challenge7b(input: String) -> String {
   return input.replacingOccurrences(of: " +", with: " ", 
options: .regularExpression, range: nil)
}

If you’re not familiar with regular expressions, “[space]+” means “match one or more spaces”, 
so that will cause all multiple spaces to be replaced with a single space. Running regular 
expressions isn’t cheap, so that code runs about 50% the speed of the manual solution, but you 
would have to be doing a heck of a lot of work in order for it to be noticeable.

www.hackingwithswift.com 31



Challenge 8: String is rotated
Difficulty: Tricky

Write a function that accepts two strings, and returns true if one string is rotation of the other, 
taking letter case into account.

Tip: A string rotation is when you take a string, remove some letters from its end, then append 
them to the front. For example, “swift” rotated by two characters would be “ftswi”.

Sample input and output
• The string “abcde” and “eabcd” should return true.
• The string “abcde” and “cdeab” should return true.
• The string “abcde” and “abced” should return false; this is not a string rotation.
• The string “abc” and “a” should return false; this is not a string rotation.

Hints
Hint #1: This is easier than you think.

Hint #2: A string is only considered a rotation if is identical to the original once you factor in 
the letter movement. That is, “tswi” is not a rotation of “swift” because it is missing the F.

Hint #3: If you write a string twice, it must encapsulate all possible rotations, e.g. “catcat” 
contains “cat”, “tca”, and “atc”.

Solution
This question appears in coding interviews far more than it deserves, because it’s a problem 
that seems tricky the first time you face it but is staring-you-in-the-face obvious once someone 
has told you the solution. I wonder how many times this question appears on interviews just so 
the interviewer can feel smug about knowing the answer!

www.hackingwithswift.com 32



Anyway, let’s talk about the solution. As I said in hint #3, if you write a string twice it must 
always encapsulate all possible rotations. So if your string was “abc” then you would double it 
to “abcabc”, which contains all possible rotations: “abc”, “cab”, and “bca”.

So, an initial solution might look like this:

func challenge8(input: String, rotated: String) -> Bool {
   let combined = input + input
   return combined.contains(rotated)
}

However, that’s imperfect – the final example input and output was that “abc” should return 
false when given the test string “a”. Using the code above, the input string would be double to 
“abcabc”, which clearly contains the test string “a”. To fix this, we need to check not only that 
the test string exists in the doubled input, but also that both strings are the same size.

So, the correct solution is this:

func challenge8(input: String, rotated: String) -> Bool {
   guard input.count == rotated.count else { return false }
   let combined = input + input
   return combined.contains(rotated)
}

Like I said, it’s easier than you think, but is it a test of coding knowledge? Not really. If 
anything, you get a brief “aha!” flash when someone explains the solution to you, but apart 
from scoring you some interview brownie points I doubt this would be useful in real life.

www.hackingwithswift.com 33



Challenge 9: Find pangrams
Difficulty: Tricky

Write a function that returns true if it is given a string that is an English pangram, ignoring 
letter case.

Tip: A pangram is a string that contains every letter of the alphabet at least once.

Sample input and output
• The string “The quick brown fox jumps over the lazy dog” should return true.
• The string “The quick brown fox jumped over the lazy dog” should return false, 

because it’s missing the S.

Hints
Hint #1: Make sure you start by collapsing case using something like lowercased().

Hint #2: You can compare letters using >, >=, and so on.

Hint #3: If you remove duplicates and non-alphabetic characters, the remaining string should 
add up to 26 letters.

Solution
You could try and solve this using character sets, but it’s really not needed: Swift’s characters 
conform to Comparable, so you can compare them against “a” and “z” directly to ensure 
they are alphabetical.

Once you know how to ensure a letter is alphabetical, all that remains is removing duplicates 
(easy using a set) and collapsing case (lowercased() is fine), then comparing the count of 
the result against 26.

So, here’s an example solution in just three lines of code:

www.hackingwithswift.com 34



So, here’s an example solution in just three lines of code:

func challenge9(input: String) -> Bool {
   let set = Set(input.lowercased())
   let letters = set.filter { $0 >= "a" && $0 <= "z" }
   return letters.count == 26
}

www.hackingwithswift.com 35



Challenge 10: Vowels and consonants
Difficulty: Tricky

Given a string in English, return a tuple containing the number of vowels and consonants.

Tip: Vowels are the letters, A, E, I, O, and U; consonants are the letters B, C, D, F, G, H, J, K, 
L, M, N, P, Q, R, S, T, V, W, X, Y, Z.

Sample input and output
• The input “Swift Coding Challenges” should return 6 vowels and 15 consonants.
• The input “Mississippi” should return 4 vowels and 7 consonants.

Hints
Hint #1: Just because a letter is not a vowel, it doesn’t mean it’s a consonant – think 
punctuation, for example.

Hint #2: You’ll need to differentiate carefully between the String and Character types.

Hint #3: You could use CharacterSet here, but is it really needed?

Hint #4: Your return type should be (vowels: Int, consonants: Int).

Hint #5: Watch out for uppercase and lowercase letters – an “A” is a vowel regardless of its 
case.

Solution
There are three interesting ways to solve this challenge, and I’m going to present them to you 
slowest first – although realistically you’ll only see significant performance differences if these 
functions are being called run a hundred times or more.

First, you could use CharacterSet here, but it has a fatal flaw: even though “Character” is 

www.hackingwithswift.com 36



right there in the name, you can’t ask a character set whether it contains a single character 
because it works on a different type called UnicodeScalar. Instead, you need to convert 
the Character to a String, then use its rangeOfCharacter(from:) method, like 
this:

func challenge10a(input: String) -> (vowels: Int, consonants: 
Int) {
   let vowels = CharacterSet(charactersIn: "aeiou")
   let consonants = CharacterSet(charactersIn: 
"bcdfghjklmnpqrstvwxyz")

   var vowelCount = 0
   var consonantCount = 0

   for letter in input.lowercased() {
      let stringLetter = String(letter)

      if stringLetter.rangeOfCharacter(from: vowels) != nil {
         vowelCount += 1
      } else if stringLetter.rangeOfCharacter(from: 
consonants) != nil {
         consonantCount += 1
      }
   }

   return (vowelCount, consonantCount)
}

The second solution builds on the first: if you’re going to work with string methods, why not 
just skip the character set entirely? There’s a perfectly good contains() method on strings 
that works just as well, so you can avoid allocating a character set at all:

func challenge10b(input: String) -> (vowels: Int, consonants: 
Int) {

www.hackingwithswift.com 37



   let vowels = "aeiou"
   let consonants = "bcdfghjklmnpqrstvwxyz"

   var vowelCount = 0
   var consonantCount = 0

   for letter in input.lowercased() {
      let stringLetter = String(letter)

      if vowels.contains(stringLetter) {
         vowelCount += 1
      } else if consonants.contains(stringLetter) {
         consonantCount += 1
      }
   }

   return (vowelCount, consonantCount)
}

The third option is to do away with strings entirely: create arrays of vowel characters and 
consonant characters, then use the contains() method on the arrays to see if each letter 
matches. In code, you’d get this:

func challenge10c(input: String) -> (vowels: Int, consonants: 
Int) {
   let vowels = "aeiou"
   let consonants = "bcdfghjklmnpqrstvwxyz"

   var vowelCount = 0
   var consonantCount = 0

   for letter in input.lowercased() {
      if vowels.contains(letter) {
         vowelCount += 1

www.hackingwithswift.com 38



      } else if consonants.contains(letter) {
         consonantCount += 1
      }
   }

   return (vowelCount, consonantCount)
}

There’s very little to separate each of these three answers in terms of performance, so go with 
whichever is most familiar to you. 

www.hackingwithswift.com 39



Challenge 11: Three different letters
Difficulty: Tricky

Write a function that accepts two strings, and returns true if they are identical in length but 
have no more than three different letters, taking case and string order into account.

Sample input and output
• The strings “Clamp” and “Cramp” would return true, because there is one letter 

difference.
• The strings “Clamp” and “Crams” would return true, because there are two letter 

differences.
• The strings “Clamp” and “Grams” would return true, because there are three letter 

differences.
• The strings “Clamp” and “Grans” would return false, because there are four letter 

differences.
• The strings “Clamp” and “Clam” would return false, because they are different lengths.
• The strings “clamp” and “maple” should return false. Although they differ by only one 

letter, the letters that match are in different positions.

Hints
Hint #1: If you value your sanity, get both strings into arrays as quickly as possible.

Hint #2: You probably want to use the enumerated() method on one array, to get the 
index and character at the same time.

Hint #3: Your function should return false as soon as it reaches four differences; there’s no 
point continuing to check characters.

Hint #4: Make sure you check the strings are the same size first, preferably using guard.

www.hackingwithswift.com 40



Solution
This problem isn’t hard as long as you convert your strings into character arrays – if you don’t, 
you need to advance through string indices, which is never pleasant and certainly hard to do 
during an interview.

The simplest, clearest way to solve this challenge is like so:

1. Start with an early return in case the two strings have different lengths.
2. Create arrays out of both strings.
3. Initialize a differences counter to 0.
4. Loop over the first array, using enumerated() so we get the current index as well as 

each character.
5. Compare that character against the character at the same index in the other string array.
6. If they are different, add one to differences.
7. If as a result of that differences is now 4, return false.
8. On the other hand, if we get to the end of the array, it means we can return true.

Something like this ought to do the trick:

func challenge11(first: String, second: String) -> Bool {
   guard first.count == second.count else { return false }

   let firstArray = Array(first)
   let secondArray = Array(second)
   var differences = 0

   for (index, letter) in firstArray.enumerated() {
      if secondArray[index] != letter {
         differences += 1

         if differences == 4 {
            return false
         }
      }
   }

www.hackingwithswift.com 41



   return true
}

www.hackingwithswift.com 42



Challenge 12: Find longest prefix
Difficulty: Tricky

Write a function that accepts a string of words with a similar prefix, separated by spaces, and 
returns the longest substring that prefixes all words.

Sample input and output
• The string “swift switch swill swim” should return “swi”.
• The string “flip flap flop” should return “fl”.

Hints
Hint #1: Start with components(separatedBy:) so you can check words with a loop.

Hint #2: You’ll need a property for the prefix you’re currently checking as well as for the best 
prefix you’ve found so far.

Hint #3: Make sure you use the hasPrefix() method.

Solution
I’ve watched some people blast through this code in a minute, and others struggle to finish in 
30 minutes as they get into a mess of recursion.

The key to a simple solution is the hasPrefix() method, which avoids the mess of string 
slicing: start with an empty string, then continue adding more letters from the first word until 
hasPrefix() fails for any of the other words.

So: rather than trying to write a recursive function, you can solve this problem using an inner 
loop, like this:

func challenge12(input: String) -> String {
   let parts = input.components(separatedBy: " ")

www.hackingwithswift.com 43



   guard let first = parts.first else { return "" }

   var currentPrefix = ""
   var bestPrefix = ""

   for letter in first {
      currentPrefix.append(letter)

      for word in parts {
         if !word.hasPrefix(currentPrefix) {
            return bestPrefix
         }
      }

      bestPrefix = currentPrefix
   }

   return bestPrefix
}

www.hackingwithswift.com 44



Challenge 13: Run-length encoding
Difficulty: Taxing

Write a function that accepts a string as input, then returns how often each letter is repeated in 
a single run, taking case into account.

Tip: This approach is used in a simple lossless compression technique called run-length 
encoding.

Sample input and output
• The string “aabbcc” should return “a2b2c2”.
• The strings “aaabaaabaaa” should return “a3b1a3b1a3”
• The string “aaAAaa” should return “a2A2a2”

Hints
Hint #1: This would be quite straightforward in other languages using character lookahead, 
but that’s expensive in Swift thanks to grapheme clusters – String isn’t an array.

Hint #2: To use the “Swifty” approach of looping over your string as-is, make sure you keep 
track of the current character, and its count, as you loop over the string.

Hint #3: Alternatively, consider converting your string to a proper array that you can index 
into freely, so you can look ahead to compare letters.

Solution
There are two ways you could solve this, but in a stressful interview environment realistically 
there’s only one you’ll reach for: the “dumb”, brute force approach. I put dumb in quotes for a 
reason – more on that later.

This approach would use an algorithm like this:

www.hackingwithswift.com 45



• Create a currentLetter variable that contains an optional Character, as well as 
a counter integer and a return value string.

• Loop through every letter in the input string.
• If the letter is equal to our current letter, add one to the counter.
• Otherwise, if currentLetter has a value it means we already had a letter and it’s 

about to change, so append it to the return string.
• Regardless, update currentLetter to be the new letter, and reset the counter to 1.
• Once the loop finishes, append currentLetter to the return string along with the 

counter, then return it.

That last step is easily forgotten – because the return string is only modified when the letter 
changes, the last letter sequence won’t be added unless we do it by hand.

Putting that into code:

func challenge13a(input: String) -> String {
   var currentLetter: Character?
   var returnValue = ""
   var letterCounter = 0

   for letter in input {
      if letter == currentLetter {
         letterCounter += 1
      } else {
         if let current = currentLetter {
            returnValue.append("\(current)\(letterCounter)")
         }

         currentLetter = letter
         letterCounter = 1
      }
   }

   if let current = currentLetter {

www.hackingwithswift.com 46



      returnValue.append("\(current)\(letterCounter)")
   }

   return returnValue
}

An alternative solution, which is probably one you would be more likely to use if you had 
come to Swift after learning a different language, would be to use character look ahead: if the 
next character is different to the current one, or if we’re about to hit the end of the array, then 
modify the return value and reset the counter.

This alternative solution doesn’t come naturally to Swift developers, because indexing into 
strings is expensive in Swift thanks to the grapheme cluster system. However, you could 
convert the Swift character index into an array, then use that. This would give you the 
following solution:

func challenge13b(input: String) -> String {
   var returnValue = ""
   var letterCounter = 0
   var letterArray = Array(input)

   for i in 0 ..< letterArray.count {
      letterCounter += 1

      if i + 1 == letterArray.count || letterArray[i] != 
letterArray[i + 1] {
         returnValue += "\(letterArray[i])\(letterCounter)"
         letterCounter = 0
      }
   }

   return returnValue
}

www.hackingwithswift.com 47



That’s a valid solution, but does it beat the “dumb” one? Well, it depends what you mean by 
“beat”: it’s certainly less code, and it avoids the if let repetition, but it runs slower – about 
15% so, in my tests, despite containing much less code.

So, I think the “dumb” solution is superior: not only does it run faster, but it’s also easier to 
figure out on the fly while you’re being tested!

www.hackingwithswift.com 48



Challenge 14: String permutations
Difficulty: Taxing

Write a function that prints all possible permutations of a given input string.

Tip: A string permutation is any given rearrangement of its letters, for example “boamtw” is a 
permutation of “wombat”.

Sample input and output
• The string “a” should print “a”.
• The string “ab” should “ab”, “ba”.
• The string “abc” should print “abc”, “acb”, “bac”, “bca”, “cab”, “cba”.
• The string “wombat” should print 720 permutations.

Hints
Hint #1: Your function will need to call itself.

Hint #2: The number of lines printed should be the factorial of the length of your string, e.g. 
“wombat” has six characters, so will have 6! permutations: 6 x 5 x 4 x 3 x 2 x 1, or 720.

Hint #3: You’ll find it easiest to convert the string to a character array for easier indexing.

Hint #4: Each time your function is called, it should loop through all letters in the string so 
that all combinations are generated.

Hint #5: You can slice arrays using strArray[0...3].

Hint #6: You can convert string array slices into strings just by using an initializer: 
String(strArray[0...3]).

Solution

www.hackingwithswift.com 49



This is a recursive function with lots of looping, but the nature of factorials is that the loops get 
smaller each time. Given the input string “wombat”, the first time the function is called you’ll 
need to loop from 0 up to 5, calling the recursive function each time.

So, initially you’ll pick “w” as it’s the first letter, and in you go to the recursive function – but 
this time there are only five letters to loop over, so you choose “o”, and go into the function 
again, etc.

Eventually you spell out “wombat”, which is the result of choosing the first remaining letter 
each time. But now that you’ve reached the deepest point of the recursion, you back up a level: 
when you had “womb” it chose the first letter in the remainder (“at”) to make “wombat”, but 
now that path has been explored it should choose the second remaining letter (“t”) to make 
“wombt”, at which point the only remaining letter now is “a” to make “wombta”.

Again the recursion has maxed out, so now it will need to go back one level further: when it 
was at “wom” the remainder was “bat” so it chose the first letter, but now it should choose the 
second, to make “woma”, with “bt” as remainder. On the first pass it will choose “b” first then 
“t” (making “womabt”), and on the second it will choose “t” then “b” (making “womatb”). 
That subset of the path is now maxed out, so it will wind back to “wom” and “bat” and choose 
the third letter, to make “womt” with “ba” as remainder. So it will get “womtba” then 
“womtab”, and so on, and so on.

That’s the algorithm. It sounds clunky when explained step by step, but trust me: a CPU flies 
through this. Here it is in code:

func challenge14(string: String, current: String = "") {
   let length = string.count
   let strArray = Array(string)

   if (length == 0) {
      // there's nothing left to re-arrange; print the result
      print(current)
      print("******")
   } else {
      print(current)

www.hackingwithswift.com 50



      // loop through every character
      for i in 0 ..< length {
         // get the letters before me
         let left = String(strArray[0 ..< i])

         // get the letters after me
         let right = String(strArray[i+1 ..< length])

         // put those two together and carry on
         challenge14(string: left + right, current: current + 
String(strArray[i]))
      }
   }
}

Note: the print("******") and second print(current) call are there to help you see 
how the function works; they serve no functional purpose.

www.hackingwithswift.com 51



Challenge 15: Reverse the words in a string
Difficulty: Taxing

Write a function that returns a string with each of its words reversed but in the original order, 
without using a loop.

Sample input and output
• The string “Swift Coding Challenges” should return “tfiwS gnidoC segnellahC”.
• The string “The quick brown fox” should return “ehT kciuq nworb xof”.

Hints
Hint #1: You should start by converting the string into an array by separating on spaces.

Hint #2: You can reverse the characters in a string by calling reversed() on it.

Hint #3: You can create a string from a character array.

Hint #4: You want to convert an array of left to right strings into an array of right to left 
strings, all without using a loop - this is a perfect use for map().

Hint #5: Once you have an array of reversed strings, you can create a single string using 
joined().

Solution
The requirement not to use a loop is what makes this a taxing challenge; if you could just use 
for-in then it would be easy or tricky depending on who spoke to.

The only sensible way to solve this is using functional programming: the need to convert from 
one kind of array (“words written left to right”) to another (“words written right to left”) is the 
perfect use case for map(), so most of what remains is creating the array by splitting on 
spaces then recreating the string once you’ve reversed the words.

www.hackingwithswift.com 52



spaces then recreating the string once you’ve reversed the words.

Here’s my solution:

func challenge15(input: String) -> String {
   let parts = input.components(separatedBy: " ")
   let reversed = parts.map { String($0.reversed()) }
   return reversed.joined(separator: " ")
}

www.hackingwithswift.com 53



Chapter 2
Numbers

www.hackingwithswift.com 54



Challenge 16: Fizz Buzz
Difficulty: Easy

Write a function that counts from 1 through 100, and prints “Fizz” if the counter is evenly 
divisible by 3, “Buzz” if it’s evenly divisible by 5, “Fizz Buzz” if it’s even divisible by three 
and five, or the counter number for all other cases.

Sample input and output
• 1 should print “1”
• 2 should print “2”
• 3 should print “Fizz”
• 4 should print “4”
• 5 should print “Buzz”
• 15 should print “Fizz Buzz”

Hints
Hint #1: You’ll need to use modulus: %.

Hint #2: Check for the “Fizz Buzz” case first, because that’s most specific.

Hint #3: Remember to use the closed range operator to include the number 100 at the end.

Solution
This is the holotype of interview questions, so it was inevitable it would be in here somewhere. 
Sadly, people really do fail it – I’ve seen it myself – which ought to be impossible, so I hope 
you didn’t just skip by blithely!

A simple solution looks like this:

func challenge16a() {

www.hackingwithswift.com 55



   for i in 1...100 {
      if i % 3 == 0 && i % 5 == 0 {
         print("Fizz Buzz")
      } else if i % 3 == 0 {
         print("Fizz")
      } else if i % 5 == 0 {
         print("Buzz")
      } else {
         print(i)
      }
   }
}

You could make it slightly more efficient by nesting the “Fizz Buzz” case inside two if 
statements rather than one:

func challenge16b() {
   for i in 1...100 {
      if i % 3 == 0 {
         if i % 5 == 0 {
            print("Fizz Buzz")
         } else {
            print("Fizz")
         }
      } else if i % 5 == 0 {
         print("Buzz")
      } else {
         print(i)
      }
   }
}

Using this approach, you don’t end up evaluating i % 3 twice.

www.hackingwithswift.com 56



You could be a bit more fancy using forEach and ternary operators, but you’re basically 
sacrificing readability for smugness:

func challenge16c() {
   (1...100).forEach {
      print($0 % 3 == 0 ? $0 % 5 == 0 ? "Fizz Buzz" : "Fizz" : 
$0 % 5 == 0 ? "Buzz" : "\($0)") }
}

Note that I used "\($0)" rather than String($0) – this is a pet hate of mine, but that code 
is so complex that Swift actually fails to compile if I used String($0)!

www.hackingwithswift.com 57



Challenge 17: Generate a random number in a range
Difficulty: Easy

Write a function that accepts positive minimum and maximum integers, and returns a random 
number between those two bounds, inclusive.

Sample input and output
• Given minimum 1 and maximum 5, the return values 1, 2, 3, 4, 5 are valid.
• Given minimum 8 and maximum 10, the return values 8, 9, 10 are valid.
• Given minimum 12 and maximum 12, the return value 12 is valid.
• Given minimum 12 and maximum 18, the return value 7 is invalid.

Hints
Hint #1: There are lots of ways to generate random numbers in Swift; you’ll be judged –
 silently or openly – on your choice.

Hint #2: Keep in mind that lots of random number generators generate from 0 up to a certain 
point so you’ll need to write code to count from an arbitrary number upwards.

Hint #3: Also remember that lots of random number generators generate up to but excluding 
the maximum, so you should add 1 to make sure your tests pass.

Hint #4: Take a look at arc4random_uniform().

Solution
There are several choices for random number generators, and your choice says a lot about your 
skill level. Very roughly:

• If you used rand() you probably came from a C background, or don’t generally care 
about randomness.

www.hackingwithswift.com 58



• If you used GameplayKit, you’re either fairly new to iOS development, or particularly 
interested in the random number generation shaping GameplayKit offers.

• If you used arc4random() you’re showing some awareness of half-decent random 
number generator, but are unaware of – or uninterested in – modulo bias.

• If you used arc4random_uniform(), then you’re showing some serious chops.

Of the four options, arc4random_uniform() is preferred amongst developers, because it 
generates suitably random numbers for most purposes, it doesn’t require seeding, it isn’t prone 
to modulo bias, and it isn’t restricted to Apple platforms – it’s a commonly used C function 
that is well understood.

Using arc4random_uniform() has three hiccups in Swift: it generates numbers from 0 
up to an upper bound, it excludes the upper bound rather than including it, and it uses UInt32 
rather than Int, so you need some typecasting.

We can fix all those problems and still write just one line of code to solve the challenge:

func challenge17(min: Int, max: Int) -> Int {
   return Int(arc4random_uniform(UInt32(max - min + 1))) + min
}

www.hackingwithswift.com 59



Challenge 18: Recreate the pow() function
Difficulty: Easy

Create a function that accepts positive two integers, and raises the first to the power of the 
second.

Tip: If you name your function myPow() or challenge18(), you’ll be able to use the 
built-in pow() for your tests. The built-in pow() uses doubles, so you’ll need to typecast.

Sample input and output
• The inputs 4 and 3 should return 64, i.e. 4 multiplied by itself 3 times.
• The inputs 2 and 8 should return 256, i.e. 2 multiplied by itself 8 times.

Hints
Hint #1: You don’t need any hints to solve this one.

Hint #2: Oh, alright: here’s a hint: you can either use a loop or, if you’re feeling fancy, use a 
recursive function.

Hint #3: Here’s another: you could use guard or precondition() to ensure both 
numbers are positive.

Solution
This ought to have been the easiest of easy challenges, because all you’re doing is multiplying 
a number against itself a fixed number of times.

In its most simple form, you can solve the challenge like this:

func challenge18a(number: Int, power: Int) -> Int {
   guard number > 0, power > 0 else { return 0 }
   var returnValue = number

www.hackingwithswift.com 60



   for _ in 1..<power {
      returnValue *= number
   }

   return returnValue
}

Like I said in the hints, you could also solve this challenge using a recursive function. To do 
that, make the function multiply its input number by the return value of calling itself, 
subtracting 1 from the power parameter each time, like this:

func challenge18b(number: Int, power: Int) -> Int {
   guard number > 0, power > 0 else { return 0 }
   if power == 1 { return number }

   return number * challenge18b(number: number, power: power - 
1)
}

www.hackingwithswift.com 61



Challenge 19: Swap two numbers
Difficulty: Easy

Swap two positive variable integers, a and b, without using a temporary variable.

Sample input and output
• Before running your code a should be 1 and b should be 2; afterwards, b should be 1 

and a should be 2.

Hints
Hint #1: There are lots of ways to solve this, but probably the easiest to remember is using 
tuples.

Hint #2: Alternatively, try using the global Swift function swap().

Hint #3: If you’re feeling fancy, you can solve this problem with arithmetic.

Hint #4: If you’re feeling fancy and want to demonstrate your bit manipulation skills, you can 
also solve this problem using bitwise XOR.

Solution
This is a favorite question of lazy interviewers – people who don’t want to spend an hour of 
life coming up with genuinely interesting, useful questions that explore real Swift knowledge. 
It used to be important in Ye Olde Days when every byte mattered, but in a world where Slack 
on macOS happily chews through 400MB of RAM just idling this test is more a curiosity than 
anything else.

Still, this question does one have benefit, which is that are idiomatic solutions for Swift 
developers – i.e., Swifty ways to solve it.

Let’s look at the basic solution first, which looks like this:

www.hackingwithswift.com 62



Let’s look at the basic solution first, which looks like this:

a = a + b
b = a - b
a = a - b

That’s the solution you’d use in most languages, and it works fine. If you’re feeling smart, you 
can also use XOR like this:

a = a ^ b
b = a ^ b
a = a ^ b

I think that solution is fractionally easier to remember, because it uses the same operator all 
three times.

So, those are the standard solutions, but Swift gives us two alternatives. First, there’s a global 
swap() function that swaps two values of the same type, like this:

swap(&a, &b)

The swap() function is micro-optimized to be as fast as possible, so you’ll see it used 
extensively in sorting algorithms.

A second idiomatic solution is to use tuples. This delivers a beautifully solution to the 
challenge that is also undeniably Swifty:

(a, b) = (b, a)

Marvelous.

www.hackingwithswift.com 63



Challenge 20: Number is prime
Difficulty: Tricky

Write a function that accepts an integer as its parameter and returns true if the number is prime.

Tip: A number is considered prime if it is greater than one and has no positive divisors other 
than 1 and itself.

Sample input and output
• The number 11 should return true.
• The number 13 should return true.
• The number 4 should return false.
• The number 9 should return false.
• The number 16777259 should return true.

Hints
Hint #1: You should start with a brute force approach: loop through every number from 2 up 
to one less than the input number, and check whether the input number divides into it.

Hint #2: You can shrink the search space by searching up to a smaller number – what’s the 
highest it could be?

Hint #3: There’s no point searching higher than the square root of your input number, 
rounding up.

Solution
There’s a naïve solution to this problem, but it has terrible performance characteristics – 
there’s a reason I included 16,777,259 in the list of sample input and output.

The naïve solution looks like this:

www.hackingwithswift.com 64



func challenge20a(number: Int) -> Bool {
   guard number >= 2 else { return false }

   for i in 2 ..< number {
      if number % i == 0 {
         return false
      }
   }

   return true
}

That counts from 2 up to one less than the input number, and returns false if the input number 
divides equally into i. That works correctly. It has a guard statement at the front because the 
numbers 1 and lower are not prime by definition.

The problem with this function is that it’s computationally expensive: 16,777,259 is a prime 
number, so this solution will divide from 2 up to 16,777,258 and find that none of them work 
before deciding that the number is prime.

Consider this: if the number n is not prime, it means it can be reached by multiplying two 
factors, x and y. If both of those numbers were greater than the square root of n, then x * y 
would be greater than n, which is not possible. So, we can be sure that at least one of x or y is 
less than or equal to the square root of n.

As a result of this, we can dramatically reduce our search space: rather than counting from 2 up 
to 16,777,259, we can square root the number and round up, to get 4097, then search up to 
there and no further. Remember, we don’t have to find both numbers that multiply to make n, 
just one of them, because if we find one – and it isn’t 1 or itself – it means n is not prime.

So, we could write a second solution like this:

func challenge20b(number: Int) -> Bool {
   guard number >= 2 else { return false }
   guard number != 2 else { return true }

www.hackingwithswift.com 65



   let max = Int(ceil(sqrt(Double(number))))

   for i in 2 ... max {
      if number % i == 0 {
         return false
      }
   }

   return true
}

Because this second solution uses the closed range operator, ..., rather than the half-open 
range operator, ..<, it’s important to add the second guard check at the top so that 2 doesn’t 
evaluate incorrectly.

This second solution performs significantly faster for primes such as 16,777,259, because 
rather than around 16 million searches you’re now doing around 4000.

www.hackingwithswift.com 66



Challenge 21: Counting binary ones
Difficulty: Tricky

Create a function that accepts any positive integer and returns the next highest and next lowest 
number that has the same number of ones in its binary representation. If either number is not 
possible, return nil for it.

Sample input and output
• The number 12 is 1100 in binary, so it has two 1s. The next highest number with that 

many 1s is 17, which is 10001. The next lowest is 10, which is 1010.
• The number 28 is 11100 in binary, so it has three 1s. The next highest number with that 

many 1s is 35, which is 100011. The next lowest is 26, which is 11010.

Hints
Hint #1: You can find the binary representation of an integer by converting it to a string – look 
for a “radix” initializer.

Hint #2: You should be using radix 2, which is binary.

Hint #3: Your return value ought to be (nextHighest: Int?, nextLowest: 
Int?).

Hint #4: You can count the 1s in a stringified number by using filter() on its letters 
property.

Hint #5: Don’t be afraid to duplicate code while you’re working – you need to search up and 
down for the same thing, so start with duplication then refactor.

Hint #6: You can’t create ranges where the end is higher than the start. Instead, create a 
forwards range then reverse it.

www.hackingwithswift.com 67



Solution
This is a classic computing science problem, although I have to admit Swift makes it quite 
easy thanks to its range of String initializers.

First and most importantly, you get the binary representation of an integer like this:

let binaryString = String(someNumber, radix: 2)

With that done, you can count the 1s by filtering by character and counting the resulting array:

let numberOfOnes = binaryString.filter { (char: Character) -> 
Bool in char == "1" }.count

All that leaves is finding the next highest and lowest, which can be done by counting upwards 
or downwards until the you back the same number of ones.

Here’s the complete solution:

func challenge21a(number: Int) -> (nextHighest: Int?, 
nextLowest: Int?) {
   let targetBinary = String(number, radix: 2)
   let targetOnes = targetBinary.filter { (char: Character) -> 
Bool in char == "1" }.count

   var nextHighest: Int? = nil
   var nextLowest: Int? = nil

   for i in number + 1...Int.max {
      let currentBinary = String(i, radix: 2)
      let currentOnes = currentBinary.filter { (char: 
Character) -> Bool in char == "1" }.count

      if targetOnes == currentOnes {
         nextHighest = i
         break

www.hackingwithswift.com 68



      }
   }

   for i in (0 ..< number).reversed() {
      let currentBinary = String(i, radix: 2)
      let currentOnes = currentBinary.filter { (char: 
Character) -> Bool in char == "1" }.count

      if targetOnes == currentOnes {
         nextLowest = i
         break
      }
   }

   return (nextHighest, nextLowest)
}

Looking at that code, the duplication of binary counting does rather stick out. We can refactor 
it into a nested function to remove the duplication, although it only reduces the overall size of 
the solution by a little:

func challenge21b(number: Int) -> (nextHighest: Int?, 
nextLowest: Int?) {
   func ones(in number: Int) -> Int {
      let currentBinary = String(number, radix: 2)
      return currentBinary.filter { (char: Character) -> Bool 
in char == "1" }.count
   }

   let targetOnes = ones(in: number)
   var nextHighest: Int? = nil
   var nextLowest: Int? = nil

   for i in number + 1...Int.max {

www.hackingwithswift.com 69



      if ones(in: i) == targetOnes {
         nextHighest = i
         break
      }
   }

   for i in (0 ..< number).reversed() {
      if ones(in: i) == targetOnes {
         nextLowest = i
         break
      }
   }

   return (nextHighest, nextLowest)
}

www.hackingwithswift.com 70



Challenge 22: Binary reverse
Difficulty: Tricky

Create a function that accepts an unsigned 8-bit integer and returns its binary reverse, padded 
so that it holds precisely eight binary digits.

Tip: When you get the binary representation of a number, Swift will always use as few bits as 
possible – make sure you pad to eight binary digits before reversing.

Sample input and output
• The number 32 is 100000 in binary, and padded to eight binary digits that’s 00100000. 

Reversing that binary sequence gives 00000100, which is 4. So, when given the input 
32 your function should return 4.

• The number 41 is 101001 in binary, and padded to eight binary digits that 00101001. 
Reversing that binary sequence gives 10010100, which is 148. So, when given the 
input 41 your function should return 148.

• It should go without saying that your function should be symmetrical: when fed 4 it 
should return 32, and when fed 148 it should return 41.

Hints
Hint #1: You can get the binary representation of an integer using String(someNumber, 
radix: 2).

Hint #2: You can get the decimal integer equivalent of a string containing binary using 
Int(someString, radix: 2) – but be warned that will given you an optional integer.

Hint #3: To pad the input number’s binary representation so that it holds eight digits, use the 
String(repeating:count:) initializer.

Hint #4: You can reverse a character array then create a new string from it.

www.hackingwithswift.com 71



Solution
As long as you’re comfortable converting to and from binary, this challenge is not likely to 
pose any problems for you. In fact, apart from converting between binary and decimal, the 
only interesting parts of the problem are calculating how much zero padding to apply and 
reversing the binary representation.

Calculating and adding the padding can be done by subtracting the current character count 
from 8, like this:

let paddingAmount = 8 - binary.count
let paddedBinary = String(repeating: "0", count: paddingAmount) 
+ binary

Reversing the padding binary representation is as easy as calling reversed() on the binary 
string, then creating a new string out of it, like this:

let reversedBinary = String(paddedBinary.reversed())

With all that in mind, here’s a complete solution:

func challenge22(number: UInt) -> UInt {
   let binary = String(number, radix: 2)
   let paddingAmount = 8 - binary.count
   let paddedBinary = String(repeating: "0", count: 
paddingAmount) + binary
   let reversedBinary = String(paddedBinary.reversed())
   return UInt(reversedBinary, radix: 2)!
}

www.hackingwithswift.com 72



Challenge 23: Integer disguised as string
Difficulty: Tricky

Write a function that accepts a string and returns true if it contains only numbers, i.e. the digits 
0 through 9.

Sample input and output
• The input “01010101” should return true.
• The input “123456789” should return true.
• The letter “9223372036854775808” should return true.
• The letter “1.01” should return false; “.” is not a number.

Hints
Hint #1: You can create integers from strings, and Swift will return nil if the conversion failed.

Hint #2: The number “9223372036854775808” is a precise choice, not just a random string of 
numbers.

Hint #3: Chances are Swift’s integer type will be 64-bit for you, and it’s signed, which means 
its maximum value is 2 to the power of 63, minus 1, i.e. 9223372036854775807 – that’s one 
less than the test case you’ve been given.

Hint #4: You should look into inverted character sets.

Hint #5: Some languages write numbers differently from English.

Solution
There are lots of ways of solving this challenge, but if you want to solve it clearly, concisely, 
and correctly then your options are more limited.

There are two big gotchas when dealing with numbers. First, integers have a ceiling, beyond 

www.hackingwithswift.com 73



which they refuse to work. In Swift, the ceiling is 9,223,372,036,854,775,807, which is the 
largest number that can be represented by a signed 64-bit integer.

The third test case I gave you was one higher than the maximum signed 64-bit integer, which 
was intentional. However, if you recognized that, you could have switched an unsigned integer 
and passed the challenge by writing code like this:

func challenge23a(input: String) -> Bool {
   return UInt(input) != nil
}

I can’t think of a faster, simpler way to solve this challenge. However, it’s also a bit of a fudge 
– all unsigned integers do is double the largest number you can address, so it would still fail if 
you added a 0 to the end of the existing big number.

From there you might have migrated to using Int() to compare each individual letter in the 
string, like this:

func challenge23b(input: String) -> Bool {
   for letter in input {
      if Int(String(letter)) == nil {
         return false
      }
   }

   return true
}

As you can see, you can create integers from strings, and strings from characters, but you can’t 
create integers from characters – d’oh. Still, though, this code runs fast: it bails out as soon as 
any non-matching letter is found, and only returns true if all digits converted to an integer 
successfully.

An alternative solution is to use the rangeOfCharacter(from:) method, which lets you 
provide a character set and returns the location – if any – of those characters in the search 

www.hackingwithswift.com 74



string. In our case we know the numbers we want (digits), so we just need to get the inverse of 
that set using something like this:

func challenge23c(input: String) -> Bool {
   return input.rangeOfCharacter(from: 
CharacterSet.decimalDigits.inverted) == nil
}

This solution is interesting because it highlights another curiosity of numbers: Apple, being the 
smart company it is, considers “decimal digits” to include numerals from other languages. I’m 
not sure how well this will print on your computer, but “٢” is the Arabic-Indic numeral 2.

If you use the decimalDigits character set, it will include Arabic-Indic numerals as well 
as the numbers 0 through 9. While none of the test cases used Arabic-Indic numerals, if you 
wanted to conform strictly to the requirements for this challenge then you could use something 
like this:

func challenge23d(input: String) -> Bool {
   return input.rangeOfCharacter(from: 
CharacterSet(charactersIn: "0123456789").inverted) == nil
}

Perhaps now you understand why I graded this challenge as tricky rather than easy!

www.hackingwithswift.com 75



Challenge 24: Add numbers inside a string
Difficulty: Tricky

Given a string that contains both letters and numbers, write a function that pulls out all the 
numbers then returns their sum.

Sample input and output
• The string “a1b2c3” should return 6 (1 + 2 + 3).
• The string “a10b20c30” should return 60 (10 + 20 + 30).
• The string “h8ers” should return “8”.

Hints
Hint #1: Creating an integer from a string returns Int? – nil if it was a number, or an Int 
otherwise.

Hint #2: Use the nil coalescing operator, ??, to strip out any unwanted optionality.

Hint #3: Don’t forget to catch trailing numbers, i.e. where the string ends with a number.

Hint #4: You could solve this using regular expressions, in which case I’d grade it as taxing 
rather than tricky.

Solution
This is a personal favorite problem of mine – not because of its complexity, but more because 
of its usefulness for weeding out developers who have exaggerated a little on their résumé.

You see, you can take a naïve approach to this challenge and get a solution that works 
efficiently with very little code. However, to do that you need to know how to convert 
characters to strings, and strings to integers, as well as how to use nil coalescing if you want to 
make the code neat. As a result, someone who has puffed up their résumé will find this harder 

www.hackingwithswift.com 76



than it ought to be, and their code will usually show them up.

On the flip side, an experienced Swift developer who is out to impress might try and solve this 
using regular expressions, in which case now they have two challenges: summing the numbers, 
and making regular expressions in Swift not suck.

Let’s take a look at the simple solution first, which is hopefully similar to the one you came up 
with. The algorithm is this:

1. Create an empty string that represents the current number being read.
2. Create a sum value that contains the total of all numbers so far, initialized to 0.
3. Loop through every letter in the input string, converting the character to a String.
4. If we can convert that string to an integer, add it to the current number string.
5. Otherwise it’s not a number, so convert the current number string to an integer, or 0 if 

it’s an invalid integer, add it to the sum and clear the current number string.
6. Finally, convert any remaining value in the current number string to an integer, and add 

it to sum.
7. Return sum.

Here’s that list translated into code:

func challenge24a(string: String) -> Int {
   var currentNumber = ""
   var sum = 0

   for letter in string {
      let strLetter = String(letter)

      if Int(strLetter) != nil {
         currentNumber += strLetter
      } else {
         sum += Int(currentNumber) ?? 0
         currentNumber = ""
      }
   }

www.hackingwithswift.com 77



   sum += Int(currentNumber) ?? 0
   return sum
}

Solving the same problem using regular expressions is a real nightmare, and highlights the 
worst of Swift’s string handling problems. You see, creating a regex requires a Swift string 
going in, but works entirely using NSRange rather than Swift’s string ranges. This means you 
need to use string.utf16.count to calculate the size of the range, and using 
string.count will introduce subtle bugs in your code.

However, it gets really grim when trying to read the contents of each match. 
NSRegularExpression returns an array of NSTextCheckingResult, which contains 
the NSRange of each match but not the contents, so you need to read the substring using that 
range… which isn’t possible in Swift, because it uses string ranges rather than NSRange. 
*Sigh*…

Anyway, if you want to prove you have more time than sense, here’s how to solve it using 
regular expressions:

1. Create the regex (\\d+).
2. Use its matches() method to pull out an array of NSTextCheckingResult 

objects.
3. Typecast your input string as NSString to get a substring() method that works 

with the NSRange provided by each NSTextCheckingResult.
4. Send that substring into Int() to get an optional integer.
5. Strip out the optionality, then sum the integers.

You can use flatMap() and reduce() for steps 4 and 5 if you really have something to 
prove, giving code like this:

func challenge24b(string: String) -> Int {
   let regex = try! NSRegularExpression(pattern: "(\\d+)", 
options: [])
   let matches = regex.matches(in: string, options: [], range: 

www.hackingwithswift.com 78



NSRange(location: 0, length: string.utf16.count))

   let allNumbers = matches.flatMap { Int((string as 
NSString).substring(with: $0.range)) }

   return allNumbers.reduce(0, +)
}

That code runs significantly slower than the previous solution, largely because of the cost of 
creating the regex. If you were to run this method many times you should move the 
NSRegularExpression creation outside the method, at which point it runs “only” about 
half the speed of the previous solution.

www.hackingwithswift.com 79



Challenge 25: Calculate a square root by hand
Difficulty: Taxing

Write a function that returns the square root of a positive integer, rounded down to the nearest 
integer, without using sqrt().

Sample input and output
• The number 9 should return 3.
• The number 16777216 should return 4096.
• The number 16 should return 4.
• The number 15 should return 3.

Hints
Hint #1: You can brute force this using a loop count from 0 up to the input number.

Hint #2: A more efficient solution is using a binary search.

Hint #3: A rounded-down integer square root will never be more than half its square.

Hint #4: If you consider half your input number to be your upper bound, then calculate the 
mid-point between that and a lower bound that’s initially 0 (i.e., input number / 4), you can 
check whether that mid-point squared gives your input.

Hint #5: If the mid-point is too low, make it the new lower bound then repeat. If the mid-point 
is too high, make it the new higher bound then repeat.

Hint #6: Using this technique, you should be able to loop until you find the best answer.

Solution
Like most coding interview problems, this one has a naïve solution, a smart solution, and a 
sneaky solution.

www.hackingwithswift.com 80



sneaky solution.

The naïve solution is trivial: loop from 1 up to one higher than half the test number, checking 
to see whether that number squared is greater than the test number. If it is, return the number 
directly below. We need to add a special case for 1, because otherwise the code returns 0.

Here’s the code:

func challenge25a(input: Int) -> Int {
   guard input != 1 else { return 1 }

   for i in 0 ... input / 2 + 1 {
      if i * i > input {
         return i - 1
      }
   }

   return 0
}

However, consider the second test case, which is calculating the square root of 16,777,216. 
That’s going to require 4097 multiplications before returning the correct response, which is 
massively wasteful.

A smarter solution is to use a binary search, which massively reduces the search space. Given 
the input number 9 it works like this:

• Start by specifying a high bound of half the input number plus one, rounding down so 
for the input number 10 that gives us a high bound of 6.

• Now specify a low bound, which will be zero to begin with.
• Calculate the mid-point of the two, which is equal to half of upper - lower, plus 

the lower. So that’s lower + ((upper - lower) / 2), which is 0 + ((6 - 
0) / 2), which is 0 + (6 / 2), which is 0 + 3 – so our mid-point is 3.

• You then square your mid-point (3 x 3 is 9) and compare it against the input number.
• If the square is less than the input it means that all the numbers from your low bound 

up to the mid-point are also too low, and so don’t need to be checked, so you can set 

www.hackingwithswift.com 81



the new low bound to be equal to your mid-point, then repeat.
• If the square is higher than the input it means that all the numbers from the mid-point 

up to the upper bound are also too high, and so don’t need need to be checked, so you 
can set the new high bound to be equal to your mid-point, then repeat.

• If the square is equal to the input, then you have your answer.
• If you find that your lower bound + 1 is greater than or equal to your upper bound it 

means you’ve overshot the mark, so you should return the lower bound.

I realize that sounds complicated, but it’s actually remarkably simple: “select the range it must 
be in, then try the middle of it. If you were too high you can eliminate the upper half of the 
range; if you were too low you can eliminate the lower half of the range. So, eliminate one half 
of the range then split the remaining half… and repeat.”

This search technique is sometimes called a binary chop, because you halve your search space 
with each check. So to get 16,777,216 you halve to a range of 8,388,608, then halve that to a 
range of 4,194,304, then halve to a range of 2,097,152, then 1,048,576, then 524,288, then 
262,144, then 131,072, then 65,536, and so on.

Using this approach takes only 11 loops to figure out the square root of 16,777,216, compared 
to 4097 loops using the naïve method, so it runs a great deal faster. Nice! Here’s the code:

func challenge19b(input: Int) -> Int {
   guard input != 1 else { return 1 }

   var lowerBound = 0
   var upperBound = 1 + input / 2

   while lowerBound + 1 < upperBound {
      let middle = lowerBound + ((upperBound - lowerBound) / 2)
      let middleSquared = middle * middle

      if middleSquared == input {
         return middle
      } else if middleSquared < input {

www.hackingwithswift.com 82



         lowerBound = middle
      } else {
         upperBound = middle
      }
   }

   return lowerBound
}

So, that’s the naïve approach and the smart approach, but there’s also a sneaky approach: the 
challenge is to calculate the square root of an integer without using sqrt(), but it didn’t say 
not to use the pow() function. If you request a number raised to the power of 0.5, you get its 
square root. The calculation isn’t precisely the same – a true square root will yield ever so 
slightly different results, and will be optimized for that task – but given that we’re working 
with integers the two will be identical.

So, here’s how to solve the challenge using pow(), which will run so fast it’s hard to 
benchmark meaningfully:

func challenge25c(input: Int) -> Int {
   return Int(floor(pow(Double(input), 0.5)))
}

Note the extensive typecasting, which is unavoidable I’m afraid – pow() works with doubles, 
not integers, so we need to typecast and floor before converting to an integer for the return 
value.

www.hackingwithswift.com 83



Challenge 26: Subtract without subtract
Difficulty: Taxing

Create a function that subtracts one positive integer from another, without using -.

Sample input and output
• The code challenge26(subtract: 5, from: 9) should return 4.
• The code challenge26(subtract: 10, from: 30) should return 20.

Hints
Hint #1: In your code you can use any other operator, or any other number, positive or 
negative, just not the - operator.

Hint #2: Swift has a full set of bitwise operators – operators that manipulate the binary digits 
of a number.

Hint #3: You could try using bitwise NOT, which is ~.

Solution
This question is looking for a basic grasp of mathematics: if you can’t subtract one number 
from another using -, how can you do it? The answer is, of course, to flipping the sign on the 
number then adding it – i.e., rather than subtracting 10, you add -10.

You could make an argument that flipping the sign on a number using - is different to using - 
for subtraction. That would give you a solution like this:

func challenge26a(subtract: Int, from: Int) -> Int {
   return from + -subtract
}

Technically, though, - in that instance is the unary minus operator, so it’s the same thing, 

www.hackingwithswift.com 84



really.

A solution that fits the spirit of the challenge a bit better is multiplying by -1, like this:

func challenge26b(subtract: Int, from: Int) -> Int {
   return from + -1 * subtract
}

That’s a negative number, not the unary minus operator, so that fits both the letter and spirit of 
the challenge.

However, for maximum effect, you can solve this challenge without typing - at all – well, 
excluding the -> used in declaring the function’s return value.

This technique depends on the ~ operator (a tilde), which is bitwise NOT. It causes all the 
binary digits in a number to be flipped. If this is not new to you, skip ahead – I’m going to take 
a brief tangent into what it does behind the scenes.

8-bit integers are stored using eight binary digits, where the right-most digit stores 1, the 
second right-most stores 2, then 4, 8, 16, 32, and 64. Depending on whether each of those 
binary digits are 1 or 0, Swift can represent numbers from 0 (all zeros) to 127 (all 1s). 
However, notice there are only seven of them – 1, 2, 4, 8, 16, 32, and 64 – and we’re talking 
about 8-bit integers, so there ought to be 8, right? Right.

That eighth digit is how we track whether a number is positive or negative. If the left-most 
digit is 0, all the digits on the right add up to a positive number. If the left-most digit is 1, all 
the digits on the right represent a negative number.

Here are some examples of positive numbers:

• 00000000 is 0
• 00000001 is 1
• 00000010 is 2
• 00000011 is 3
• 00000100 is 4
• 00000101 is 5

www.hackingwithswift.com 85



• 01000000 is 64
• 01111111 is 127

So, as long as that first digit is 0, the rest of the digits form a positive number. 

Things are a little tricksier when you use negative numbers. Whereas the highest positive 
number starts with a zero and is followed by all ones, the lowest negative number starts with a 
one and is followed by all zeros – it’s the exact opposite.

So:

• 00000000 is still 0
• 10000000 is -128
• 10000001 is -127
• 10000010 is -126
• 10000011 is -125
• 10000100 is -124
• 10000101 is -123
• 11000000 is -64
• 11111111 is -1

Now, all this becomes important when you flip the 1s and 0s. For example, 01000000 is 64, 
but if you make the 0s into 1s and the 1s into 0s you get 10111111, which is -65. Similarly, if 
you take 01010101, which is 85, and flip the bits, you get 10101010, which is -86.

Both times the negative number is identical to add one to the number and flipping its sign, i.e. 
making positive negative. And that’s where our solution comes in – check this out:

func challenge26c(subtract: Int, from: Int) -> Int {
   return from + (~subtract + 1)
}

So, to subtract one number from another, we flip the bits (64 becomes -65) then add one (to 
make -64), and add that to our input number to make subtraction. Done!

www.hackingwithswift.com 86



Chapter 3
Files

www.hackingwithswift.com 87



Challenge 27: Print last lines
Difficulty: Easy

Write a function that accepts a filename on disk, then prints its last N lines in reverse order, all 
on a single line separated by commas.

Sample input and output
Here is your test input file:

Antony And Cleopatra
Coriolanus
Cymbeline
Hamlet
Julius Caesar
King Lear
Macbeth
Othello
Twelfth Night

• If asked to print the last 3 lines, your code should output “Twelfth Night, Othello, 
Macbeth”.

• If asked to print the last 100 lines, your code should output “Twelfth Night, Othello, 
Macbeth, King Lear, Julius Caesar, Hamlet, Cymbeline, Coriolanus, Antony and 
Cleopatra”.

• If asked to print the last 0 lines, your could should print nothing.

Hints
Hint #1: Use the contentsOfFile initializer to pull in the text, then 
components(separatedBy:) to create an array of lines.

Hint #2: Arrays have a built-in reverse() method that flip them around in-place.

www.hackingwithswift.com 88



Hint #3: You need to print the last N lines, but of course you don’t want to read beyond the 
size of the array. Make sure you use the min() function to choose the lesser of the two.

Solution
This is a nice and simple challenge, and is the kind of thing you’ll hit in real-world coding all 
the time. Hopefully, then, you found it a breeze: use contentsOfFile to get a multiline 
String of text, use components(separatedBy: "\n") to split it into an array of 
lines, then reverse the array and loop over N items.

In order to fully satisfy the challenge’s requirements – and to add safety to make sure we don’t 
read outside the array! – we also need to add a couple of guard statements to eliminate bad 
input, and use min() to pick the lowest number between the requested line count and the 
number of lines in the array.

Here’s the code:

func challenge27(filename: String, lineCount: Int) {
   guard let input = try? String(contentsOfFile: filename) else 
{ return }

   var lines = input.components(separatedBy: "\n")
   guard lines.count > 0 else { return }

   lines.reverse()

   for i in 0 ..< min(lines.count, lineCount) {
      print(lines[i])
   }
}

www.hackingwithswift.com 89



Challenge 28: Log a message
Difficulty: Easy

Write a logging function that accepts accepts a path to a log file on disk as well as a new log 
message. Your function should open the log file (or create it if it does not already exist), then 
append the new message to the log along with the current time and date. 

Tip: It’s important that you add line breaks along with each message, otherwise the log will 
just become jumbled.

Sample input and output
• If the file does not exist, running your function should create it and save the new 

message.
• If it does exist, running your function should load the existing content and append the 

message to the end, along with suitable line breaking.

Hints
Hint #1: I think it would be reasonable to use contentsOfFile to load the existing log file 
into a string.

Hint #2: You can use try? and nil coalescing to provide a default value if the existing log 
file doesn’t exist.

Hint #3: How you format the date and time for each message will be interesting, but don’t 
forget KISS: Keep It Simple, Stupid.

Hint #4: What should happen if you can’t write the log file? This wasn’t specified in the 
challenge description, so you get to use some initiative.

Solution

www.hackingwithswift.com 90



This is a small and contained challenge, but gives you just enough scope to demonstrate your 
Swift skills.

Specifically, there are three areas where your solution is open for discussion:

1. How you load the log file, or provide a default value if the log hasn’t been created yet.
2. How you format dates to include in each log message.
3. How you write the updated log file back to disk, handling any errors that might occur.

The easiest way to solve the first is using one of my favorite Swift tips: combining try? with 
nil coalescing to provide a default value in cases where a thrown exception just means 
“missing value”. In code it looks like this:

var existingLog = (try? String(contentsOfFile: logFile)) ?? ""

After that line has run, existingLog will either be an empty string or the contents of the log 
file.

There are lots of ways to solve the second, but honestly the smartest way is also the easiest: 
just using Date() inside string interpolation. That will cause the current date and time to be 
printed out as “Year-Month-Day Hour:Minute:Second”, which is ideal. So, you would write 
this:

existingLog.append("\(Date()): \(message)\n")

Finally, writing data to disk is a throwing function, so you need to decide what to do with any 
errors. You could ignore them, like this:

_ = try? existingLog.write(toFile: logFile, atomically: true, 
encoding: .utf8)

…but that’s probably not smart: failure to write log messages seems serious to me, so at the 
very least you will want to print a warning, like this:

do {
   try existingLog.write(toFile: logFile, atomically: true, 

www.hackingwithswift.com 91



encoding: .utf8)
} catch {
   print("Failed to write to log: \
(error.localizedDescription)")
}

Another reasonable approach would be to do without do/catch, just use try by itself, then 
mark the whole function with throws and let the call site deal with it. Regardless of what you 
choose, it leaves some scope for interesting discussion, which is always a good thing.

Here’s my complete solution to this challenge:

func challenge28(log message: String, to logFile: String) {
   var existingLog = (try? String(contentsOfFile: logFile)) ?? 
""

   existingLog.append("\(Date()): \(message)\n")

   do {
      try existingLog.write(toFile: logFile, atomically: true, 
encoding: .utf8)
   } catch {
      print("Failed to write to log: \
(error.localizedDescription)")
   }
}

www.hackingwithswift.com 92



Challenge 29: Documents directory
Difficulty: Easy

Write a function that returns a URL to the user’s documents directory.

Sample input and output
• Your function should need no input, and return a URL pointing to /Users/

yourUserName/Documents on macOS, and /path/to/container/Documents on iOS.

Hints
Hint #1: This is one you either know or you don’t. I’d be tempted to answer “that’s something 
I could find on Google” if the answer fled from my brain in an interview.

Hint #2: You should investigate the urls(for:in) method of FileManager.

Hint #3: The user has only one documents directory.

Solution
This is the kind of question that gives coding interviews a bad rap: there’s no logic that you 
can figure out yourself, or any algorithm you might have learned in a class once. Instead, you 
either know the answer or you don’t, which is testing nothing other than your ability to 
memorize vast swathes of Cocoa Touch.

There are three reasons I’m including it here. First, if you didn’t know how to do this, you do 
know now so you’ve learned something new. Second, it’s actually pretty easy to do once you 
know how, so you might as well get it under your belt in case you get hit with this question in 
the future. Third, this is useful code to have at hand: reading and writing to the user’s 
documents directory is very common on iOS, so you’ll almost certainly use this code in real 
projects.

www.hackingwithswift.com 93



Here’s my solution:

func challenge29() -> URL {
   let paths = 
FileManager.default.urls(for: .documentDirectory, 
in: .userDomainMask)
   return paths[0]
}

Like I said in the hints, though: if you get asked this during an interview, I’d be tempted to 
answer that it’s something trivial enough to be looked up online. I’m no fan of coding by Stack 
Overflow, and if someone said “I don’t know much about UITableView but I could look it 
up” then I’d not think highly of their skills, but this particular problem is something so simple 
that it really is just a Google search away if you need it.

www.hackingwithswift.com 94



Challenge 30: New JPEGs
Difficulty: Easy

Write a function that accepts a path to a directory and returns an array of all JPEGs that have 
been created in the last 48 hours.

Tip #1: For the purpose of this task, just looking fo