Swift 4 is the latest major release from Apple scheduled to be out of beta in the fall of 2017. Its main focus is to provide source compatibility with Swift 3 code as well as working towards ABI stability.

Prerequisite 

Swift 4 is included in Xcode 9. To support Xcode 9 we need to upgrade to macOS Sierra. You can download the latest version of Xcode 9 from Apple’s developer portal (you must have an active developer account).

Migrating to Swift 4

The migration from Swift 3 to 4 will be much less cumbersome than from 2.2 to 3. In general, most changes are additive and shouldn’t need a ton of personal touch. Because of this, the Swift migration tool will handle the majority of changes for you.

Xcode 9 simultaneously supports both Swift 4 as well as an intermediate version of Swift 3 in Swift 3.2. Each target in your project can be either Swift 3.2 or Swift 4 which lets you migrate piece by piece if you need to.

When you’re ready to migrate to Swift 4, Xcode once again provides a migration tool to help you out. In Xcode, you can navigate to Edit/Convert/To Current Swift Syntax… to launch the conversion tool.

After selecting which targets you want to convert, Xcode will prompt you for a preference on Objective-C inferencing. Select the recommended option to reduce your binary size by limiting inferencing.

API Changes

Strings

String will be treated as collection 

Just like it was in Swift version 1.x Strings can be treated as collection. You no longer need to write string.characters.xxx to perform string manipulation.

let message = "Message!"
message.count // no need of message.characters.count
for character in message {  // no need of message.characters
    print(character)
}

Along with logical iteration through String we can perform following operations.

message.count       // 8
message.isEmpty     // false
message.dropFirst() // "essage!"
String(message.reversed()) // "!egasse"

Multi-Line String Literals

Swift 4 has addressed this issue by giving Multi line string literal support.To begin string literal add three double quotes marks (”””) and press return key, After pressing return key start writing strings with any variables , line breaks and double quotes just like you would write in notepad or any text editor. To end multi line string literal again write (”””) in new line.

let multiLineStringLiteral = """
This is one of the best feature add in Swift 4
It let’s you write “Double Quotes” without any escaping
and new lines without need of “\n”
"""
print(multiLineStringLiteral)

Which produces following output.

“This is one of the best feature add in Swift 4 
It let’s you write “Double Quotes” without any escaping
and new lines without need of “\n””

Unicode characters count 

Another great improvement is how String interprets grapheme clusters. This resolution comes from the adaptation of Unicode 9. Previously, unicode characters made up of multiple code points resulted in a count greater than 1. A common situation where this happens is an emoji with a selected skin-tone. Here are a few examples showing the before and after behavior:

"👩‍💻".count // Now: 1, Before: 2
"👍🏽".count // Now: 1, Before: 2
"👨‍❤️‍💋‍👨".count // Now: 1, Before, 4

MutableCollection.swapAt

New swap function now takes indices of elements which are to be swapped.

var names = [“Sandesh”,”Harshal”,”Dhiraj”,”Saurabh”]
names.swapAt(2,3)

Which will produce following output.

[“Sandesh”, “Harshal”, “Saurabh”, “Dhiraj”]

One-Sided Ranges

let names =[“Sandesh”,”Harshal”,”Dhiraj”,”Saurabh”,"Niraj"]
let firstTwoString = names[..<2]
let lastThreeString = names[2…]
print(firstTwoString)
print(lastThreeString)

Which will produce following output.

[“Sandesh”,”Harshal”] // [..<2] => [0,1]
[”Dhiraj”,”Saurabh”, "Niraj"] // names[2…] => [2,3,4]

Dictionary and Set

Create a dictionary using array elements.

let friends =[“Sandesh”, “Harshal”, “Saurabh”]
let friendDictionary = Dictionary(uniqKeysWithValues: zip(1...,friends ))
print(friendDictionary)

Which will produce following output.

[2: “Vinay”, 3: “Vishal”, 1: “Vipul”]

Another Example :

let friendName =[“Sandesh”, “Harshal”, “Saurabh”]
let friendAge = [26, 29, 25] 
// Dictionary from sequence of keys-values
let starDistanceDict = Dictionary(uniqueKeysWithValues: zip(nearestStarNames, nearestStarDistances)) 
// ["Saurabh": 25, "Sandesh": 26, "Harshal": 29]

 

Provide default value if value for key does not exist

In following statement. in following statement if there is no value for key 10 found then it will print default value.

print(friendsDictionary[10, default: “No Friends Exist yet!”])

Which will produce following output.

No Friends Exist yet!

 

Partition array in different buckets

let mates = [“Akshay”, “Anil”, “Vishal”, “Vinay”, “Ankit”]
let buckets = Dictionary(grouping: mates, by: { $0.first! })

We have created different buckets where each bucket will contain mates having same first character in their name. Output will be as below.

[“A”: [“Akshay”, “Anil”, “Ankit”], “V”: [“Vishal”, “Vinay”]]

Duplicate Key Resolution

You can now handle initializing a dictionary with duplicate keys any way you’d like. This helps avoid overwriting key-value pairs without any say in the matter:

// Random vote of people's favorite stars
let favoriteStarVotes = ["Alpha Centauri A", "Wolf 359", "Alpha Centauri A", "Barnard's Star"]

// Merging keys with closure for conflicts
let mergedKeysAndValues = Dictionary(zip(favoriteStarVotes, repeatElement(1, count: favoriteStarVotes.count)), uniquingKeysWith: +) 
// ["Barnard's Star": 1, "Alpha Centauri A": 2, "Wolf 359": 1]

The code above uses zip along with the shorthand + to resolve duplicate keys by adding the two conflicting values.

Filtering

Both Dictionary and Set now have the ability to filter results into a new object of the original type:

// Filtering results into dictionary rather than array of tuples
let closeStars = starDistanceDict.filter { $0.value < 5.0 }
closeStars // Dictionary: ["Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Alpha Centauri B": 4.37]

Dictionary Mapping

Dictionary gained a very useful method for directly mapping its values:

// Mapping values directly resulting in a dictionary
let mappedCloseStars = closeStars.mapValues { "\($0)" }
mappedCloseStars // ["Proxima Centauri": "4.24", "Alpha Centauri A": "4.37", "Alpha Centauri B": "4.37"]

Swift Archival & Serialization

Swift 4 comes with built in encoders and decoders for JSON. JSON <-> Model conversion is comes built in ( Although you can customize that behavior if required ).

Define Models

struct Address:Codable {
  var street:String
  var zip:String
  var city:String
  var state:String
}
struct Person: Codable {
  var name: String
  var address: Address
}

Let’s fill out some details

let address = Address(street: “Apple Bay Street”, zip: “94608”, city: “Emeryville”, state: “California”)
let person = Person(name: “Steve Jobs”, address: address)

Encoding ( Model -> JSON )

let encoder = JSONEncoder() // Define JSONEncoder
if let encoded = try? encoder.encode(person) {
  if let json = String(data: encoded, encoding: .utf8) {
    print(json)
  }
}

It will print following output. ( No more third party libs to convert model into JSON 🙂 )

{“name”:”Steve Jobs”,”address”:{“state”:”California”,”street”:”Apple Bay Street”,”zip”:”94608",”city”:”Emeryville”}}

Decoding ( JSON -> Model )

let decoder = JSONDecoder()
if let decoded = try? decoder.decode(Person.self, from: encoded) {
  print(decoded.name)
  print(decoded.address)
}

It will print following output.

Steve Jobs
Address(street: “Apple Bay Street”, zip: “94608”, city: “Emeryville”, state: “California”)

 

Private Access Modifier

An element of Swift 3 some haven’t been too fond of was the addition of fileprivate. In theory, it’s great, but in practice its usage can often be confusing. The goal was to use private within the member itself, and to use fileprivate rarely in situations where you wanted to share access across members within the same file.

The issue is that Swift encourages using extensions to break code into logical groups. Extensions are considered outside of the original member declaration scope, which results in the extensive need for fileprivate.

Swift 4 realizes the original intent by sharing the same access control scope between a type and any extension on said type.

struct SpaceCraft {
  private let warpCode: String

  init(warpCode: String) {
    self.warpCode = warpCode
  }
}

extension SpaceCraft {
  func goToWarpSpeed(warpCode: String) {
    if warpCode == self.warpCode { // Error in Swift 3 unless warpCode is fileprivate
      print("Do it Scotty!")
    }
  }
}

let enterprise = SpaceCraft(warpCode: "KirkIsCool")
//enterprise.warpCode  // error: 'warpCode' is inaccessible due to 'private' protection level
enterprise.goToWarpSpeed(warpCode: "KirkIsCool") // "Do it Scotty!"

This allows you to use fileprivate for its intended purpose rather than as a bandaid to code organization.

240_F_77959340_hWLiOY93juohUoXqjKzqPB79552nw8XU

Hope you find this blog useful. Please feel free to contact with me in case you have any query, suggestions.  You can comment, like and follow posts.

You can request any topic related to Swift and iOS development.

Donate any small amount you think for this knowledge to grow this forum.                        

To Donate –  paypal.me/SandeshSardar

download

Advertisements