What’s New In Swift 5.0?

Author - Krishna Baldha

Introduction:

The latest version of Swift programming language, Swift 5.0 is released in March 2019. It is available in Xcode 10.2. One of the main features of Swift 5 is ABI Stability which provides binary compatibility between applications and swift libraries which is compiled in a different version of the Swift language.

ABI

ABI stands for Application Binary Interface. Application Binary Interface defines how application data and its structures are accessed in machine code, how data is provided as input and read as an output. It defines low-level details such as how a function is called, what arguments are passed, how data occupies memory and how to access it. The main advantage of ABI stability is that developers don’t have to worry about swift new version compatibility of their app. It also reduces the size of the application because there is no need to add Swift standard library in the framework folder and also language changes become smaller and less frequent. Below is some new features in Swift 5.0.

Enhancing Raw String

An escape character (\) is used to create a special interpretation of subsequent characters within a string literal in Swift 4.2. We use escape sequences like \”, \\ and \u{n} to represent quotes, backslashes, and Unicode as follow.

let string = "This is the string example with \"quotes\" and backslashes(\\)"
//output : This the string example with “quotes” and backslashes(\)

let multiline = """
                \"This is the
                multiline string example
                with more \"quotes\" and \"backslashes\"(\\)\"
                """
//output :   This is the
//	     multiline string example
//	     with more “quotes” and “backslashes”(\)

In Swift 5.0, no need to use this. A raw string literal is used to ignore the escape sequence. Quotes and backslashes can be used by adding # before and after string as follow.

let string = #"This is the string example with “quotes” and backslashes(\)"#
//output : This the string example with “quotes” and backslashes(\)


let multiline = #"""
                "This is the
                multiline string example
                with more "quotes" and "backslashes"(\)"
                """#
//output :   This is the
//			multiline string example
//			with more “quotes” and “backslashes”(\)

If there is # in your string then you can display it by adding one more # at beginning and end in your string. Make sure the number of # at beginning and end must be same.

let string = ##”this is the string example with # character.”##

This is also useful to write RegEx without lots of backslashes. Before Swift 5.0, it was like below code

let regex = "^[A-Za-z0-9 !\"#$%&'()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~].{8,}$"

In Swift 5.0, it is written as below

let regex =  #”^[A-Za-z0-9 !"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~].{8,}$”#

isMultipleOf Added

In Swift4.0, if you want to find whether one number is multiple of another number or not? Then % operator is used. But before this operation, you need to check whether the second number is zero or not as below

let Num1 = 15
let Num2 = 3
if Num2 != 0 && Num1 % Num2 == 0 
{
    print("\(Num2) * \(Num1 / Num2) = \(Num1)")
}

In Swift5.0, there is no need to check whether the second number is zero or not. You can use isMultiple function. If works even if you set zero to the second number as below

if Num1.isMultiple(of: Num2) 
{
    print("\(Num2) * \(Num1 / Num2) = \(Num1)")
}

Count(where:) Added

In an earlier version of swift,  for finding a specific object from the array, we use filter(_:) with predicates. A simple code with filter(_:) is given below

let marks = [75, 39, 82, 23, 56, 46]
let pass = marks.filter({ $0 > 35 })
print("Passing Subjects : \(pass)")

//output : Passing Subjects : [75, 39, 82, 56, 46]

Now if you want to get just count of passing subjects, not the scores, then you need to use count function for that.

let marks = [75, 39, 82, 23, 56, 46, 64, 98]
let passCount = marks.filter({ $0 > 35 }).count
print("Passing Subjects Count : \(passCount)")

//output : Passing Subjects Count : 5

Although we don’t have any use of which subject’s mark is greater than 35 in counting subjects, we have to filter all subject and then count them. This is an unnecessary double process.

In Swift 5.0, count(where:) is added for counting filter values as below

let marks = [75, 39, 82, 23, 56, 46, 64, 98]
let passCount = marks.count(where : { $0 > 35 })
print("Passing Subjects Count : \(passCount)")

//output : Passing Subjects Count : 5

Handling Future Enum Cases

For switch statement, all cases must be covered. This is the swift’s one of the security feature. But this feature sometimes not suitable in our coding. For example, if we want to add a new case in the future then it is automatically called a default statement because we didn’t add this new feature. Now this problem can be solved with @unknown attribute.

Example of this situation is below

enum Mobile 
{
    case iPhone 7
    case iPhone 8
    case iPhone X
}

We can write function like below for getting language description using switch blog

func getSelectedMobilePrice(name: Mobile) 
{        
    switch name {
    case .iPhone 7:
        print(“Price : $649“)
    case . iPhone 8:
        print("Price : $767”)
    default:
        print(“We are not selling iPhoneX“)
    }
}

For the first two cases, there will be no issue for future changes but for the Gujarati language, we have to write it in default. Now if the new language (ex, Marathi) will be added in future, then for that default blog will execute. But this is not correct for the Marathi language. Swift doesn’t warn about this because it is not an issue technically. The solution of this issue is, add @unknown attribute before default like below.

func getSelectedLanguageDesc(name: LanguageName) 
{
    switch name {
    case .iPhone 7:
        print(“Price : $649“)
    case . iPhone 8:
        print("Price : $767”)
    unknow default:
        print(“We are not selling this mobile“)
    }
}

Now this code give warning that it is not exhausting. So in future if new case will be added, it will give warning but your code won’t break.

Flatten Nested Optionals

Swift allows to create nested optionals. for example

func getResult() -> Any
{
    return "Result"
}
    
func getOptionalResult() -> Any?
{
    return "OptionalResult"        
}
        
let a = getResult() as? String
let b = getOptionalResult()() as? String  
print(“\(a), \(b)”)

//output : Optional<String>, Optional<String>

Here function getResult produces non optional value and function getOptionalResult produces option value. But a and b both store optional value. In swift 4.2 , we can create nested optionals using try? as below

let objCourse: Course?

objCourse is an optional value here. If we want to find completion year of course then following code is used

let completionYear = try? objCourse?.getCompletionYear()

Here, getCompletetionYear function also gives optional value. So in the above version of Swift 5.0, the type of output will be Optional so you have to unwrap twice for getting value. But in Swift 5.0, it does not create nested optionals. It returns optional so you need to unwrap only once in swift 5.0.

Identify Key Path

.self is used for accessing the values in earlier version of Swift 5.0 .

import UIKit

class Student {
    let standard: Int
    let marks: Int
    
    init(standard: Int, marks: Int)
    {
        self.standard = standard
        self.marks = marks
    }
}

var objStudent = Student(standard: 5, marks: 78)
objStudent.self = Student(standard: 6, marks: 90)
print(objStudent.standard)
print(objStudent.marks)

In the above example, we created Student class and its variables. First, we assigned value to the object of Student and then replace that object with the new one. We can change the value by .self one time only.

Identify key path is added to Swift 5.0 for accessing values. You need to add \.self to change the value of student object.

objStudent[keyPath: \.self] = Student(standard: 6, marks: 90)

Properties added to Character

Swift 5.0 added many new properties to character that makes easier to use characters.

1.) isNewLine :- This property checks whether character represents a new line or not and return bool value.

2.) isNumber :- This property checks whether character represents a number or not and return bool value.

3.) isLetter :- This property checks whether character represents a letter or not and return bool value.

4.) uppercased() :- This property returns character in upper case same as String.

5.) lowercased() :- This property returns character in lower case same as String.

6.) isUpperCase :- This property checks whether character is uppercase or not and return bool value.

7.) isLowerCase :- This property checks whether character is lowercase or not and return bool value.

Example

let str = "AB01c8"
print(str.first.isNewLine)      //false
print(str.last.isNumber)        //true
print(str.first.isLetter)       //true
print(str.first.uppercased())   //A
print(str.first.lowercased())   //a
print(str.first.isUpperCase)    //true
print(str.first.isLowerCase)    //false

You can find many other new characters property from SE-0221.

Properties added to Unicode.Scalar

We can find number count from string with Unicode scalar in Swift 4.2 like below

let str = "AC120G"
var count = 0
str.unicodeScalars.forEach
{
    count += (65...90) ~= $0.value || (97...122) ~= $0.value ? 1 : 0
}
print("number count = \(count)”)

In Swift 5.0, many properties are added to the Unicode scalar. We can write above code in Swift 5.0 like this.

str.unicodeScalars.forEach
{
    count += $0.properties.isAlphabetic ? 1 : 0
}

You can find many other Unicode scalar property from SE-0211.

CompactMapValues() function added to Dictionary

MapValues, reduce and filter are used to filter values from dictionary as below

let dict: [String: String?] = ["1": "value1", "2": "value2", "3": nil]
let dictFinal1 = dict.filter { $0.value != nil }.mapValues { $0! }
let dictFinal2 = dict.reduce(into: [String: String]()) { (result, item) in result[item.key] = item.value }
// dictFinal1 == ["1": "value1", "2": "value2"]
// dictFinal1 == ["1": "value1", "2": "value2"]

As above, we can find and remove nil value object from a dictionary using filter or reduce. Both generate the same results. But Swift 5.0 adds new compactMapValues() function to the dictionaries. We can write above code in Swift 5.0 like this

let dictFinal = dict.compactMapValues({$0})
// dictFinal == ["1": "value1", "2": "value2"]

Dictionary Literals to KeyValuePairs

DictionaryLiteral is a little bit confusing and misnamed because it is neither a dictionary or a literal. It is just a key pair values.

In Swift 4.2

let marks: DictionaryLiteral = ["student1": "62", "student2": "85", "student3": "92"]

In Swift 5.0

let marks: KeyValuePairs = ["student1": "62", "student2": "85", "student3": "92"]

String Interpolation Updates

In Swift 4.2, String interpolation can be implemented by interpolating segments like below

let statement1 = "There are many updates in Swift 5.0 "
let segment1 = String(stringInterpolationSegment: statement1)
let statement2 = "and it makes easier."
let segment2 = String(stringInterpolationSegment: statement2)
let strFinalStatement = String(stringInterpolation: segment1, segment2)
// “There are many updates in Swift 5.0 and it make easier.”

In Swift 5.0, there are changes in string interpolation. First, create DefaultStringInterpolation object with capacity and interpolation count. And then call appendLiteral(_:) or appendInterpolation(_:) for adding literal and interpolation values. Finally, the interpolated string can be created by init(stringInterpolation:) method. Example with all this is given below

var interpolation = DefaultStringInterpolation(literalCapacity : 55, interpolationCount: 1)
interpolation.appendLiteral("There are many updates in Swift 5.0 ")
interpolation.appendLiteral("and it makes easier.")
let strFinalStatement = String(stringInterpolation: interpolation)

Result type added to Standard Library

Swift 5.0 adds completely new type result to the standard library. It gives a simple and easy way to handle the error. It can be implemented as an enum which has two cases: Success and Failure. Success can be any type but Failure must be of type Error. Now you can use this type in your completion handler for getting the result. Let’s take one enum of type Error for failure handling.

enum ConnectionError: Error 
{
    case wrongUrl
}

For connecting to server, below function is written.

func connectToServer(strUrl: String, completionHandler: @escaping (Result<Int, NetworkError>) -> Void)  {
   
    guard let url = URL(string: strUrl) else {
        completionHandler(.failure(.wrongUrl))
        return
    }
    
    print("connecting")
    completionHandler(.success("successfully connected"))
}

Now if you call this function by correct URL then the result will be success as below

connectToServer(strUrl: "www.google.com") { result in
    
    switch result {
    case .success(let strResult):
            print(strResult)
    case .failure(let error):
            print(error.localizedDescription)
    }
}
// “successfully connected”

But if you pass wrong url then result will failure

connectToServer(strUrl: “testing .com“) { result in
    
    switch result {
    case .success(let strResult):
            print(strResult)
    case .failure(let error):
            print(error.localizedDescription)
    }
}
// wrongUrl

Dynamically callable types

@dynamicCallble is added to swift, which marks a type as being callable directly. In swift 4.2 @dynamicMemberLookup is available which make swift code easier and dynamic such as Python and JavaScript. @dynamicCallable is the extension of this attribute with the same purpose.

Here is the example :-

@dynamicCallable
class TestDynamic {
    func dynamicallyCall(withArguments params: [Double]) -> Double? {
        guard !params.isEmpty else {
            return nil
        }
        return params.max()
    }
    
    func dynamicallyCall(withKeywordArguments params: KeyValuePairs<String, Int>) -> Double? {
        guard !params.isEmpty else {
            return nil
        }
        return params.reduce(0) { $1.key.isEmpty ? $0 : $0 + $1.value }
    }
}

let objTest = TestDynamic()
objTest() // nil
objTest(8, 10, 25) // 25
objTest(first: 8, 2, second: 2) // 10

In the above example, @dynamicCallable added to the function TestDynamic. Then to confirm this @dynamicCallable, we have to implement two methods
1. dynamicallyCall(withArguments:)
2. dynamicallyCall(withKeywordArguments:)
Then create an object of class TestDynamic and you can use these function as above.

There are some rules of @dynamicCallable.
1. It can be added only to the primary definition. Can’t be added to the extension.
2. It can be applied to the structs, classes, protocols, and enums.
3. Other methods can be added with these methods and they will work as normal methods.
4. If you implement withKeyworkArguments: method and not implement withArguments: method then you can still call your type with our parameter. You will get an empty string for the keys.

Subsequence removed

In Swift 4.2, Sequence declares several methods that return a SubSequence

func dropFirst(_:) -> SubSequence
func dropLast(_:) -> SubSequence
func drop(while:) -> SubSequence
func prefix(_:) -> SubSequence
func prefix(while:) -> SubSequence
func suffix(_:) -> SubSequence
func split(separator:) -> [SubSequence]

but in Swift 5.0, SubSequence is replaced with concrete type of Sequences.

let sequence = [5, 2, 7, 4]
sequence.dropFirst(2)

// [7,4]

Here in Swift 4.2 this returns type SubSequence with above output and for Swift 5.0, it returns same output with type [Element].

Package Manager Setting Updates

In Swift 5.0, you can specify the minimum required platform deployment target version in Package.swift file as follow

let package = Package(name: “Test”,
    platforms: [
    .macOS(.v10_14_1), .iOS(.v12_3), .tvOS(.v12_3), .watchOS(.v5)
    ])

Remove Customization Points From Collection.

In Swift 5.0, the following customization points have been removed from the standard library.

1. map, forEach and filter from Sequence
2. first, prefix(uptown:), prefix(through:) and suffix(from:) from Collection
3. last from BidirectionalCollection

Initializing Literals Via Coercion

If types conform to the literal protocol, literal initializers coerce the literal to its type.

struct Test: ExpressibleByIntegerLiteral {
    
    typealias IntegerLiteralType = Int
    
    var objTest: Int
    
    init(integerLiteral value: IntegerLiteralType) {
        self.objTest = value
    }
    
    init?(_ possibleQuestion: IntegerLiteralType) {
        return nil
    }
    
}

// Swift 4.2
let objTest1 = Test(3) // 'nil'
let objTest2 = 3 as Test // Test(objTest: 3)


// Swift 5.0
let objTest1 = Test(3) // Test(objTest: 3)
let objTest2 = 3 as Test // Test(objTest: 3)

In the above example, type Test confirms to ExpressibleByIntegerLiteral. Then Test doesn’t create a literal of type Test with calling an initializer of Test with a value of the protocol’s default literal type but it creates a literal of type Test using the corresponding protocol.

Deprecate String Index Encoded Offsets

In Swift 4.2, Strings are encoded with UTF-16. So encodedOffset returns an offset into the UTF-16 format as below

let str = "String With Swift 4.2"
let offset = str.endIndex.encodedOffset

Strings now uses UTF-8  in Swift 5.0. So above code will not work. Swift 5.0 replaces encodedOffset with utf16Offset(in:) as below

let str = "String With Swift 4.2"
let offset = str.endIndex.utf16Offset(in: str)

Support ‘less than’ operator in compilation conditions

In Swift 4.2, “ >=“ operator is used for compilation conditions like below

#if !swift(>=5.0)
    print("Swift 4.2 available")
#else
    print("Swift 5.0 available")
#endif

#if !compiler(>=5.0)
    print("Swift Compiler 4.2 available")
#else
    print("Swift 5.0 available")
#endif

Swift 5.0 adds new operator “<“, which makes code more readable and easier. Now for above example, new code will like below

#if swift(<5.0)
    print("Swift 4.2 available")
#else
    print("Swift 5.0 available")
#endif

#if compiler(<5.0)
    print("Swift Compiler 4.2 available")
#else
    print("Swift 5.0 available")
#endif

Support Array instead of Variadic parameters for Enum

In Swift 4.2, we can use variadic parameters for enum cases as below

enum Subject
{
    case Chapter(_: String...)
}

func getChapter() -> Subject
{
    return .Chapter("Chapter1", "Chapter2", "Chapter3")
}

But in Swift 5.0, this will not work. This will general an error. You have to use Array instead of Variadic parameter as below

enum Subject
{
    case Chapter([String])
}

func getChapter() -> Subject
{
    return .Chapter([“Chapter1", "Chapter2", "Chapter3”])
}

Don’t miss the next post!

Loading

Related Posts