We've got our hands on the project, so open it in Xcode.
What now?
Well, first things first - check out how it works as-is... Click RUN! Keep your fingers crossed and hope for the best. A usual simulator should come up and display the main view.
Oops… that didn't happen! Instead, we got this:

Let's look at the console:

That's called a crash!
That was unexpected. At this point, we can't even check what the app is about. But not to get discouraged - we are about to learn how to deal with it! 💪
Xcode is a sophisticated tool that's designed to assist developers with various tasks, including identifying what's wrong with the code.
Call stack
When an app crashes, Xcode presents us with additional information regarding the issue by displaying what’s called a Call-Stack in the console.
A call-stack is used to store the call sequence of all the functions of a program. This mechanism makes it possible to keep track of the functions that are called during the app execution, in order to be able to resume the order of instructions when a function is completed.
Let's use a small example to illustrate this:
Imagine that you have a function named
getTemperature
that returns the outside temperature.This function uses another
getTemperatureFromSensor
function, which allows you to query the thermometer.Finally, the function
getTemperatureFromSensor
uses a last function namedconvertFahrenheitToCelsius
that converts a temperature initially in degrees Fahrenheit to degrees Celsius.
The corresponding simplified code would look like this:
func getTemperature() -> Int {
return getTemperatureFromSensor()
}
func getTemperatureFromSensor() -> Int {
let fahrenheitTemperature = sensor.getTemperature()
return convertFarenheitToCelsius(farenheit: farenheitTemperature)
}
func convertFahrenheitToCelsius(fahrenheit: Int) -> Int{
return (fahrenheit - 32) * 5 / 9;
}
When we call the getTemperature
function, it is automatically added to the stack. The stack therefore contains:
Stack |
getTemperature |
Then, within the getTemperature
function, we call getTemperatureFromSensor
. It's also added to the stack. Now stack contains two items:
Stack |
getTemperatureFromSensor |
getTemperature |
And finally, when getTemperatureFromSensor
functions convertFahrenheitToCelsius
function, the 3rd item is added to the stack:
Stack |
convertFahrenheitToCelsius |
getTemperatureFromSensor |
getTemperature |
imagine a call-stack like a pile of paper documents on a desk. When we call a function, we add a document on top of the pile. When the function is completed, we remove the corresponding document from the pile! This ensures the correct order of instructions in the app.
The items constantly get added and removed from the stack. The items that got placed into the stack earlier are referred to as "lower layers" and the latest as "higher layers."
Analyzing the stack
When a crash happens, the first thing we need to do is analyze the call-stack. That's where the error that caused the crash is described.
Now let's look at our stack. It contains 46 calls from the lowest layers of our program to the highest layers. Here we need to identify the lines that correspond to our code and we can ignore the rest for the moment.
Let's highlight different parts of the output:

We can identify 3 groups here:
Stack |
Error details - an explanation of an error that caused a crash |
ViewController related functions that led to the crash |
The log of the beginning of the call-stack before the error causing view controller functions |
What interests us at the moment is the green section - a group of logs related to the ViewController functions that led to the crash:
7 TicTacToe 0x000000010676ba07_T09TicTacToe14ViewControllerC10clearBoardyyF+583 8 TicTacToe 0x00000001067683ac_T09TicTacToe14ViewControllerC11viewDidLoadyyF+108 9 TicTacToe 0x0000000106768444_T09TicTacToe14ViewControllerC11viewDidLoadyyFTo+36
| Slack ... || clearBoard || viewDidLoad || viewDidLoad || ... |
So the error occurred during execution of the clearBoard
method of the ViewController class.
So, what's wrong with this code?
To identify the details of the error, we need to refer to the note at the very top of the console, above the green section:
2018-04-23 11:02:42.533527+0200 TicTacToe[18950:953130] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSFrozenArrayM objectAtIndex:]: index 9 beyond bounds [0 .. 8]'
It seems to mean that we're trying to access an element in an array at index 9, but the largest available index is 8. We'll need to do a little investigation to see why this is so!
You may wonder, why did the app crash?
When you made errors previously, Xcode was able to notify you right away! This is true, but only for certain type of errors.
Compilation vs execution
There are 2 processes associated with preparation of an app:
compilation
and execution.
After the code is written in a programming language, it's then compiled into an executable version of an application and, finally, when application is run, it gets executed:
Writing code ➡️ Compiling into an executable ➡️ Executing
Compilation
What's compilation? 🤔
Compilation is the process of translating the code from the original programming language into machine code that a computer can understand. The compilation is performed by software that's called a compiler.
A result of the compilation process is a piece of code in executable format, called an executable. In order for it to be completed, we must resolve all the errors!
Execution
The final code after compilation is now in a machine-understandable format ready to be executed on a target platform - iOS in our case.
When is this code executed?
The executable is executed when we launch our app. On the iOS platform, it happens when we tap on the app icon on the home screen of a mobile device.
Let's illustrate the full process:

Great, but what's the point of compilation? Can't iOS just execute from the original language?
The compilation makes it possible to generate machine code - the most primitive commands that need to be followed. Which, in turn, guarantees the fastest execution.
Imagine if you were to cook a meal and needed to produce the most precise result in the fastest way possible. Think of a fast food restaurant where all the ingredients are measured and packaged in the most optimal way, versus a boutique restaurant where meal creators follow a much slower but more engaging and creative process.
Machines, like honey badgers, they don't care...

During the execution, new errors may occur that are not related to correctness of the code, therefore, could not be detected at compilation.
Two types of errors
Each phase of app processing comes with its own separate set of potential errors:
Compilation errors
and Run-time errors.
Compilation errors
Compilation errors can be detected by Xcode while generating an executable - during translation of your code from swift to machine code. In fact, Xcode does it in live mode, as you type, and shows you warnings and errors.
These errors can be syntactic - incorrect spelling - or semantic - such as use of forbidden words, non-interpretable code, incorrect types, etc.
The lines of code that correspond to compilation errors are highlighted in red.
Run-time errors
The runtime errors occur during the execution process - after the app is launched. They 'appear' only during the execution process, and there's no way to detect them during compilation.
When a runtime error occurs, it causes an application crash. This kind of error is typically related to the logic of the code, such as accessing data that doesn't exist or that exists in a different format, or performing an unsupported action, etc.
Some of this errors can be easily reproduced - they happen consistently. Others occur 'occasionally' - those are harder to fix as they are typically harder to reproduce. Occasional errors are usually related to specific conditions in the code. Here's an example of a ninja-error in pseudo-code:
if week-day run CORRECT CODE
else run INCORRECT CODE
In the example above, if you are working on your project only Monday to Friday and get all the crashes reported on weekends, it won't be evident at first why your app is crashing.
Once we manage to reproduce a crash, thankfully we have the details of a crash in console! Let's illustrate compilation and execution errors:

You already know how to handle compilation errors! And now you need to learn how to deal with run-time errors!
Let's Recap!
A call-stack stores the call sequence of methods (or functions) of a program.
When an application crashes, the call-stack and the error are displayed in the console. This information helps trace the problem.
There are 2 stages of code processing for an app - compilation and execution.
Compilation is translation of the code from a programming language to machine code.
Execution is a process of executing instructions of machine code generated after compilation.
There are two types of errors associated with the 2 processes: compilation errors and run-time errors.