WWDC 2018 Session 401 What’s New in Swift?

This Session is divided into two parts. The first half of this Session is a brief introduction to Swift open source issues, and the second half is an in-depth look at what Swift 4.2 is bringing.

Community development

Let’s start with some Swift statistics. Swift has 600 contributors since it opened source and has consolidated over 18K pull requests.

Community-led continuous integration

Swift wants to be a cross-platform universal language, and about a month ago, the Swift team expanded its existing open Integration platform called Community-Hosted Continuous Integration. If you want to bring Swift to another platform, This is where you plug in your own hardware, and Swift runs regular integration tests on these hardware so that you know very quickly if the latest Swift is working on your platform.

Fedora 27 / Ubuntu on PowerPC/Debian 9.1 on Raspberry Pi

Swift BBS

At the same time, the Swift team has put a lot of effort into maintaining the Swift community. Two months ago, the Swift community officially moved from mailing lists to forums, making it easier for people to contribute, such as this proposal from March:

You simply to answer these questions, participate in discussions, if you for the understanding of this aspect is not deep, don’t want to speak rashly, in fact, as long as you read about community members, have understanding to this matter, it is also a kind of participation, then maybe this proposal out you can also write an article at that time to discuss with you about the content and key points.

If you are maintaining a Swift related program, you might consider applying for a section on the forum where the community can follow and participate in your program.

Swift documentation is now maintained by swift.org, docs.swift.org.

Chris Lattner

When Chris Lattner left Apple, there was a lot of discussion about whether it would be bad for Swift, but over the past year, Chris has actually done his part to promote Swift, going to Google and even serving as Swift’s evangelist.

After Chris joined Google, Google fork a Swift repository, which acts as a hub for Google developers to Commit, and over the past year fixed a number of issues with Swift running on Linux, making Swift running on Linux more stable. Google has also written a Swift Formatter, which is currently in development.

And facilitated the cooperation between Swift and Tensorflow to develop Swift for Tensorflow, mainly because Python has gradually been unable to meet the needs of Tensorflow, and millions of learning cycles make performance extremely important. You need a language that can interact more closely with Tensorflow, and you might think that other languages can use Tensorflow, and there’s nothing special about it. In fact, other languages just develop the Tensorflow API. Swift code is compiled directly into the Tensorflow Graph, which is much more high-performance. The Tensorflow team even developed a syntax for Swift, making Swift a first-class citizen of Tensorflow. The addition of Python interaction gives Swift a better ecosystem in machine learning.

Chris has spent the past year working with Google to maintain Swift, strengthen Swift’s presence on Linux, create a machine learning area for Swift, and continue to contribute to the Swift community. Now, I don’t think anyone should worry that Chris’s departure will have any negative impact on Swift.

In addition, there are many great talents in Swift development team and community who have made great contributions, such as Slava, one of the Session speakers, Ted, Doug Gregor, Xiaodi Wu, and so on. They also gave their energy and talent to Swift.

What is Swift, 4.2?

Next we’ll look at Swift 4.2, what is Swift 4.2? What is its role throughout the development cycle?

Swift will have a Major Release every six months. Swift 4.2 is a Major Release after 4.0 and 4.1. The official team has been committed to improving development experience, which is the development goal of Swift 4.2:

  • Faster compilation speed
  • Add features to improve code writing efficiency
  • Make the SDK provide better Swift support
  • Improve ABI compatibility

Swift 5 will be released in early 2019, when the ABI will finally be stabilized and the Swift runtime will be built into the operating system, which will further speed up App launches and make packaged apps smaller.

If you’re interested in an ABI stable plan, check out the ABI Dashboard.

Compiler improvements

Code compatibility

Like Xcode 9, Xcode 10 will ship with only one Swift compiler and will offer two compatibility modes for both previous Swift releases, all of which will use new apis and new language features.

And not only is Swift syntactically compatible, the three developer modes also cover SDK compatibility, which means that as long as your code runs in Xcode 8, Swift 3, it will definitely run in Xcode 10.

But Swift 4.2 does offer more great features, and this will be the last version to support Swift 3 compatibility mode for future development.

Faster Debug compilation speed

Let’s talk about the compilation speed improvement we saw in testing existing apps on the Macbook Pro quad-core I7:

Wikipedia is a hybrid objective-C and Swift project, which may be closer to your actual project. The speed of a project’s compilation depends on many things, such as the configuration of the project, the number and size of image files.

The 1.6-fold increase is the overall speed, and if we just look at Swift’s compile time, it’s actually a 3-fold increase in total, and for a large number of projects, a full compile can be about twice as fast.

Where do these elevations come from? Since Swift does not need to import header files, but each file has access to the contents of other files in the module, there is a lot of repetitive work to do symbol look-up during compilation. This time the compiler built a compilation pipeline to reduce repetitive cross-file execution.

Compilation Mode vs. Optimization Level

In addition, this time, “compile mode” has been removed from “optimization level”. Compile mode means how we compile our modules. There are currently two modes:

  • Incremental: Formerly known as Single File, compiled File by File.
  • Whole Module compilation: The Whole Module is compiled together.

Incremental compilation Although full compilation is slower than modular compilation, subsequent changes to files require only a recompilation of the relevant files, rather than the entire module being recompiled.

Compiling the entire module together is faster. It is said that the principle is to merge all files into a single file and then compile it, thus reducing symbol lookups across files. But if you change one of the files, you need to compile the entire module again.

The addition of this compiler option actually has another important significance. Previously, we had only three options to achieve the following three effects:

Increased quantization compilation Modular compilation
To optimize the
Don’t optimize

Optimizations take time, and now we can use the modular and non-optimized option to achieve the fastest build times. Applying this option to the parts of our project that don’t change often (such as the pod dependency library) can greatly improve our build times.

After I applied this configuration to my project, the actual compilation speed increased from 113s to 64s, just by adding this code to my podfile (it works fine in Xcode 9.3) :

post_install do |installer|
  # Improve pod library compilation speed
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_COMPILATION_MODE'] = 'wholemodule'
      if config.name == 'Debug'
        config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Onone'
      else
        config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Osize'
      end
    end
  end
end
Copy the code

The Runtime optimization

ARC

Swift uses ARC for memory management, which is an evolution of MRC. ARC uses an object management model that automatically inserts retain and release code in place at compile time.

Swift 4.2 used a “owned” model in which the caller retained and the caller released. In other words, the caller held the passed object, as shown in the following figure:

Now Swift 4.2 uses a “Guaranteed” model, where the caller guarantees that objects will not be released during the life cycle of a function call and the object is no longer owned by the called party:

Adopting this model not only results in better performance, but also results in smaller compiled binaries.

String

When we instantiate a String on a 64-bit platform, it is 16 bytes long. In order to store something of varying length, it allocates space in the heap for storage. Those 16 bytes will store information such as the encoding format. This is a trade-off between performance and memory footprint.

However, the 16 bytes memory footprint actually has some optimization space. For some strings that are small enough, we can not store them in the heap independently, but in the spare part of the 16 bytes, which can give better performance and less memory footprint for small strings.

The principle is the same as Tagged Pointer of NSString, but it can store a slightly larger string than NSString.

Reduce code size

Swift also added an optimization level option called “Optimize for Size”, which allows the compiler to make the resulting binary smaller by reducing generic specialisation, function inlining and so on

In reality, performance may not be the most important concern, but the size of the application may be more important. Using this compilation option can reduce binaries by 10-30%, while performance typically costs 5% more.

New syntax features

Traversal enumeration

Previously we might have implemented an allCases attribute ourselves to traverse enumeration values:

enum LogLevel {
    case warn
    case info
    
    static let allCases: [LogLevel] = [.warn, .info]
}
Copy the code

However, we may forget to update allCases when adding new cases. Now we can use the CaseIterable protocol in Swift 4.2 and have the compiler automatically create allCases for us:

enum LogLevel: CaseIterable {
    case warn
    case info
}

for level in LogLevel.allCases {
    print(level)
}
Copy the code

Conditional Conformance

Conditional Conformance expresses the semantics that generic types comply with a specific protocol under certain conditions. For example, Array will only follow Equatable if its element is Equatable:

extension Array: Equatable where Element: Equatable {
    func ==<T : Equatable>(lhs: Array<Element>, rhs: Array<Element>) -> Bool{... }}Copy the code

This is a powerful feature that is used extensively in the Swift standard library and is available in Codable to help us automatically generate parsing code.

The strengthening of Hashable

Similar to Codable, Swift 4.2 introduces automated features for Equatable and Hashable:

struct Stock: Hashable {
    var market: String
    var code: String
}
Copy the code

But that raises the question, how do you implement hashValue? The existing API for hashValue is simple but difficult to implement. You have to figure out a way to mash all the attributes together to produce a hash, and sequences like sets and dictionaries built around hash tables depend entirely on the hash implementation of the stored elements. This is unreasonable.

In Swift 4.2, we improved the Hashable API by introducing a new Hasher type to store hash algorithms. The new Hashable looks like this:

protocol Hashable {
    func hash(into hasher: inout Hasher)
}
Copy the code

Instead of deciding on a hash algorithm when we implement Hashable, we now need to decide which attributes are involved in the hash process:

extension Stock: Hashable {
    func hash(into hasher: inout Hasher) {
        market.hash(into: &hasher)
        code.hash(into: &hasher)
    }
}
Copy the code

Instead of relying ona hash implementation to store elements, Dictionary can choose an efficient hash algorithm to build Hasher and then call the hash(into:) method to get the element’s hash value.

Swift will provide a random seed for the Dictionary and Set to generate random numbers as arguments to the hash at every run time, so the Dictionary and Set will not be an ordered Set, if your code depends on their order, then fix it.

If you want to use a custom random seed, use the environment variable SWIFT_DETERMINISTIC_HASHING:

More details can be found in proposal SE-0206. It’s not very long, so I suggest you read it once.

Random number generation

Random number generation is a big topic, usually it requires the system to obtain variables in the running environment to be used as random seeds, which also makes different random number API on different platforms:

#if os(iOS) || os(tvOS) || os(watchOS) || os(macOS)
    return Int(arc4random())
#else
    return random()
#endif
Copy the code

However, developers should not worry about such trivial matters. The most important thing in Swift 4.2 is the improvement in ABI compatibility, but there is an API for random numbers:

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomFloat = Flow.random(in: 0 ..< 1)

let greetings = ["hey"."hi"."hello"."hola"]
print(greetings.randomElement()!)

let randomlyOrderGreetings = greetings.shuffled()
print(randomlyOrderedGreetings)
Copy the code

We can now simply take a random number, take a random element of the array, or scramble the array, and it’s safe to generate random numbers on MAC or Linux.

You can also define your own random number generator:

struct CustomRandomNumberGenerator: RandomNumberGenerator {... }var generator = CustomRandomNumberGenerator(a)let randomIntFrom0To10 = Int.random(in: 0 ..< 10, using: &generator)
let randomFloat = Flow.random(in: 0 ..< 1, using: &generator)

let greetings = ["hey"."hi"."hello"."hola"]
print(greetings.randomElement(using: &generator)!)

let randomlyOrderGreetings = greetings.shuffled(using: &generator)
print(randomlyOrderedGreetings)
Copy the code

Detect target operating platform

In the past, when we customized some cross-platform code, we decided to do so:

#if os(iOS) || os(watchOS) || os(tvOS)
    import UIKit
    typealias Color = UIColor
#else
    import AppKit
    typealias Color = NSColor
#endif

extension Color {... }Copy the code

We don’t really care what platforms our code runs on, but what libraries it can import, so Swift 4.2 has added a new macro to determine whether libraries can be imported:

#if canImport(UIKit)
    import UIKit
    typealias Color = UIColor
#elseif canImport(AppKit)
    import AppKit
    typealias Color = NSColor
#else
    #error("Unsupported platform")
#endif
Copy the code

Swift also has a new set of compilation macros that allow you to manually raise compilation errors # ERROR (” error “) or compilation warnings # WARN (“Warning”) in your code (FIXME is no longer needed).

In addition, we have added a set of macros to determine the running environment. Here is the code to determine whether it is an emulator environment:

// before Swift 4.2
#if (os(iOS) || os(watchOS) || os(tvOS) &&
    (cpu(i396) || cpu(x86_64))
    ...
#endif

/ / Swift, 4.2
#if hasTargetEnviroment(simulator)
    ...
#endif
Copy the code

The abolition of ImplicityUnwrappedOptional type

ImplicityUnwrappedOptional unpack the optional type is also known as mandatory, it is a necessary tool, we use it is the main purpose, reduce the explicit solution package, for example, UIViewController’s life cycle, View is a null value when init, but it will be there as long as viewDidLoad is there, and if we use it every time we need to manually explicitly force unpack the view! Using IUO can save this part of unpacking code.

So ImplicityUnwrappedOptional is useful when interacting with Objective – C API of a tool, has not all been marked with nullability variables will be exposed as IUO types to the Swift, It was also created to temporarily fill in the undefined parts of the Swift language to deal with fixed code patterns. As the language develops, we should identify the role of IUO and replace it in a good way.

This is the result of proposal SE-0054, which was actually partially implemented in Swift 3 and further refined and fully implemented in Swift 4.2.

In the past, we used to mark IUO as a type. After Swift 4.2, IUO is no longer a type, but a tag. The compiler will do this by marking the variable @_autounwrapped. All variables marked as IUO are implicitly forced to unpack by the compiler at compile time:

let x: Int! = 0 // x is marked as IUO (Optional
      
       )
      
let y = x + 1   // The compiler actually converts to x! + 1 to compile
Copy the code

This is more consistent with our original purpose, because what we need to mark is nullability of variables, and to mark by type is actually to mark IUO of a value, not a variable.

Of course, this change also has a slight impact on the previous code, because we are marking objects for variables, not types, so the IUO that used to exist as a type becomes an illegal declaration:

let a: [Int! ] = []// Failed to compile
Copy the code

Exclusive memory access

The code has exclusive access to a certain portion of memory at the same time. This may sound confusing, but for example, you can iterate through an array while modifying an array:

var a = [1.2.3]

for number in a {
    a.append(number) // Generates undefined behavior
}
Copy the code

Swift uses the memory exclusive access model to detect this error at compile time, which has been enhanced in Swift 4.2 to detect more instances of illegal memory access, and to provide runtime checks. In the future, memory exclusive access checks will be enabled by default like array out of bounds:

Recommended resources

We recommend checking out What’s New in Swift 4.2 by Ole Begemann to take you on Playground and experience the new grammatical functions in Swift.

conclusion

Swift 5 is an important milestone, and the stability of ABI means that this design will support the functional requirements of several larger versions later. I think the delay is not a bad thing, remember that Apple is the largest user of Swift, and this language will support Apple’s SDK development and ecosystem for the next ten years. That’s why they’re being more cautious about stabilizing the ABI, and it’s in line with Apple’s strategy this year to keep things steady.

There will definitely be another explosion of Swift syntax features after the ABI settles down, async/await, native regular expressions… And even the possibility of Apple developing a Swift Only SDK makes me look forward to Swift 5 in 2019.