More on this book
Kindle Notes & Highlights
Swift 3, which was released in 2016, was a major enhancement to the Swift language that was not source-compatible with previous releases of the Swift language.
One of the main goals of Swift 3 was to be source-compatible across all platforms, so the code that was written for one platform would be compatible with all other platforms. This means that the code we develop for macOS should work on Linux.
In September 2017, Swift 4 was released. One of the primary goals of the Swift 4 compiler is to be source-compatible with Swift 3. This will allow us to compile both Swift 3...
This highlight has been truncated due to consecutive passage length restrictions.
One of the original goals of Swift 4 was to stabilize the Swift Application Binary Interface (ABI). The main benefit of a stable ABI is to allow us to distribute frameworks in a binary format across multiple versions of Swift. If a stable ABI is in place, we would be able to build a framework with the Swift 4 compiler and have it work with applications that were written in future versions of Swift.
A stable ABI means that a library that is compiled for one version of Swift, let's say Swift 5, will theoretically work with future versions of Swift without having to be recompiled.
The development of Swift was started in 2010 by Chris Lattner.
There are a lot of similarities between Swift and Objective-C. Swift adopts the readability of Objective-C's named parameters and dynamic object model.
dynamic object model, we are referring to the ability for types to change at runtime.
Objective-C is a superset of C and provides object-oriented capabilities and a dynamic runtime to the C language.
For style purposes, it is strongly recommended that you do not use semicolons in your Swift code. If you are really set on using semicolons, be consistent and use them on every line of code; however, there is no warning if you forget them.
For style purposes, it is recommended that you do not include parentheses in your code unless you have multiple conditional statements on the same line. For readability purposes, it is good practice to put parentheses around the individual conditional statements that are on the same line.
These bugs could have also been prevented by other means, such as unit testing and code reviews, but requiring developers to use curly brackets, in my opinion, is a good security standard.
This is another safety feature built into Swift. It prevents the developer from forgetting the second equals sign (=) in a comparison statement.
Constants and variables associate an identifier (such as myName or currentTemperature) with a value of a particular type (such as the String or Integer type), where the identifier can be used to retrieve the value. The difference between a constant and a variable is that a variable can be updated or changed, while a constant cannot be changed once a value is assigned to it.
The use of constants is encouraged in Swift. If we do not expect or want a value to change, we should declare it as a constant. This adds a very important safety constraint to our code that ensures that the value never changes.
Type inference allows us to omit the variable type when the variable is defined with an initial value.
Unless there is a specific reason to define the size of an integer, I would recommend using the standard Int or UInt types.
One of the new features of Swift 5 is the isMultiple(of:) method, which was added to the integer type. This method allows us to check if one number is the multiple of another number.
We can also retrieve substrings and individual characters from our strings; however, when we retrieve a substring from a string, the substring is an instance of the Substring type and not the String type.
Tuples group multiple values into a single compound type. These values are not required to be of the same type.
Enumerations (also known as enums) are a special data type that enables us to group related types together and use them in a type-safe manner.
If integers are used for the raw values of an enumeration, then we do not have to assign a value to each member. If no value is present, the raw values will be auto-incremented.
It is very important to understand that nil in Swift is very different from nil in Objective- C or other C-based languages. In these languages, nil is a pointer to a non-existent object; however, in Swift a nil value is the absence of a value. This concept is very important to fully understand optionals in Swift.
The optional type is an enumeration with two possible values, None and Some(T), where T is the generic associated value of the appropriate type.
With optionals, Swift is able to detect problems such as this at compile-time and alert us before it becomes a runtime issue.
To unwrap or retrieve the value of an optional, we place an exclamation mark (!) after the variable name.
Optional binding is the recommended way to unwrap an optional. With optional binding, we perform a check to see whether the optional contains a valid value and, if so, unwrap it into a temporary variable or constant. This is all performed in one step.
Arrays store data in an ordered collection, sets are unordered collections of unique values, and dictionaries are unordered collections of key-value pairs.
Concurrency is the concept of multiple tasks starting, running, and completing within the same time period.
In fact, in order for tasks to be run simultaneously, our application needs to be running on a multicore or multiprocessor system.
Parallelism is the concept of two or more tasks running simultaneously.
In order to understand the difference between concurrency and parallelism, let's look at how a juggler juggles balls. If you watch a juggler, it seems they are catching and throwing multiple balls at any given time, however, a closer look reveals that they are, in fact, only catching and throwing one ball at a time. The other balls are in the air waiting to be caught and thrown. If we want to be able to catch and throw multiple balls simultaneously, we need to have multiple jugglers.
Traditionally, the way applications added concurrency was to create multiple threads;
The biggest problem with using threads was that our applications ran on a variety of systems (and processors), and in order to optimize our code, we needed to know how many cores/processors could be efficiently used at a given time, which is usually not known at the time of development.
An asynchronous function typically starts the long running task and then returns prior to the task's completion. Usually, this task runs in the background and uses a callback function (such as closure in Swift) when the task completes.
The queues manage these submitted tasks and execute them in a First-In, First-Out (FIFO) order. This ensures that the tasks are started in the order they were submitted.
The system can scale the number of threads dynamically, based on the overall available resources of the system and the current system conditions. This means that dispatch queues can manage the threads with greater efficiency than we could.
It is recommended that we use a reverse DNS naming convention. This parameter is optional and can be nil.
A concurrent queue will execute the tasks in a FIFO order; however, the tasks will execute concurrently and finish in any order.
This shorthand version is how we usually submit small code blocks to our queues. If we have larger tasks or tasks that we need to submit multiple times, we will generally want to create a closure and submit the closure to the queue as we showed in the first example.
A serial queue functions a little different than a concurrent queue. A serial queue will only execute one task at a time and will wait for one task to complete before starting the next one.
The main queue is automatically created for the main thread when the application starts. This main queue is a serial queue; therefore, items in this queue are executed one at a time, in the order that they were submitted. We will generally want to avoid using this queue unless we have a need to update the user interface from a background thread.
Operation queues are part of the Foundation framework and function like dispatch queues as they are a higher-level of abstraction over GCD.
An operation represents a single unit of work or task. The Operation type is an abstract class that provides a thread-safe structure for modeling the state, priority, and dependencies.
class must be subclassed
Apple provides a concrete implementation of the Operation type that we can use as-is for situations where it does not make sense to build a custom subclass. This subclass is BlockOperation.
More than one operation queue can exist at the same time, and, in fact, there is always at least one operation queue running. This operation queue is known as the main queue. The main queue is automatically created for the main thread when the application starts and is where all of the UI operations are performed.
The BlockOperation class is a concrete implementation of the Operation type that can manage the execution of one or more blocks. This class can be used to execute several tasks at once without the need to create separate operations for each task.
One of the differences between dispatch queues and operations is that, with dispatch queues, if resources are
available, the tasks are executed as they are added to the queue. With operations, the individual tasks are not executed until the operation itself is submitted to an operation queue. This allows us to initiate all of the operations into a single block operation prior to executing them.

