How does Xcode work when we build the iOS app project

Danny Santoso
CodeX
Published in
7 min readOct 18, 2022

--

Photo by Ross Sneddon on Unsplash

May of you has familiar with Xcode, usually, Xcode is used by many iOS developers to develop an iOS app, but have you been wondering what the process that Xcode has been executed from our source code to becoming the executable app (.ipa) ? in this article I will explain you in a concise way about how the Xcode processing our source code to become some executable app.

If you look at the Show the report navigator menu in Xcode, there is a process that is called build, if you tapped there it shows many processes from prepare build to build target of your project. in this article, I will only explain the phase of the build target process, especially in the process that is called compile and link, because it is a very basic concept if you want to migrate from the Xcode build system into your own build system.

Before we jump into the explanation of compile and link processs, usually programming language has 2 different types, the first one is interpreted programming language and the second one is compiled programming language.

The main idea of interpreted programming language literally is a programming language that is not conducting the compiling process, however, it conducts a process to interpret or translate the language.

Usually interpreted programming language will duplicate the source that will be run by the user. to run the source code, the user will need the interpreter to translate or interpret the code. The code and interpreter will be run concurrently. one of the example of the interpreter is a web browser.

Examples of the interpreter programming language are PHP, ruby, javascript, etc.

And, The main idea of Compiled programming language is a program that translates high-level or human-readable source code into machine code and it will be run by the CPU as an executable app.

Examples of the compiled programming language are swift, objective c, java, c++, etc.

And, Because Swift is a Compiled programming language no wonder why in Xcode when we build the iOS app project Xcode will need to compile and link the swift source code into the executable app so that the device can run the app (.ipa).

Same like the other programming language, swift has the same compiler and linker process with other programming language, but in swift it has some additional processes within it, and that is why it makes the swift compiler process is more complicated than the others. I won't discuss the detail in here because it will take a long read journey to explain about it.

So basically swift has 2 dimensions in their compiling process, that is front-end and back-end.

  • Front-end: in front-end swift does lexical analysis, parsing, and semantic analysis like common other programming languages, for the example in this process, the swift code will be taken by lexical analysis as text and token that will be passed to the parser to be converted into a structured representation of the program. and this representation also will be passed to the semantic analysis, after that it will be passed to the back-end process.
  • Back-end: Swift does some really smart things on the backend to handle the optimization and code generation phases. The Swift compiler uses LLVM for optimization and binary generation. When Swift code is parsed in the form of an Abstract Syntax Tree (AST) which is then through semantic analysis and converted into Swift Intermediate Language (SIL). This code goes through analysis and optimizer along with LLVM IR (Intermediate Representation). in here LLVM turned our original Swift code into assembly code and finally ended up in a binary file, and then it will be linked as an executable app.

Here is the concise diagram that I have created for the swift compiling and linking process.

In nutshell, basically, our file .swift will be translated into the assembly files by the LLVM in the swift compiler process, and finally become a binary file (.o), which is a binary file is used to create an executable app. and how to create the executable app from the binary file ? in here we need the linking process, so basically Linker is the computer system program that combines one or more object files into a single binary or executable. Imagine a puzzle, before you see the image of the puzzle, you need to combine each piece to the correct place until you see a whole image. In this case, you as the person who combines pieces of the puzzle that it’s mean you are the Linker, and each piece of the puzzle is the Object File.

I hope this explanation already explains the compile and link process in the swift programming language in very concisely, now let’s dirty our hands to compile and link our swift file.

You can try to follow these commands below to compile and link by using the example code in here.

or you can create a separate file for this example class.

  • Create a file with the name of Bob.swift with the following code.
  • Create a file with the name of John.swift with the following code
  • Create a file with the name of People.swift with the following code
  • Create a file with the name of main.swift with the following code

After you have created these files or downloaded the source code that I have shared. now let's try to compile all these files. you can try to use this command :

swiftc main.swift John.swift Bob.swift People.swift -o Executable.out

in this command, you will we need to write all our file swift within it, and define the executable name after -o, -o in here means output. so after you run this command the executable app will be named as Executable.out.

If you want to know all the processes behind this compile you can add -v (verbose) to know what the processes are inside.

as you can see, in the first and second line, the compiler defines the architecture target and swift version that want to use, and after that, in the red line that I have marked, it show the terminal show the command to compile main.swift, and in the blue line it shows the command to compile John.swift, and so on. until we have all the binary file and the terminal will show the command to link all these binary files.

Unfortunately by using this command, every time we change our code we need to compile all of these files again. so that’s why we can separate to compile each one by one as one module, so if there is any change in one file, we only compile one module where the change file resides.

this is also why, when you have a monolithic iOS app project that has a large file within it, and when you create a small change it will take a long time to compile. it because it will compile again all of the files inside your app project. the solution is you need to separate these files as a module.

below is the example if you want to separate of each compile.

  • Compile People.swift
swiftc -c People.swift

because People file/class doesn’t have any dependency at all, we can directly use this command.

  • Compile John.swift
swiftc -c John.swift People.swift -module-name MyApp

since the John.swift has a dependency with People.swift, we need to declare the dependency file in the command, and group them into one module.

the order file name in this command is matters, the first order is the swift file that will be translated to the object file and the rest is the file dependencies.

-module-name — is the Name of the module to build, to easier your understanding, you can assume this is a group of swift files in the same directory.

  • Compile Bob.swift
swiftc -c Bob.swift People.swift -module-name MyApp

same as the compile process of John.swift, since the Bob.swift has a dependency with People.swift, we need to declare the dependency file in the command, and group them into one module.

  • Compile main.swift
swiftc -c main.swift Bob.swift John.swift People.swift -module-name MyApp

because in the main class initialize John and Bob class that has a dependency on the People class, for this case we need to add John, Bob, and People class as a dependency to run the command.

After you get 4 object files which main.o, John.o, Bob.o, and People.o, we need to link them as one executable, because we can't run or see the program yet.

to link all of these binary files, you can use either ld or swiftc command.

if you want to use ld command it will be more complicated, you can see the example like the linker process in the picture above, it uses ld command to link all of the binary files. which is in here we need to declare many aspects from the architecture, toolchain path, swift path, and SDK path.

here is an example of how to link them to create an executable app using swiftc the command.

swiftc main.o People.o Bob.o John.o -o Executable.app

in the end, ld and swiftc command it will generate the same executable app with the name as you defined after -o argument. that is Executable.app

So, That is the process of the compile and linker process in the Xcode, which is you can improve the Xcode build system process with your own build system, or you can use Bazel build system that I will discuss in the next topic, or other build systems.

References:

apple/swift: The Swift Programming Language (github.com)

a brief of swift compiler (medium.com)

--

--

Danny Santoso
CodeX

not a wizard, nor a sage. only an apprentice who keeps intensifying his intelligence to form great magic.