Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4370

What’s New in Swift 3.1?

$
0
0


Great news: Xcode 8.3 and Swift 3.1 is now out of beta! This release contains some long-awaited Swift Package Manager features and improvements to the language itself.

If you haven’t been following the Swift Evolution Process closely, keep reading – this article is for you!

In this article, I’ll highlight the most significant changes in Swift 3.1 which will have a major impact on your code. Let’s dive in! :]

Getting Started

Swift 3.1 is source-compatible with Swift 3.0, so the new features won’t break your code if you’ve already migrated your project to Swift 3.0 using Edit\Convert\To Current Swift Syntax… in Xcode. However, Apple has dropped support for Swift 2.3 in Xcode 8.3. So if you haven’t migrated from Swift 2.3 yet, now is the time to do so!

In the sections below, you’ll see linked tags such as [SE-0001]. These are Swift Evolution proposal numbers. I’ve included the link to each proposal so you can discover the full details of each particular change. I recommend you try out the features we discuss in a playground, so you have a better understanding of everything that changes.

So, fire up Xcode, select File\New\Playground…. Choose iOS as the platform, call it whatever you want, and save it wherever you want. While you’re reading this article, try out each feature in this playground.

Note: If you need a short and sweet review of the Swift 3.0 highlights, check out our article on What’s New in Swift 3.

Language Improvements

First, let’s take a look at the language improvements in this release, including failable initializers for numeric types, new sequence functions, and more.

Failable Numeric Conversion Initializers

Swift 3.1 implements failable initializers for all numeric types (Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Float80, Double) which either complete successfully without loss of information or simply return nil [SE-0080].

This feature is useful, for example, when dealing with loosely typed data conversions from external sources in a safe and recoverable manner. For example, this is how you might process a JSON array of students:

class Student {
  let name: String
  let grade: Int
 
  init?(json: [String: Any]) {
    guard let name = json["name"] as? String,
          let gradeString = json["grade"] as? String,
          let gradeDouble = Double(gradeString),
          let grade = Int(exactly: gradeDouble)  // <-- 3.1 feature here
    else {
        return nil
    }
    self.name = name
    self.grade = grade
  }
}
 
func makeStudents(with data: Data) -> [Student] {
  guard let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),
        let jsonArray = json as? [[String: Any]] else {
    return []
  }
  return jsonArray.flatMap(Student.init)
}
 
let rawStudents = "[{\"name\":\"Ray\", \"grade\":\"5.0\"}, {\"name\":\"Matt\", \"grade\":\"6\"},
                    {\"name\":\"Chris\", \"grade\":\"6.33\"}, {\"name\":\"Cosmin\", \"grade\":\"7\"},
                    {\"name\":\"Steven\", \"grade\":\"7.5\"}]"
let data = rawStudents.data(using: .utf8)!
let students = makeStudents(with: data)
dump(students) // [(name: "Ray", grade: 5), (name: "Matt", grade: 6), (name: "Cosmin", grade: 7)]

You use a failable initializer to convert the grade property from Double to Int inside the Student class designated failable initializer like so:

let grade = Int(exactly: gradeDouble)

If gradeDouble is a fractional value, such as 6.33, it will fail. If it can be represented exactly with an Int, such as 6.0, it will succeed.

Note: An alternative design used throwing initializers instead of failable ones. The community chose failable ones to be a better, more ergonomic design.

New Sequence Functions

Swift 3.1 adds two new functions for data filtering to the standard library’s Sequence protocol: prefix(while:) and drop(while:) [SE-0045].

Consider the Fibonacci infinite sequence:

let fibonacci = sequence(state: (0, 1)) {
  (state: inout (Int, Int)) -> Int? in
  defer {state = (state.1, state.0 + state.1)}
  return state.0
}

In Swift 3.0 you simply specified the iteration count to iterate through the fibonacci sequence:

// Swift 3.0
for number in fibonacci.prefix(10) {
  print(number)  // 0 1 1 2 3 5 8 13 21 34
}

Swift 3.1 lets you use prefix(while:) and drop(while:) with a condition to get all elements of the sequence between two given values, like so:

// Swift 3.1
let interval = fibonacci.prefix(while: {$0 < 1000}).drop(while: {$0 < 100})
for element in interval {
  print(element) // 144 233 377 610 987
}

prefix(while:) returns the longest subsequence which satisfies a certain predicate. It starts at the beginning of the sequence and stops at the first element for which the given closure returns false.

drop(while:) does the opposite: It returns the subsequence that begins with the first element for which the given closure returns false and finishes at the end of the sequence.

Note: You may use trailing closure syntax in this case:
let interval = fibonacci.prefix{$0 < 1000}.drop{$0 < 100}

Concrete Constrained Extensions

Swift 3.1 lets you extend a generic type with a concrete type constraint. Previously, you couldn’t extend a type like this because the constraint had to be a protocol. Let’s see an example.

For example, Ruby on Rails provides a very useful isBlank method for checking for user input. Here is how you would implement it in Swift 3.0 as a computed property on the String data type extension:

// Swift 3.0
extension String {
  var isBlank: Bool {
    return trimmingCharacters(in: .whitespaces).isEmpty
  }
}
 
let abc = " "
let def = "x"
 
abc.isBlank // true
def.isBlank // false

If you want the isBlank computed property to work with optional strings as well, you would do the following in Swift 3.0:

// Swift 3.0
protocol StringProvider {
  var string: String {get}
}
 
extension String: StringProvider {
  var string: String {
    return self
  }
}
 
extension Optional where Wrapped: StringProvider {
  var isBlank: Bool {
    return self?.string.isBlank ?? true
  }
}
 
let foo: String? = nil
let bar: String? = "  "
let baz: String? = "x"
 
foo.isBlank // true
bar.isBlank // true
baz.isBlank // false

This creates a StringProvider protocol for String to adopt. Which in turn you use to extend Optional when the wrapped type is a StringProvider, to add the isBlank method.

Swift 3.1 lets you extend a concrete type instead of a protocol like this:

// Swift 3.1
extension Optional where Wrapped == String {
  var isBlank: Bool {
    return self?.isBlank ?? true
  }
}

This provides the same functionality as before, in much fewer lines of code!

Nested Generics

Swift 3.1 allows you to mix nested types with generics. As an exercise, consider this (not too crazy) example. Whenever a certain team lead at raywenderlich.com wants to publish a post on the blog, he assigns a team of dedicated developers to work on it so that it meets the website’s high quality standards:

class Team<T> {
  enum TeamType {
    case swift
    case iOS
    case macOS
  }
 
  class BlogPost<T> {
    enum BlogPostType {
      case tutorial
      case article
    }
 
    let title: T
    let type: BlogPostType
    let category: TeamType
    let publishDate: Date
 
    init(title: T, type: BlogPostType, category: TeamType, publishDate: Date) {
      self.title = title
      self.type = type
      self.category = category
      self.publishDate = publishDate
    }
  }
 
  let type: TeamType
  let author: T
  let teamLead: T
  let blogPost: BlogPost<T>
 
  init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost<T>) {
    self.type = type
    self.author = author
    self.teamLead = teamLead
    self.blogPost = blogPost
  }
}

You nest the BlogPost inner class within its corresponding Team outer class and make both classes generic. This is how the teams look for the tutorials and articles I’ve published on the website so far:

Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix",
     blogPost: Team.BlogPost(title: "Pattern Matching", type: .tutorial,
     category: .swift, publishDate: Date()))
 
Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix",
     blogPost: Team.BlogPost(title: "What's New in Swift 3.1?", type: .article,
     category: .swift, publishDate: Date()))

But actually, in this case you can simplify that code a bit more. If the nested inner type uses the generic outer one, it inherits the parent’s type by default. Therefore you don’t have to declare it, like so:

class Team<T> {
  // original code 
 
  class BlogPost {
    // original code
  }
 
  // original code 
  let blogPost: BlogPost
 
  init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost) {
    // original code   
  }
}
Note: If you want to learn more about generics in Swift, read our recently updated tutorial on getting started with Swift generics.

Availability by Swift Version

You may check for a specific Swift version with the #if swift(>= N) static construct like this:

// Swift 3.0
#if swift(>=3.1)
  func intVersion(number: Double) -> Int? {
    return Int(exactly: number)
  }
#elseif swift(>=3.0)
  func intVersion(number: Double) -> Int {
    return Int(number)
  }
#endif

However, this approach has one major drawback when used in something like the Swift standard library. It requires compiling the standard library for each and every supported old language version. That’s because when you run the Swift compiler in backwards compatibility mode, to say you want Swift 3.0 behaviour for example, it would need to use a version of the standard library compiled for that specific compatibility version. If you used one compiled with in version 3.1 mode, you simply wouldn’t have the right code there.

So, Swift 3.1 extends the @available attribute to support specifying Swift version numbers in addition to its existing platform versions [SE-0141]:

// Swift 3.1
 
@available(swift 3.1)
func intVersion(number: Double) -> Int? {
  return Int(exactly: number)
}
 
@available(swift, introduced: 3.0, obsoleted: 3.1)
func intVersion(number: Double) -> Int {
  return Int(number)
}

This new feature gives the same behaviour in terms of which intVersion method is available under which Swift version. However it allows libraries like the standard library to be compiled only once. The compiler then simply picks the features that are available for the given compatibility version selected.

Note: If you want to learn more about the availability attributes in Swift, check out our tutorial on availability attributes in Swift.

Convert Non-Escaping Closures to Escaping Ones

Closure arguments to functions were made non-escaping by default in Swift 3.0 [SE-0103]. However, part of that proposal was not implemented at the time. In Swift 3.1, you can convert non-escaping closures to escaping ones temporarily by using the new withoutActuallyEscaping() helper function.

Why would you ever want to do this? It’s probably not often but consider the example from the proposal.

func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void,
             on queue: DispatchQueue) {
  withoutActuallyEscaping(f) { escapableF in     // 1
    withoutActuallyEscaping(g) { escapableG in
      queue.async(execute: escapableF)           // 2
      queue.async(execute: escapableG)
 
      queue.sync(flags: .barrier) {}             // 3
    }                                            // 4
  }
}

This function runs two closures simultaneously and then returns after both are done.

  1. f and g come in as non-escaping and are converted to escapableF and escapableG.
  2. async(execute:) calls require escaping closures. Fortunately you have those because of the previous step.
  3. By running sync(flags: .barrier), you ensure that the async(execute:) methods are completely done and the closures won’t be called later on.
  4. Scope limits the use of escapableF and escapableG.

If you squirrel either temporary escaping closures away (i.e. actually escaped them) it would be a bug. Future versions of the Standard Library may be able to detect this and trap if you tried to call them.

Swift Package Manager Updates

Ah, the long-awaited updates to the Swift Package Manager have arrived!

Editable Packages

Swift 3.1 adds the concept of editable packages to the Swift Package Manager [SE-0082].

The swift package edit command takes an existing package and converts it to an editable one. The editable package replaces all of the canonical package’s occurrences in the dependency graph. Use the --end-edit command to revert the package manager back to the canonical resolved package.

Version Pinning

Swift 3.1 adds the concept of version pinning package dependencies to specific versions to the Swift Package Manager [SE-0145]. The pin command pins one or all dependencies like this:

$ swift package pin --all      // pins all the dependencies
$ swift package pin Foo        // pins Foo at current resolved version
$ swift package pin Foo --version 1.2.3  // pins Foo at 1.2.3

Use the unpin command to revert to the previous package version:

$ swift package unpin —all
$ swift package unpin Foo

The package manager stores the active version pin information for each package in Package.pins. If the file doesn’t exist, the package manager creates it automatically based on the requirements specified in the package manifest, as part of the automatic pinning process.

Other Bits

The swift package reset command resets a package back to a clean state, with no dependencies checked out or build artifacts present.

As well, the swift test --parallel command executes tests in parallel.

Miscellaneous Bits and Pieces

There’s a few other tidbits in Swift 3.1 that don’t quite fit anywhere else:

Multiple-Return Functions

C functions which return twice such as vfork and setjmp don’t work anymore. They change the program’s control flow in interesting ways. So the Swift community has decided that using them should be banned and now results in a compile-time error.

Disable Auto-Linking

The Swift Package Manager disables the auto-linking feature of module maps for C language targets:

// Swift 3.0
module MyCLib {
    header “foo.h"
    link “MyCLib"
    export *
}
 
// Swift 3.1
module MyCLib {
    header “foo.h”
    export *
}

Where to Go From Here?

Swift 3.1 polishes up some Swift 3.0 features in preparation for more serious changes that will be bundled in Swift 4.0 later on this year. These include huge improvements to generics, regular expressions, a more ergonomic string design and more.

If you’re feeling adventurous, then head over and look at the Swift standard library diffs or look at the official Swift CHANGELOG where you can read more information about all of the changes. Or you can use this to keep an eye out for what’s coming in Swift 4.0!

And if you’re really curious about what changes are coming in Swift 4 and beyond, then head over to the Swift Evolution proposals where you can see what things are being proposed right now. If you’re really keen then why not give feedback on one of the current proposals under review or even pitch a proposal yourself ;].

What do you like or dislike about Swift 3.1 so far? Let us know in the forum discussion below!

The post What’s New in Swift 3.1? appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4370

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>