BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Swift 5.2 Brings callAsFunction, Subscript with Default Arguments, and More

Swift 5.2 Brings callAsFunction, Subscript with Default Arguments, and More

Leia em Português

This item in japanese

Bookmarks

The Swift programming language, originally developed by Apple and released in 2014, has just reached version 5.2. Swift 5.2, available in the Xcode 11.4 Beta, is a release designed to include significant improvements in quality and performance. Following the Swift Evolution process, this version brings "callAsFunction", subscript with default arguments, Key Path Expressions as Functions, a new diagnostic architecture, and more.

Swift 5.2 callAsFunction() or "callable values of user-defined nominal types" introduces statically callable values to Swift. Callable values can be called using function call syntax, since they are values that define a function-like behavior. This feature supports argument labels and parameter types, throws and rethrows, and is not constrained to primary type declarations. Furthermore, it is possible to define multiple callAsFunction methods on a single type, and Swift will handle which one to call, similar to a simple overloading.

struct Adder {
    var base: Int
    func callAsFunction(_ x: Int) -> Int {
        return base + x
    }
}

let add3 = Adder(base: 3)
add3(10) // returns 13, same as adder.callAsFunction(10)

Subscripts are shortcuts for accessing member elements of a collection, sequence or list, and can be defined in classes, structures, and enumerations. Developers can use subscripts to set and retrieve values by index without needing to write a method for setting and another for retrieval. In Swift 5.2, it is now possible to add subscripts with default arguments for any type of parameter. The following snippet of code shows the BucketList structure that returns a default value when someone tries to read an index out of bound:

struct BucketList {
    var items: [String]
 
    subscript(index: Int, default: String = "your list is over") -> String {
        if index >= 0 && index < items.count {
            return items[index]
        } else {
            return `default`
        }
    }
}
 
let bucketList = BucketList(items: ["travel to Italy", "have children", "watch super bowl")
print(bucketList[0])
print(bucketList[4])

This code will print "travel to Italy" and then "your list is over" because there is no value at index 4.

The Key Path Expressions as Functions introduces the ability to use the key path expression \Root.value wherever functions of (Root) -> Value are allowed. Considering the following User struct:

struct User {
    let email: String
    let isActive: Bool
}

Suppose we have already created an array of users; to get a list of users before the key path feature, we can do this applying users.map { $0.email } to gather an array of emails, or similarly users.filter { $0.isActive } to get an array of active users.

With the key path, it is now possible to apply users.map(\.email) to gather an array of emails, or users.map { $0[keyPath: \User.email] } to get an array of active users.

Swift 5.2 also brings a new diagnostic architecture designed to improve the quality and precision of error messages when a developer makes a coding mistake. Let's explore some examples of less accurate diagnostics before the new diagnostic architecture, and how it is in Swift 5.2:

Invalid Optional Unwrap

struct S { init(_: [T]) {} } var i = 42 _ = S([i!])

Previously, this resulted in the following diagnostic error: type of expression is ambiguous without more context, and now this is diagnosed as error: cannot force unwrap value of non-optional type 'Int' _ = S<Int>([i!]) ~^.

Argument-to-Parameter Conversion Mismatch

import SwiftUI struct Foo: View { var body: some View { ForEach(1...5) { Circle().rotation(.degrees($0)) } } }

Previously, this resulted in the following diagnostic error: Cannot convert value of type '(Double) -> RotatedShape<Circle>' to expected argument type '() -> _'.  Now this is diagnosed as error: cannot convert value of type 'Int' to expected argument type 'Double' Circle().rotation(.degrees($0)) ^ Double( ).

Other features not directly related to Swift 5.2, but which are worth taking note of, include:

  • Xcode 11.4 beta supports building and distributing macOS apps as a universal purchase. Universal purchase is enabled by default for new Mac Catalyst apps created in Xcode 11.4
  • Build settings have a new evaluation operator, default, allowing developers to specify the default value of a build setting if it evaluates to nil in the context of the evaluation, such as $(SETTING:default=something)
  • Remote Swift packages with tools version 5.2 and above no longer resolve package dependencies that are only used in their test targets, improving performance and reducing the risk of dependency version conflicts

The complete list of features, bug fixes, and known issues can be found on the release notes of the Xcode 11.4 beta.

Rate this Article

Adoption
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

BT