In the 2014, Apple released a new programming language, called Swift. Swift has been designed from scratch with many powerful features. It is statically typed and very safe. It has a clean and nice syntax, it's fast, it's flexible, and it has many other advantages that you will learn later in the book. Swift seems to be very powerful and it has big potential. Apple has set big expectations for Swift, and their main goal for Swift is that it should be a replacement for Objective-C, which is going to happen in the near future.
In this chapter, you will become familiar with the Swift programming language, what it was made for, and what its advantages and features are. We will also make our first Swift application and see how easy it is to integrate with existing Objective-C code.
In this chapter, we will cover the following topics:
Welcome to Swift
Writing swift code
Swift interoperability
The importance of performance and performance key metrics
I can guess you opened this book because you are interested in speed and are probably wondering, "How fast can Swift be?" Before you even start learning Swift and discovering all the good things about it, let's answer it right here and right now.
Let's take an array of 100,000 random numbers; sort it in Swift, Objective-C, and C using the standard sort
function from stdlib
(sort
in Swift, qsort
in C, and compare
in Objective-C); and measure how much time each would take.
Sorting an array with 100,000 integer elements gives us this:
Technology |
Time taken |
---|---|
Swift |
0.00600 sec |
C |
0.01396 sec |
Objective-C |
0.08705 sec |
And the winner is, Swift! Swift is 14.5 times faster than Objective-C and 2.3 times faster than C.
In other examples and experiments, C is usually faster than Swift and Swift is way faster than Objective-C. These measurements were done with Xcode 7.0 beta 6 and Swift 2.0. It's important to highlight that the improvements in Swift 2.0 were mainly focused on making it cleaner, more powerful, safer, and more stable, and preparing it for open sourcing. Swift's performance hasn't reached its full potential yet, and the future is so exciting!
The Swift programming language has been designed by Apple from the ground up. It was released with the slogan Objective-C without the C. The meaning of this phrase is that Swift doesn't have any limitation of backward compatibilities. It's totally new and with no old baggage. Before you start learning all the power of Swift, I think it would be useful to answer a few questions about why should you learn it, and if you have any doubts about that, I should dispel them.
Swift is a very new programming language but it has become very popular and has gained huge traction. However, many iOS and OS X developers ask these questions:
Should I learn Swift?
What should I learn, Swift or Objective-C?
Is Objective-C going to stay or die?
Is Swift ready for production apps?
Is Swift faster than Objective-C or C?
What applications can I write using Swift?
My answer is, "Yes. Definitely!" You should learn Swift. It doesn't matter whether you are a new iOS and OS X developer or you have some Objective-C background; you should definitely learn Swift.
If you are new developer, then it's really useful to start with Swift, because you will learn programming basics and techniques in Swift, and further Swift learning would be much easier. Although it would definitely useful to learn Objective-C as well, I would recommend learning Swift first so that you build your programming mindset on Swift.
If you already have some experience in Objective-C, then you should try Swift as soon as possible. It will not only give you the knowledge of a new programming language, but also open the door to new ideas and ways of solving problems in Objective-C. We can see that Objective-C has started evolving right now because of Swift.
Objective-C has many limitations because of its backward capabilities with C. It was created 23 years ago, in 1983, but it will die much sooner than Swift.
After the release of Swift version 1.0, in only a year's time we have seen many Swift applications successfully developed and released on the App Store. In this time period, many Swift tools and open source libraries that increase development productivity have been created.
During WWDC 2015, Apple announced that Swift will be made open source. This means that Swift can be used to write any software and not only iOS or OS X apps. You can write a piece of server-side code or web app in Swift. This is one more reason you should learn it.
On the other hand, we see that Swift is under constant development. There were many changes and improvements in version 1.2, and there were even more changes in version 2.0. Although it's very easy to upgrade to the newer Swift version with the Xcode migrator, it's something you should think about.
Swift has some promising performance characteristics. We have seen a huge performance improvement in the Swift 1.2 release, and some improvements in Swift 2.0 as well. You have seen from the previous example how fast Swift is, and in general, Swift has more potential to achieve high performance than Objective-C.
Finally, I want to mention a phrase I really like, by Bryan Irace:
When the iOS SDK says "Jump", ask "How High?"
Don't wait, learn Swift!
At this point, you know that you should learn Swift, and you shouldn't have any doubts. Let's take a look what makes Swift so amazing and powerful. Here is a list of a few important features that we are going to cover:
Powerful features and performance are important, but I think that cleanness and beauty are no less important. You write and read code everyday, and it has to be clean and beautiful so that you can enjoy it. Swift is very clean and beautiful, and the following are the main features that make it so.
Semicolons were created for the compiler. They help the compiler understand the source code and split it into commands and instructions. But the source code is written for people, and we should probably get rid of the compiler instructions from it:
var number = 10 number + 5 // Not recommended var count = 1; var age = 18; age++
There is no need for a semicolon (;) at the end of every instruction. It may seem like a very small feature, but it makes code so much nicer and easier to write and read. You can, however, put semicolons if you want. A semicolon is required when you have two instructions on the same line. There are also some exceptions when you have to use semicolons, a for
loop as an example (for var i = 0; i < 10; i++
), but in that context, they are used for a different purpose.
With type inference, you don't need to specify the types of variables and constants. Swift automatically detects the correct type from the context. Sometimes, however, you have to specify the type explicitly and provide type annotation. When there is no value assigned to the variable, Swift can't predict what type that variable should be:
var count = 10 //count: Int var name = "Sara" //name: String var empty = name.isEmpty //empty: Bool // Not recommended var count: Int = 10 var name: String = "Sara" var empty: Bool = name.isEmpty // When you must provide type annotation var count: Int var name: String count = 10 name = "Sara"
In most cases, Swift can understand a variable's type from the value assigned to it.
The list of all of Swift's clean code features is very long; here are few of them: closure syntax, functions' default parameter values, functions' external parameter names, default initializers, subscripts, and operators:
Clean closure syntax: A closure is a standalone block of code that can be treated as a light unnamed function. It has the same functionality as a function but has a cleaner syntax. You can assign it to a variable, call it, or pass it as an argument to a function. For example,
{ $0 + 10 }
is a closure:let add10 = { $0 + 10 } add10(5) let numbers = [1, 2, 3, 4] numbers.map { $0 + 10 } numbers.map(add10)
Default parameter values and external names: While declaring a function, you can define default values for parameters and give them different external names, which are used when you call that function. With default parameters, you can define one function but call it with different arguments. This reduces the need for creating unnecessary functions:
func complexFunc (x: Int, _ y: Int = 0, extraNumber z: Int = 0, name: String = "default") -> String{ return "\(name): \(x) + \(y) + \(z) = \(x + y + z)" } complexFunc(10) complexFunc(10, 11) complexFunc(10, 11, extraNumber: 20, name: "name")
Default and memberwise initializers: Swift can create initializers for struct and base classes in some scenarios for you. Less code, better code:
struct Person { let name: String let lastName: String let age: Int } Person(name: "Jon", lastName: "Bosh", age: 23)
Subscripts: This is a nice way of accessing the member elements of a collection. You can use any type as a key:
let numbers = [1, 2, 3, 4] let num2 = numbers[2] let population = [ "China" : 1_370_940_000, "Australia" : 23_830_900 ] population["Australia"]
You can also define a subscript operator for your own types or extend existing types by adding own subscript operator to them in an extension:
// Custom subscript struct Stack { private var items: [Int] subscript (index: Int) -> Int { return items[index] } // Stack standard functions mutating func push(item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } } var stack = Stack(items: [10, 2]) stack.push(6) stack[2] stack.pop()
Operators: These are symbols that represent functionality, for example, the
+
operator. You can extend your types to support standard operators or create your own custom operators:let numbers = [10, 20] let array = [1, 2, 3] let res = array + numbers struct Vector { let x: Int let y: Int } func + (lhs: Vector, rhs: Vector) -> Vector { return Vector(x: lhs.x + rhs.x, y: lhs.y + rhs.y); } let a = Vector(x: 10, y: 5) let b = Vector(x: 2, y: 3) let c = a + b
guard: The
guard
statement is used to check whether a condition is met before continuing to execute the code. If the condition isn't met, it must exit the scope. Theguard
statement removes nested conditional statements and the Pyramid of Doom problem:Note
Read more about the Pyramid of Doom at https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming).
func doItGuard(x: Int?, y: Int) { guard let x = x else { return } //handle x print(x) guard y > 10 else { return } //handle y print(y) }
As you can see, Swift is very clean and nice. The best way to show how clean and beautiful Swift is is by trying to implement the same functionality in Swift and Objective-C.
Let's say we have a list of people and we need to find the people with a certain age criteria and make their names lowercase.
This is what the Swift version of this code will look like:
struct Person { let name: String let age: Int } let people = [ Person(name: "Sam", age: 10), Person(name: "Sara", age: 24), Person(name: "Ola", age: 42), Person(name: "Jon", age: 19) ] let kids = people.filter { person in person.age < 18 } let names = people.map { $0.name.lowercaseString }
The following is what the Objective-C version of this code will look like:
//Person.h File @import Foundation; @interface Person : NSObject @property (nonatomic) NSString *name; @property (nonatomic) NSInteger age; - (instancetype)initWithName:(NSString *)name age:(NSInteger)age; @end //Person.m File #import "Person.h" @implementation Person - (instancetype)initWithName:(NSString *)name age:(NSInteger)age { self = [super init]; if (!self) return nil; _name = name; _age = age; return self; } @end NSArray *people = @[ [[Person alloc] initWithName:@"Sam" age:10], [[Person alloc] initWithName:@"Sara" age:24], [[Person alloc] initWithName:@"Ola" age:42], [[Person alloc] initWithName:@"Jon" age:19] ]; NSArray *kids = [people filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"age < 18"]]; NSMutableArray *names = [NSMutableArray new]; for (Person *person in people) { [names addObject:person.name.lowercaseString]; }
The results are quite astonishing. The Swift code has 14 lines, whereas the Objective-C code has 40 lines, with .h
and .m
files. Now you see the difference.
Swift is a very safe programming language, and it does a lot of security checks at compile time. The goal is to catch as many issues as possible during compiling and not when you run an application.
Swift is a type-safe programming language. If you made any mistakes with a type, such as trying to add an Int
and a String
or passing the wrong argument to a function, you will get an error:
let number = 10 let part = 1.5 number + part; // Error let result = Double(number) + part
Swift doesn't do any typecasting for you; you have to do it explicitly, and this makes Swift even safer. In this example, we had to cast an Int
number to the Double
type before adding it.
A very important safe type that was introduced in Swift is an optional. An optional is a way of representing the absence of a value—nil
. You can't assign nil
to a variable with the String
type. Instead, you must declare that this variable can be nil
by making it the optional String?
type:
var name: String = "Sara" name = nil //Error. You can't assign nil to a non-optional type var maybeName: String? maybeName = "Sara" maybeName = nil // This is allowed now
To make a type an optional type, you must put a question mark (?
) after the type, for example, Int?
, String?
, and Person?
.
You can also declare an optional type using the Optional
keyword, Optional<String>
, but the shorter way with using ?
is preferred:
var someName: Optional<String>
Optionals are like a box that contains some value or nothing. Before using the value, you need to unwrap it first. This technique is called unwrapping optionals, or optional binding if you assign an unwrapped value to a constant:
if let name = maybeName { var res = "Name - " + name } else { print("No name") }
Swift 2.0 has powerful and very simple-to-use error handling. Its syntax is very similar to the exception handling syntax in other languages, but it works in a different way. It has the throw
, catch
, and try
keywords. Swift error handling consists of a few components, explained as follows:
An error object represents an error, and it must conform to the
ErrorType
protocol:enum MyErrors: ErrorType { case NotFound case BadInstruction }
Every function that can throw an error must be declared using the
throws
keyword after its parameters' list:func dangerous(x: Int) throws func dangerousIncrease(x: Int) throws -> Int
To throw an error, use the
throw
keyword:throw MyErrors.BadInstruction
When you are calling a function that can throw an error, you must use the
try
keyword. This indicates that a function can fail and further code will not be executed:try dangerous(10)
If an error occurs, it must be caught and handled with the
do
andtry
keywords or thrown further by declaring that function withthrows
:do { try dangerous(10) } catch { print("error") }
Let's take a look at a code example that shows how to work with exceptions in Swift:
enum Error: ErrorType { case NotNumber(String) case Empty } func increase(x: String) throws -> String { if x.isEmpty { throw Error.Empty } guard let num = Int(x) else { throw Error.NotNumber(x) } return String(num + 1) } do { try increase("10") try increase("Hi") } catch Error.Empty { print("Empty") } catch Error.NotNumber (let string) { print("\"\(string)\" is not a number") } catch { print(error) }
There are many other safety features in Swift:
Memory safety ensures that values are initialized before use.
Two-phase initialization process with security checks
Required method overriding and many others
Swift has the following powerful types:
Structures are flexible building blocks that can hold data and methods to manipulate that data. Structures are very similar to classes but they are value type:
struct Person { let name: String let lastName: String func fullName() -> String { return name + " " + lastName } } let sara = Person(name: "Sara", lastName: "Johan") sara.fullName()
Tuples are a way of grouping multiple values into one type. Values inside a tuple can have different types. Tuples are very useful for returning multiple values from a function. You can access values inside a tuple by either index or name if the tuple has named elements; or you can assign each item in the tuple to a constant or a variable:
let numbers = (1, 5.5) numbers.0 numbers.1 let result: (code: Int, message: String) = (404, "Not fount") result.code result.message let (code ,message) = (404, "Not fount")
Range represents a range of numbers from x to y. There are also two range operators that help create ranges: closed range operator and half-open range operator:
let range = Range(start: 0, end: 100) let ten = 1...10 //Closed range, include last value 10 let nine = 0..<10 //half-open, not include 10
Enumeration represents a group of common related values. An enumeration's member can be empty, have a raw value, or have an associated value of any type. Enumerations are first-class types; they can have methods, computed properties, initializer, and other features. They are great for type-safe coding:
enum Action: String { case TakePhoto case SendEmail case Delete } let sendEmail = Action.SendEmail sendEmail.rawValue //"SendEmail" let delete = Action(rawValue: "Delete")
There are two very powerful value types in Swift: struct
and enum
. Almost all types in the Swift standard library are implemented as immutable value types using struct
or enum
, for example, Range
, String
, Array
, Int
, Dictionary
, Optionals
, and others.
Value types have four big advantages over reference types, they are:
Immutable
Thread safe
Single owned
Allocated on the stack memory
Value types are immutable and only have a single owner. The value data is copied on assignment and when passing it as an argument to a function:
var str = "Hello" var str2 = str str += " :)"
Swift is a multiparadigm programming language. It supports many different programming styles, such as object-oriented, protocol-oriented, functional, generic, block-structured, imperative, and declarative programming. Let's take a look at a few of them in more detail here.
Swift supports the object-oriented programming style. It has classes with the single inheritance model, the ability to conform to protocols, access control, nested types and initializers, properties with observers, and other features of OOP.
The concept of protocols and protocol-oriented programming is not new, but Swift protocols have some powerful features that make them special. The general idea of protocol-oriented programming is to use protocols instead of types. In this way, we can create a very flexible system with weak binding to concrete types.
In Swift, you can extend protocols and provide a method's default implementation:
extension CollectionType { func findFirst (find: (Self.Generator.Element) -> Bool) -> Self.Generator.Element? { for x in self { if find(x) { return x } } return nil } }
Now, every type that implements CollectionType
has a findFirst
method:
let a = [1, 200, 400] let r = a.findFirst { $0 > 100 }
One big advantage of using protocol-oriented programming is that we can add methods to related types and use the dot (.
) syntax for method chaining instead of using free functions and passing arguments:
let ar = [1, 200, 400] //Old way map(filter(map(ar) { $0 * 2 }) { $0 > 50 }) { $0 + 10 } //New way ar.map{ $0 * 2 } .filter{ $0 > 50 } .map{ $0 + 10 }
Swift also supports the functional programming style. In functional languages, a function is a type and it is treated in the same way as other types, such as Int
; also, it is called a first class function. Functions can be assigned to a variable and passed as an argument to other functions. This really helps to decouple your code and makes it more reusable.
A great example is a filter
function of an array. It takes a function that performs the actual filtering logic, and it gives us so much flexibility:
// Array filter function from Swift standard library
func filter(includeElement: (T) -> Bool) -> [T]
let numbers = [1, 2, 4]
func isEven (x: Int) -> Bool {
return x % 2 == 0
}
let res = numbers.filter(isEven)
Swift has a very powerful feature called generics. Generics allow you to write generic code without mentioning a specific type that it should work with. Generics are very useful for building algorithms, reusable code, and frameworks. The best way to explain generics is by showing an example. Let's create a minimum
function that will return a smaller value:
func minimum(x: Int, _ y: Int) -> Int { return (x < y) ? x : y } minimum(10, 11) minimum(11,5, 14.3) // error
This function has a limitation; it will work only with integers. However, the logic of getting a smaller value is the same for all types—compare them and return the smaller value. This is very generic code.
Let's make our minimum
function generic and work with different types:
func minimum <T : Comparable>(x: T, _ y: T) -> T { return (x < y) ? x : y } minimum (10, 11) minimum (10.5, 1.4) minimum ("A", "ABC")
There are two main points that Apple thought of when introducing Swift:
The usage of the Cocoa framework and established Cocoa patterns
Easy to adopt and migrate
Apple understood that and took it very seriously while working on Swift. They made Swift work seamlessly with Objective-C and Cocoa. You can use all Objective-C code in Swift, and you can even use Swift in Objective-C.
It's very crucial to be able to use the Cocoa framework. All of the code that is written in Objective-C is available for use in Swift, both Apple frameworks and third-party libraries as well.
All the Cocoa frameworks written in Objective-C are available in Swift by default. You just need to import them and then use them. Swift doesn't have header files; instead, you need to use a module name. You can also include your own Swift frameworks in the same way:
import Foundation import UIKit import Alamofire // Custom framework
To include your own Objective-C source files, you need to do a small setup first. The process is a bit different for the application target and framework target. The main idea is the same—to import the Objective-C header files.
For the application target, you need to create a bridging header. A bridging header is a plain Objective-C header file in which you specify the Objective-C import
statements.
Xcode will show a popup, offering to create, and set up a bridging header for you when you add the Objective-C file to a Swift project, or vice versa for the first time. This is the best and the most convenient way to add it.

If you decline the Xcode help, you can create a bridging header yourself anytime. To do that, you need to follow these steps:
Add a new header file to the project.
Go to Target | Build Settings.
Search for
Objective-C Bridging Header
and specify the path to the bridging header file created in step 1.
Once you set up bridging header, the next step is to add import
statements to it:
Bridging.h
// // Use this file to import your target's public headers that you // would like to expose to Swift. #import "MyClass.h"
For the framework target, you simply need to import the .h
Objective-C header files to the framework's umbrella header. The Objective-C header files must be marked as public. The umbrella header is the header in which you specify your publicly available API. Usually, it looks like this—the ExampleFramework.h
umbrella header:
#import <UIKit/UIKit.h>
//! Project version number for MySwiftKit.
FOUNDATION_EXPORT double MySwiftKitVersionNumber;
//! Project version string for MySwiftKit.
FOUNDATION_EXPORT const unsigned char MySwiftKitVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <MySwiftKit/PublicHeader.h>
#import <SimpleFramework/MyClass.h>
Once you are done with the setup, you can use all Objective-C APIs in Swift. You can create instances, call methods, inherit from Objective-C classes, conform to protocols, and do other things that you can do in Objective-C. In this example, we will use the Foundation
classes, but the rules are the same for third-party code as well:
import UIKit import Foundation let date = NSDate() date.timeIntervalSinceNow UIColor.blackColor() UIColor(red: 0.5, green: 1, blue: 1, alpha: 1) class MyView: UIView { //custom implementation }
Tip
Inherit from Objective-C classes only if you need it. This can have a negative impact on performance.
There is free bridging between Swift types and Objective-C Foundation types. Automatic bridging happens on assignment and when you pass it as an argument to a function:
let array = [1, 2, 3] func takeArray(array: NSArray) { } var objcArray: NSArray = array takeArray(array)
Converting from Objective-C to a Swift type requires explicit type casting. There are two types of casting: downcasting and upcasting. Casting is usually an unsafe operation, which could fail, and that's why it returns an optional type:
//Upcasting or safe casting let otherArray: [AnyObject] = objcArray as [AnyObject] //Downcasting, unsafe casting if let safeNums = objcArray as? [Int] { safeNums[0] + 10 //11 } let string: NSString = "Hi" let str: String = string as String
The String
type has gone one step even further. You can invoke the Objective-C foundation methods on the Swift String
type without any type casting:
var name: String = "Name" name.stringByAppendingString(": Sara")
Swift made a small improvement to Objective-C code so that it looks more Swift-style. The biggest change is made to instance creation and the style of the initialization code. The init
, the initWith
, and other factory methods are transformed into Swift initializers:
//Objective-C - (instancetype)initWithFrame:(CGRect)frame; + (UIColor *)colorWithWhite:(CGFloat)white alpha:(CGFloat)alpha; // Swift init(frame: CGRect) init(white: CGFloat, alpha: CGFloat)
The other change is made to NS_ENUM
and NS_OPTIONS
. They become native Swift types: enum
and RawOptionSetType
.
As you can see, the API looks a bit different. Because Swift strives for cleanliness, it removes word duplications from the API nomenclature. The other method calls, properties, and names, are the same as they were in Objective-C, so it should be easy to find and understand them.
What is happening behind the scenes is that Swift is generating special interface files to interact with Objective-C. You can see these Swift interface files by holding down the command key and clicking on the type, NSDate
and UIColor
in our example.
It is also possible to use Swift in Objective-C. It makes Swift very easy to adapt to an existing project. You can start by adding one Swift file, and move more functionality to Swift over time.
The setup process is much easier than that for including Objective-C in Swift. All you need to do is import Swift's autogenerated header to Objective-C. The naming convention of the files for application targets is ProductModuleName + -Swift.h
, and for frameworks, it is <ProductName/ProductModuleName + -Swift.h>
.
Take a look at the following examples:
#import "SwiftApp-Swift.h" #import <MySwiftKit/MySwiftKit-Swift.h>
You can inspect the content of that autogenerated file by holding down the command key and clicking on it. By default, Swift classes aren't exposed for use in Objective-C. There are two ways of making Swift classes available in Objective-C:
Mark the Swift class, protocol, or enumeration with the
@objc
attribute.You can mark classes, methods, protocols, and enumerations with the
@objc
attribute. The@objc
attribute also accepts the alternative name that is used for Objective-C. When you expose a Swift class by marking it with the@objc
attribute, it has to inherit from the Objective-C class, and the enumeration must have a rawInt
value:@objc(KOKPerson) class Person: NSObject { @objc(isMan) func man() -> Bool { ... } } @objc enum Options: Int { case One case Two }
Now, the
KOKPerson
class with theisMan
method is available for use in Objective-C.Inherit from an Objective-C class,
NSObject
for example:When you inherit from an Objective-C class, your Swift class automatically becomes available in Objective-C. You don't need to perform any extra steps in such cases. You can also mark it with the
@objc
attribute and provide an alternative name:class Person: NSObject { }
There are two key characteristics of code:
Making the code architecture very solid and stable is the most important task, but we shouldn't forget about making it fast as well. Achieving high performance can be a tricky and dangerous task. Here are a few things that you should keep in mind while working on performance improvement:
Don't optimize your code upfront
There are many articles about this topic, why it's dangerous, and why you shouldn't do it. Just don't do it, and as Donald Knut says:
"Premature optimization is the root of all evil"
Measure first
Firstly, don't optimize upfront, and secondly, measure first. Measure the code's performance characteristics and optimize only those parts that are slow. Almost 95 percent of code doesn't require performance optimization.
I totally agree with these points, but there is another type of performance optimization that we should think of upfront.
The small decisions that we make every day include the following:
What type should it be,
Int
orString
?Should I create a new class for a new functionality or add to an existing one?
Use an array? Or maybe a set?
It seems as if these don't have any impact on the application's performance, and in most cases, they don't. However, making the right decision not only improves an application's speed, but also makes it more stable. This gives higher performance in application development. The small changes that we make every day make a big impact at the end of the year.
High performance is very crucial. The performance of an app is directly related to user experience. Users want to get results immediately; they don't want to wait for the view to load, see a long Loading indicator, or see a lagging animation.
Every year, our computers and devices become more and more powerful, with more CPU speed, memory, storage, and storage speed. Performance problems could seem irrelevant because of this, but the software complexity increases as well. We have more complex data to store and process. We need to show animations and do a lot of other things.
The first way of solving a performance problem is by adding more power. We can add more servers to handle data, but we can't update our clients' PC and mobile devices. Also, adding more power doesn't solve the code performance issue itself, but just delays it for some time.
The second, and correct, solution is to remove the issue that causes the performance problem. For that, we need to identify the problem, the slow piece of the code, and improve it.
There are many things that impact an application's performance and user experience. We will cover the following key metrics:
Operations' performance speed
Memory usage
Disk space usage
The most important of these and the one that has the biggest impact is the operations' performance speed. It tells us how fast a particular task can be performed, for example, creating a new user, reading from a file, downloading an image, searching for a person with a particular name, and so on.
Swift is a powerful and fast programming language. In this chapter, you learned about many powerful features of Swift and how easy it is to start coding in Swift and integrate it into existing projects. We also covered why performance is important and what you should be thinking about when working with it.
In the next chapter, we will do more coding in Swift, and you will learn how to use all the features of Swift to make a good application architecture.