What's new in Swift 3?

    As we all know for a long time, Apple integrated Swift 3 into Xcode 8. This is the first version of the open source language that works on both macOS and Linux. If you have been following the development of the language on Swift Evolution since December last year and managed to experiment with it in IBM sandbox, you probably already realized that a lot of changes appeared in it. I am absolutely sure that when compiling an existing project in Xcode 8, your code will surprise you with errors. But it is fixable. Let's get acquainted with some changes in the new language versions.
    image

    Changes in the new versions, two main categories can be divided:

    • Remote features that are already deprecated with Swift 2.2
    • Language improvements

    Let's start with the category of remote functions, since it is easier to understand and you probably met these functions when you received warnings in Xcode version 7.3.

    ++ and - operators


    The increment and decrement operators of the C language heritage and their functionality are simple - add one or subtract one to a specific variable:

    var i = 0
    i++
    ++i
    i--
    --i
    

    However, things get more complicated when it comes to deciding which one to choose. Each of them can be presented in two possible variants: prefix and postfix - they all function thanks to the engine and return values ​​that you can use or reject due to reloading operators.

    This is more than enough for beginners, as they have been removed - use the assignment operators and addition + = and subtraction - = instead:

    var i = 0
    i += 1
    i -= 1
    

    Of course, you can use the addition (+) and subtraction (-) operators, as well as share assignment operators, so you can save your time when writing code, although:

    i = i + 1
    i = i - 1
    

    To familiarize yourself: If you want to know more about the reasons for this change, see Chris Latner's suggestion on this.

    C language style for looping is history


    The most common example of using increment and decrement operators is the C language style for writing a loop. Deleting operators means deleting everything that is connected with them, since you can’t do anything with it all, unlike what you did previously with expressions and an operator to specify a range.

    For example, if you have some knowledge, you will probably use a for loop to display a number from 1 to 10:

    for (i = 1; i <= 10; i++) {
        print(i)
    }
    

    In Swift 3, this is not possible. This is his prototype in Swift 3 - let's look at the closed range operator (...) in action:

    for i in 1...10 {
        print(i)
    }
    

    Alternatively, you can also use for-each loop with closed expressions and abbreviated arguments - more detailed information can be found here .

    (1...10).forEach {
        print($0)
    }
    

    To familiarize yourself: If you want to know more about the motivation for this change, see Erica Sadun's suggestion .

    Removed a variable from function parameters


    Function parameters are usually defined as constants because they do not need to be changed inside functions. However, there are certain cases where declaring them as variables may come in handy. In Swift 2, you can mark a function parameter as a variable with the var keyword . After the parameter is specified as var , it will create a local copy of the value, so you can change its value in the body of the function.

    As an example, the following function determines the greatest common factor of two given numbers - if you missed a math course in high school, read more about it here:

    func gcd(var a: Int, var b: Int) -> Int {
      if (a == b) {
        return a
      }
      repeat {
        if (a > b) {
          a = a - b
        } else {
          b = b - a
        }
      } while (a != b)
      return a
    }
    

    The algorithm is simple: if both numbers are already equal, one of them is returned. Otherwise, compare them, subtract the smaller from the larger and assign the result to the larger until they become equal and return any of them. As you can see, by marking a and b as variables, we can change their values ​​in the function.

    Swift 3 no longer allows developers to set function parameters as variables, since Swift developers can get confused between var and inout . Thus, the latest version of Swift simply removes var from the function parameters.

    Therefore, to write the same gcd functionin Swift 3, a different approach is needed. You need to save function parameter values ​​for local variables:

    func gcd(a: Int, b: Int) -> Int {
      if (a == b) {
        return a
      }
      var c = a
      var d = b
      repeat {
        if (c > d) {
          c = c - d
        } else {
          d = d - c
        }
      } while (c != d)
      return c
    }
    

    If you want to know more about the reasons for this decision, you can read the original sentence .

    Sequential labels behavior for function parameters


    Lists of function parameters are tuples, so you can use them to call functions, as long as the tuple structure matches the function prototype. Take the gcd () function as an example. You can make a call as follows:

    gcd(8, b: 12)
    

    Or you can even call the function as shown below:

    let number = (8, b: 12)
    gcd(number)
    

    As you can see in Swift 2, you do not need to specify the label of the first parameter. However, you must specify a label for the second (and other parameters) when calling the function.

    This syntax is confusing for novice developers, so it is designed to standardize label behavior. In the new versions of Swift, you can call the function as follows:

    gcd(a: 8, b: 12)
    

    You must explicitly specify a label for the first parameter. If you do not, Xcode 8 will show you an error message.

    Your first reaction to this change may look like “OMG! I will have to make many changes to the existing code. ” You're right. These are tons of changes. Thus, Apple offers a way to suppress the first label of a function call parameter. You can add an underline to the first parameter, as shown below:

    func gcd(_ a: Int, b: Int) -> Int {
    ...
    }
    

    By doing this, you can call the function using the old method - without specifying the first label. This will help make migrating your code from Swift 2 to Swift 3 a lot easier.

    On the motivation and intention of this change, you can read this sentence .

    Selectors as strings are no longer used


    Let's create a button and perform some action, when you click on it - use only the playground, not the storyboard:

    // 1
    import UIKit
    import XCPlayground
    // 2
    class Responder: NSObject {
      func tap() {
        print("Button pressed")
      }
    }
    let responder = Responder()
    // 3
    let button = UIButton(type: .System)
    button.setTitle("Button", forState: .Normal)
    button.addTarget(responder, action: "tap", forControlEvents: .TouchUpInside)
    button.sizeToFit()
    button.center = CGPoint(x: 50, y: 25)
    // 4
    let frame = CGRect(x: 0, y: 0, width: 100, height: 50)
    let view = UIView(frame: frame)
    view.addSubview(button)
    XCPlaygroundPage.currentPage.liveView = view
    

    Quite a lot of events happen there, so let's break them down into stages:

    1. Import UIKit and XCPlayground frameworks - you need to create a button and show it in the playground editor.
    2. Determine the click method that will be executed when the user clicks on the button and create the target? the logical object of a button is its base class NSObject, since selectors work only with Objective-C methods.
    3. Declaring a button and setting properties.
    4. Creating a view of a certain size, adding a button to the view and displaying it in the assistant of the Playground editor.

    Look at the highlighted code. The button selector is a string. If you enter it incorrectly, the code will be compiled, but it will fail during its execution, since the corresponding method may not be found.

    To fix a potential compile time issue, Swift 3 replaced the selector string with the #selector () keyword. This allows the compiler to detect the problem earlier if you do not correctly specify the method name.

    button.addTarget(responder, action: #selector(Responder.tap), for: .touchUpInside)
    

    To understand the reasons for this change, you can read Doug Gregor's suggestion .

    This is for remote functions. Now let's move on to the main points of modernizing the language.

    Key-paths as a string


    This function is similar to the previous one, but it relates to key value coding (KVC) and key-value observation (KVO):

    class Person: NSObject {
      var name: String = ""
      init(name: String) {
        self.name = name
      }
    }
    let me = Person(name: "Cosmin")
    me.valueForKeyPath("name")
    

    You create a Person class that matches the encoding key-value, create my identity with the class designated by the initializer, and use KVC to set the name.

    Again, if you do it wrong, everything will explode!

    Fortunately, this will no longer happen in Swift 3. Key-paths have been replaced with the #keyPath () expression:

    class Person: NSObject {
      var name: String = ""
      init(name: String) {
        self.name = name
      }
    }
    let me = Person(name: "Cosmin")
    me.value(forKeyPath: #keyPath(Person.name))
    

    To understand the reasons for this change, you can read the David Hearts proposal .

    Removed NS Base Type Prefix


    Removed the NS prefix for basic types. Let's see how it works. A typical example is working with JSON:

    let file = NSBundle.mainBundle().pathForResource("tutorials", ofType: "json")
    let url = NSURL(fileURLWithPath: file!)
    let data = NSData(contentsOfURL: url)
    let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: [])
    print(json)
    

    You can use the base classes to connect to the file and extract the data in JSON format accordingly: NSBundle -> NSURL -> NSData -> NSJSONSerialization.

    The NS prefix is not used in Swift 3, so it all boils down to Bundle -> URL -> Data -> JSONSerialization ():

    let file = Bundle.main().pathForResource("tutorials", ofType: "json")
    let url = URL(fileURLWithPath: file!)
    let data = try! Data(contentsOf: url)
    let json = try! JSONSerialization.jsonObject(with: data)
    print(json)
    

    For this naming change procedure, you can check out this sentence written by Tony Parker and Philip Hausler.

    M_PI vs .pi


    Let's calculate the circles and the area of ​​a circle with a given radius:

    let r =  3.0
    let circumference = 2 * M_PI * r
    let area = M_PI * r * r
    

    For older versions of Swift, you use M_PI to denote the pi constant . Swift 3 integrates the constant pi into the types Float , Double and CGFloat :

    Float.pi
    Double.pi
    CGFloat.pi
    

    The above code snippet will be written like this in Swift 3:

    let r = 3.0
    let circumference = 2 * Double.pi * r
    let area = Double.pi * r * r
    

    Using type inference, you can even omit a type. Here is the short version:

    let r = 3.0
    let circumference = 2 * .pi * r
    let area = .pi * r * r
    

    Grand central dispatch


    Grand Central Dispatch is used for network operations that do not block the user interface in the main thread. It is written in the C language and its API is very difficult for novice developers, even to perform simple tasks, such as creating an asynchronous queue and performing any operations:

    let queue = dispatch_queue_create("Swift 2.2", nil)
    dispatch_async(queue) {
      print("Swift 2.2 queue")
    }
    

    Swift 3 eliminates all boilerplate code and redundant material by adopting an object-oriented approach:

    let queue = DispatchQueue(label: "Swift 3")
    queue.async {
      print("Swift 3 queue")
    }
    

    For more information on this change, you can read Matt Wright .

    Core Graphics is now more Swifty


    Core Graphics is a powerful graphics framework, but it uses a C-style interface similar to GCD:

    let frame = CGRect(x: 0, y: 0, width: 100, height: 50)
    class View: UIView {
      override func drawRect(rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()
        let blue = UIColor.blueColor().CGColor
        CGContextSetFillColorWithColor(context, blue)
        let red = UIColor.redColor().CGColor
        CGContextSetStrokeColorWithColor(context, red)
        CGContextSetLineWidth(context, 10)
        CGContextAddRect(context, frame)
        CGContextDrawPath(context, .FillStroke)
      }
    }
    let aView = View(frame: frame)
    

    You create a view frame by extending the UIView class by overriding the DrawRect () method.

    Swift 3 takes a completely different approach - first expands the current graphics context and performs all the drawing operations that are subsequently associated with it:

    let frame = CGRect(x: 0, y: 0, width: 100, height: 50)
    class View: UIView {
      override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else {
          return
        }
        let blue = UIColor.blue().cgColor
        context.setFillColor(blue)
        let red = UIColor.red().cgColor
        context.setStrokeColor(red)
        context.setLineWidth(10)
        context.addRect(frame)
        context.drawPath(using: .fillStroke)
      }
    }
    let aView = View(frame: frame)
    

    Note: The context will be zero before calling DrawRect () , so you need to expand it using the expression guard - more on this here .

    Naming Agreement for Verbs and Nouns


    Time for grammar! Groups of methods in Swift 3 fall into two categories: methods that return a certain meaning - are meant as nouns - and methods that perform a certain type of action - are meant as verbs.

    The output from 10 to 1 is executed here:

    for i in (1...10).reverse() {
      print(i)
    }
    

    You use the reverse () method to change the range. Swift 3 treats this operation as a noun, as it returns the original range in reverse order. He adds the suffix “ed” to the method:

    for i in (1...10).reversed() {
      print(i)
    }
    

    The most common use of tuples / tuples to display the contents of an array:

    var array = [1, 5, 3, 2, 4]
    for (index, value) in array.enumerate() {
      print("\(index + 1) \(value)")
    }
    

    Swift 3 treats this method as a noun, as it returns a tuple containing the current index and value of this array, and adds the suffix “ed” to it:

    var array = [1, 5, 3, 2, 4]
    for (index, value) in array.enumerated() {
      print("\(index + 1) \(value)")
    }
    

    Another example is array sorting. Here is an example of how you can sort an array in ascending order:

    var array = [1, 5, 3, 2, 4]
    let sortedArray = array.sort()
    print(sortedArray)
    

    Swift 3 treats this operation as a noun, since it returns a sorted array. The sort method is now called sorted :

    var array = [1, 5, 3, 2, 4]
    let sortedArray = array.sorted()
    print(sortedArray)
    

    Let's sort the array, without using an intermediate constant. In Swift 2, you can call a function as follows:

    var array = [1, 5, 3, 2, 4]
    array.sortInPlace()
    print(array)
    

    You use sortInPlace () to sort the mutable array. Swift 3 treats this method as a verb, as it does the actual sorting without returning values. It uses only the base word that describes the action. So sortInPlace () is now called sort () :

    var array = [1, 5, 3, 2, 4]
    array.sort()
    print(array)
    

    For more information on naming conventions, check out the API Design Guidelines .

    Swift API


    Swift 3 adopts a simple philosophy for its APIs - to omit unnecessary words, so if something is redundant or can be taken out of context, delete it:

    • XCPlaygroundPage.currentPage becomes PlaygroundPage.current
    • button.setTitle (forState) becomes button.setTitle (for)
    • button.addTarget (action, forControlEvents) becomes button.addTarget (action, for)
    • NSBundle.mainBundle () becomes Bundle.main ()
    • NSData (contentsOfURL) becomes URL (contentsOf)
    • NSJSONSerialization.JSONObjectWithData () becomes JSONSerialization.jsonObject (with)
    • UIColor.blueColor () becomes UIColor.blue ()
    • UIColor.redColor () becomes UIColor.red ()

    Enum


    Swift 3 treats the enumeration as properties, so use lowerCamelCase instead of upperCamelCase for them:

    • .System becomes .system
    • .TouchUpInside becomes .touchUpInside
    • .FillStroke becomes .fillStroke
    • .CGColor becomes .cgColor

    @discardableResult


    In Swift 3, Xcode will show you a warning if you are not using the return value of a function or method. Here is an example:

    image

    In the above code, the printMessage method returns the received message for the caller. However, the return value is not used. This can be a potential problem, so the compiler in Swift 3 will give you a warning.

    In this case, if it is not a processed return value. You can suppress the warning by adding @discardableResult in the method declaration:

    override func viewDidLoad() {
        super.viewDidLoad()
        printMessage(message: "Hello Swift 3!")
    }
    @discardableResult
    func printMessage(message: String) -> String {
        let outputMessage = "Output : \(message)"
        print(outputMessage)
        return outputMessage
    }
    

    Conclusion


    It's all about Swift 3. The new version of Swift is one of the major releases, which makes the language even better. It contains many fundamental changes that can certainly affect your existing code. I hope this article helps you better understand the changes, and I hope it helps you save some time moving your project to Swift.

    Also popular now: