Swift 2 focused on improving the language itself, interacting with Objective-C, and improving the performance of compiled applications. New features of
Swift 2 are presented in 6 different areas:
- fundamental constructs of the language, such as enum , scoping (scope), argument syntax, etc.
- pattern matching
- availability check
- protocol extensions
- error handling
- interaction with objective-c
I will consider the new features of Swift 2, accompanying them with examples whose code is on
Github .
1. Fundamental language constructions
No more println ()
Usually we used the
println () function to print a message on the console. In the Swift 2 version, we will use only
print () . Apple combined the
println () and
print () functions into one. The
print () function, by default, prints your message with a newline character "\ n". If you want, you can output a line without a newline:
')

map , filter and company
The definition of these convenient functions and methods through the collections in Swift 1.2 was not completely consistent. In Swift 1.2, there was no default implementation of the
map method for the
CollectionType protocol, since the default implementation of the protocol was not possible and extensions were made only for classes. Partly for this reason,
map was defined as a method in the
Array class (which implements the
CollectionType protocol), and not defined in the
Set class (which also implements the
CollectionType protocol). In addition to this, the global
map function was disclaimed, which took an instance of
CollectionType as the first parameter. This created complete confusion.
// Swift 1.2 let a: [Int] = [1,2,3] // map Array let b = a.map{ $0 + 1 } // map map([1,2,3]) { $0 + 1 } let set = Set([1,2,3]) // , map Set set.map{ $0 + 1 } // map Set map(set) { $0 + 1 }
It turned out that, depending on the type of collection, either the global
map function is used, or the
map method of this collection. It looks inconsistent and poorly readable if a chain of transformations is used using the methods and functions of
map ,
filter and
reduce .
Now, in Swift 2, protocol extensions are allowed, therefore
map ,
filter & co are implemented at the protocol level for
CollectionType , as protocol extensions. Therefore, the same methods will operate on
Array ,
Set, or any other collection implementing the
CollectionType protocol.
// Swift 2 let a: [Int] = [1,2,3] let b = a.map{ $0 + 1 } let set = Set([1,2,3]) let anotherSet = set.map{ $0 + 1 } let sum = (1...100) .filter { $0 % 2 != 0 } .map { $0 * 2 } .reduce(0) { $0 + $1 } print(sum) // prints out 5000
We see in the example above that
filter now works on
Range . This did not work in the previous version, because, although
Range confirmed the
CollectionType protocol, the
filter method was not implemented. Now everywhere we have a much more understandable syntax of these methods for any collection.
Enum enums
In Swift 2,
enum has sufficient
reflection information to enable their printing.

The
print (an) clause will now print
Dragon correctly, although in the previous version of Swift, the output was completely non-informative
(Enum Value) .
Another improvement regarding
enum is that Swift now allows you to represent the associated values of various types in
enum . As an example, you can now legally present the
Either type:

Now
enum can be recursive, that is, we can build a tree using
enum . Let's look at this example:

We must use the
indirect keyword in front of
case Node . And it allowed us to create a tree:

Here's what it looks like:

Now we can create a function that recursively traverses the entire tree and adds the numbers:

A result of 21 must be printed.
Diagnostics.
In addition to this, Swift 2 brought a huge number of improvements in diagnosing errors and assumptions to correct them, such as correctly defining a developer’s attempt to modify
var using an immutable
struct method, or when the
var property never changes after initialization or when the result of a function call and etc.
One of the simplest changes makes the code more readable. As you know, Swift developers prefer to declare many things as constants, using
let , rather than variables, using
var . But what if you accidentally used the keyword
var? Or did you think that you need to change it, but did not do it? Both Xcode 7 and Swift 2 will give you a warning that you don’t change this variable anywhere in your code - Xcode literally explores all uses of the variable and knows for sure whether you changed it or not.
Many options (Option Sets)
The set of options is a way of representing a set of Boolean values, and in Swift 1.x it looked like this:
viewAnimationOptions = nil viewAnimationOptions = .Repeat | .CurveEaseIn | .TransitionCurlUp if viewAnimationOptions & .TransitionCurlUp != nil { ...
This type of syntax was widely used in Cocoa, but in reality, it is only a “relic” of the C language. So, in Swift 2, it is removed and its own type is presented for many options, it is the
OptionSetType protocol:

So now the set of options can be any type of
Set or
struct , confirming the
OptionSetType protocol. This leads to a clearer syntax when using multiple options:

The syntax does not rely on “bit” operations, as in previous versions, and does not use
nil to represent an empty set of options.
It should be noted that the many
OptionSetType options now rely on another feature in Swift 2, called the “default” implementation implementations for protocol extensions, so simply confirming the
OptionSetType protocol, you get a default implementation, for example, for the method
contains ,
subtractInPlace ,
unionInPlace and other set operations. We will look at protocol extensions later.
Functions and methods
The Swift 1.x syntax for declaring functions and methods was inherited from two different conventions originating from C, where the function arguments have no labels, and Objective-C, which labels the methods arguments. So you had such declarations:
func save(name: String, encrypt: Bool) { ... } class Widget { func save(name: String, encrypt: Bool) { ... } save("thing", false) widget.save("thing", encrypt: false)
In Swift 2, the above code would look like this:

So the functions get the same convention as the methods:
- implies that the name of the first argument is contained in the function name;
- subsequent arguments have labels.
However, these changes do not apply to functions imported from the C and Objective-C APIs.
Additionally, the parameter label declaring model has become more convenient since the
#option option, which was used in Swift 1.x to denote a parameter with the same internal (
internal ) and external (
external ) name, has been removed.
Scoping Operators
The new
do clause allows developers to explicitly define the explicit scope of variables and constants. This can be useful for re-using already declared names or for early release of some resources. The
do clause looks like this:

In order to avoid ambiguity with the
do ... while clause, which is presented in earlier versions of Swift 1.x, the latter was renamed to
repeat ... while in Swift 2.
UNIT testing
The problem with unit-testing Swift 1.x code is that Swift 1.x made you mark the
public word with all that you want unit-testing to see. As a result, there are
public notes where they should not be. All this is due to the fact that
Test Target is different from
Application Target , and files from your application that are
internal are not available for
Test Target .
In Swift 2, substantial unit relief has been achieved. Xcode 7 automatically compiles Swift 2 code in a special "test" mode

to access all
internal definitions as if they are defined as
public . This is done using the
@testable attribute when importing our module.

That's all it takes, and you don't need to mark anything with the word
public .
Moreover, these changes do not affect the main release of your application, maintaining the correct behavior both in terms of performance and in terms of access control.
2. Control the order of calculations
Swift 2 introduced new concepts for managing the order of computation, and improved existing structures.
Offer guard
The
guard clause, as well as the
if clause, executes the code depending on the Boolean value of the conditional expression. You use the
guard clause to, if the Boolean value is
true , continue the code following the
guard clause.
A
guard clause is essentially the inverse of an
if clause. For
if, we write:
if condition { // true } else { // false }
For
guard ,
true the branch rises to a higher level compared to the
false branch:
guard condition else {
Notice that the
false branch should end execution in a closed context (scope), returning a value or “throwing” (throw) an error. You guarantee that the code in the
true branch will be executed only if the condition is met.
This makes
guard a natural way to check non-fatal preconditions without using the “Smetri pyramid” formed by nested
if statements and without inversion of conditions.
Let's see what a typical code path looks like when using the traditional
if statement .

The
jsonDict dictionary is input to the
createPersonFromJSON function, and a valid instance of the
Person structure is created at the output if the corresponding information is presented in the dictionary, otherwise
nil is returned. The function is written as it would appear in Swift 1.2 - using the
if let construct. There are a couple of “pain points” in this code. First, the “turning off” of the direction of correct calculations from the main code, that is, the “successful” (in terms of condition) direction of calculations turned out to be “nested” in the
if let clause. Secondly, the
createPersonFromJSON function
does not always return an instance of
Person when we need it. The
Person structure contains 3 properties, one of which is
Optional , but the function returns the correct instance of
Person only if we get values from the dictionary that are different from
nil for all 3 keys. Let's rewrite this function as follows: so that we can return an instance of
Person if the address is missing, that is, if the
address key returns
nil .

We made a slight improvement in functionality. This version of the
createPersonFromJSON2 function can now instantiate
Person even if the address is
nil . This reflects the
Person structure better, but now we have a lot of
if statements , as well as the need to expand the final values assigned to
name and
age . Let's see how this can be improved with the new
guard clause.

In the case of
guard clauses, as well as with the
if let clause, we can check for the presence of values, “expand” them and assign them to constants. However, with the
guard let construct, code execution continues after curly braces
{} , if the conditional expression is evaluated as
true . This means that we can instantiate
Person within the normal scope of the function without using additional code branching to expand the values. If any of the
name or
age values is
nil , then the code "jumps" to the
else clause and an early return
nil is performed.
Let's take a quick look at
guard .
- If the condition of the guard clause is satisfied, execution of the code continues after the curly braces of the guard sentence are closed;
- If this condition is not met, then the code in the else “branch” is executed; unlike if , guard always has an > else block;
- The else clause must transfer control beyond the normal scope of the function using return , break , continue > or by calling another function or method.
.
Offer defer
The
defer clause resembles
finally in other programming languages, except that it is not tied to a
try clause, and you can use it anywhere. You write
defer {...} and somewhere in the code and this block will be executed when the calculation control leaves this code scope (enclosing scope), and it does not matter whether the code gets to the end or receives the return clause or “throws An error. The operator defer is perfectly combined with
guard and error handling (discussed later).
guard let file1 = Open(...) else { // file1 return } defer { file1.close() } guard let file2 = Open(...) else { // file2 return } defer { file2.close() } // file1 file2 . . . . . . . // ,
Note that
defer works for
file1 both in the normal course of the computational process and in the case of an error with the file
file2 . This removes numerous repetitions from the code and helps you not to forget to “clear” something in any branch of the calculations. When handling errors, there is the same problem and the
defer clause is
best suited for this purpose.
Repeat - while
Swift 2.0 introduced syntactic changes to the
do-while clause that was used before. Instead of a
do-while , we now get a
repeat-while .

There are two reasons for such changes:
When you use the
do - while loop, it is immediately unclear that this is a construct to be repeated. This is especially true if the block of code inside the
do clauses is large, and the
while condition is outside the screen. To mitigate this circumstance, the
do keyword has been replaced by
repeat , which makes it clear to the user that this is a duplicate block of code.
The keyword
do has a new assignment in Swift 2 in the new error handling model, which we will explore later.
Pattern matching
Swift has always had the power of
pattern matching , but only in the
switch construction. The
switch construct considered the value
value and compared it with several possible patterns. One of the drawbacks of the
switch clause is that we have to present all possible options for the
value , that is, the
switch statement must be exhaustive and this causes inconvenience of use. Therefore, Swift 2 ported
pattern matching capabilities, which previously were only for
switch / case , to other sentences that control the flow of computations.
if case is one of them, and it allows you to rewrite the code with the
switch more briefly. Other sentences are
for case and
while case .
Pattern matching if case
New in Swift 2 is support for
pattern matching inside
if (and
guard ) clauses. Let's first define the simplest enumeration
Number , and then show how to use it.

1. Check a specific option (case)
Use
case : we want to check if the value matches a specific
case . This works regardless of whether this
case has an associated value or not, but the value is not restored (if it exists).

The pattern starts with
case .IntegerValue , and the value that must match this pattern, the
myNumber variable, comes after the
= equality sign. This may seem illogical, but we see the same thing when the
Optional is expanded by the
a1 value in the
if let a = a1 construction: the
a1 value that is being checked comes after the equal sign.
Here is the equivalent version of Swift 1.2 using
switch :

2. Getting the associated value
We use
case : we want to check whether the value corresponds to a specific
case , and also to extract the associated value (or values).

The “pattern” has now become
case let .IntegerValue (theInt) . The value that must match the “sample” is the same as in the previous example.
Below is an example reflecting the same concept, but applied to
guard . The predicate semantics for
guard and
if are identical, so
pattern matching works just as well.

3. Selection using the where clause
An optional
where clause may be added to any
case in the
guard clause to provide additional restrictions. Let's modify the
getObjectInArray: atIndex: function from the previous example:

4. Matching range

5. Use the tuple tuple

6. Complex if predicates
The if sentence in Swift 2 was surprisingly capable. It can have multiple predicates separated by a comma. Predicates fall into one of three categories:
- The simplest logical tests (for example, foo == 10 || bar> baz ). There can be only one such predicate and it should be placed in the first place.
- Expanding Optional (for example, let foo = maybeFoo where foo> 10 ). If another option's immediate predicate is immediately followed by the optional deployment predicate, then you can skip the let. It can be added with the where qualifier.
- Pattern matching (for example, case let. Bar (something) = theValue ), is what we looked at above. It can be added with the where qualifier.
Predicates are evaluated in the order of their definition and after not completing a predicate, the rest are not evaluated.
Pattern matching for case
Pattern matching can be used in conjunction with the
for -in loop. In this case, our intention is to go through the elements of the sequence, but only on those that correspond to a given “pattern”. Here is an example:

Note that just like the “patterns” in the
switch clause, you can retrieve a set of associated values and use
_ if you are not interested in this associated value. If necessary, you can also add additional constraints using the
where clause.
Pattern matching while
Pattern matching can also be used with a
while loop. In this case, we will repeat the body of the loop until a certain value in the predicate matches the “pattern”. Here is an example:

Note that the complex predicates described in section “6. Complex if ”predicates are also supported by a
while loop , including the use of
where .
Pattern for "unwrapping" numerous optional
In Swift 1.2, we had a nice compact syntax for “deploying” the
Optionals set in one simple
if let clause:
var optional1: String? var optional2: String? if let optional1 = optional1, let optional2 = optional2 { print("Success") } else { print("Failure") }
Great!
However, you still encounter a situation where you really need to manage various combinations of existing / missing
Optional values. One such example is the form to fill in the
username and
password fields, and the user did not fill in one of them, and clicked the "Submit" button. In this case, you will want to show a special error in order to notify the user what exactly is missing. To do this, we can use the
pattern makching in Swift 1.x!
var username: String? var password: String? switch (username, password) { case let (.Some(username), .Some(password)): print("Success!") case let (.Some(username), .None): print("Password is missing") case let (.None, .Some(password)): print("Username is missing") case (.None, .None): print("Both username and password are missing") }
It's a little awkward, but we enjoyed it right from the start.
In Swift 2, the syntax looks clearer:

At first glance, confused by the use of a question mark
? to show that the value is present (especially if it is associated with the idea of
Optionals , when the value may or may not exist), but it must be recognized that this example becomes very understandable in contrast to the awkward syntax
.Some (username) .
Error processing
In order to understand the new features of Swift related to error handling, it will be useful to remember that there are 3 ways where a function can end abnormally (hereafter, for brevity, let's switch to jargon and say “fall”):
- Many functions can “fall” for one reasonably simple “inherent” reason, for example, when you are trying to convert a String to an Int; such cases are handled fairly well by returning Optional values;
- At the other end of the spectrum there are logical programming errors that cause the array index to go out of bounds, inadmissible conditions, etc., it’s very difficult to deal with them and we don’t know how to manage them.
- The third case is a detail error, a fixable error, for example, such as a file is not found, or a network error or the user has destroyed the operation (situational errors).
Handling errors of the third type related to the situation is what Swift 2 is trying to improve.If we consider a typical control scheme for such errors in Swift 1.x and Objective-C, then we find a scheme when the function takes the inout argument NSError? and returns Bool to represent the success or failure of the operation: // error , var error: NSError? // success Bool: let success = someString.writeToURL(someURL, atomically: true, encoding: NSUTF8StringEncoding, error: &error) if !success { // error: println("Error writing to URL: \(error!)") }
This approach has many “dark” sides that make it less understandable, and what the method itself does, but more importantly, manual implementation of agreements regarding what is behind the returned Bool is required . If the method returns an object and received an error, then it returns nil ; if it is a boolean value, then false is returned, and so on. You need to know which method you are dealing with, what to check if the result is nil or false or something else when the method contains an NSError error object ? .
Very confusing syntax. All these difficulties are related to the fact that Objective-C could not return a set of values from a function or method, and if we need to notify the user about an error, such an ingrained way of handling it was suggested.Swift 2 got new error management. It uses the do-try-catch syntax that replaces NSError . Let's see how you can use this new syntax. I will consider a very simple example of handling such errors, which the optional returns perfectly cope with.values, and for which the new syntax is not intended. But the simplicity of this example will allow me to focus your attention on the mechanism of “throwing out” and “catching” errors, and not on the complexity of their semantic content. In the end I will give a real example of processing data coming from the network.Before an error can be thrown (throw) or caught (catch), it must be defined. You can define it in Swift 2 with the help of enum , which implements the ErrorType protocol :
In order for a function
to “throw out” (throw) an error, you need to announce the keyword throws in the function header :
Now this function can throw an error using the keyword throwand a link to a specific type of error:
If you try to call this function, the compiler will generate an error: “The function being called throws errors, and the reference to it is not marked with the try keyword and there is no error handling.”
Because the function declared that it is capable of throwing errors , and you have to “catch” potential mistakes. Let's try to use the keyword the try :
it was not enough, the compiler tells us that the required error handling, which is produced by using syntax do-try-catch :
Furthermore, in the block do-try-catch you have the opportunity to "catch" a few errors:
If the semantic part of errors does not interest you, then instead of using do-try-catch constructions , you can treat the values you are interested in as with Optional :
aTry and aTrySuccess are Optional , so don't forget to “deploy” them before use!Sometimes there is a method that can “fall” only in certain circumstances, and you know for sure that it will not “fall” in your mode of use. Then you can use try! .
If the function throws an error, it returns immediately. But sometimes you need to do some actions, such as freeing up resources or closing files, before the function returns. In this situation, the defer keyword already familiar to us works just fine . With the defer keyword, you can define a block of code that is always executed if the function returns, and it does not matter whether it returns normally or due to errors.We can define a defer block anywhere in our function. Moreover, it is possible to define more than one defer block. In this case, they will be performed in the reverse order. Let's look at an example:
Let us consider the real example presented in the Natasha Murashev article . Swift 2.0: Let's try? .
Consider the data that comes from an API (after deserialization):
This data needs to be converted to a Model for later use in an application:
The TodoItemParser parser deals with mixed data coming from an API, converts it into an understandable Model for later use in an application and “Throws” errors if it detects them:
Now we will perform the parsing of “good” data in the Model using the do-try-catch construction . We will perform the
parsing of “bad” data.
Instead of using the do-try-catch construct , you can treat the values that interest us as Optional with the try? :
In the first part, we considered only a part of the new features of Swift 2:- the fundamental constructs of the language, such as enum
, scoping
(scope), the syntax of the arguments, etc.- pattern matching ( pattern matching )- error handling ( error handling )In the second part, we will look at the rest:
- availability check ( availability Available checking )- extension ( the extensions ) protocol- interaction with Objective-CReferences used article:the New features in Swift 2What I Like in Swift 2A Beginner's guide to Swift 2the Error Handling in Swift 2.0 We doSwift 2.0 We do: for Let's try?Video Tutorial: What's New in Swift 2 Part 4: Pattern A Matchingthe Throw for What the Do not the Throwof The of the Best for What's in the New Swift