#95 What's new in Swift 4?

どうも、esuiです。
某位置情報ゲームで、レイドバトルが実装されていますが、皆様におきましては楽しまれていますでしょうか?
これが出来てから、外でみんなで楽しめるようになったので非常に良い感じですね。




What's new in Swift 4?


スクリーンショット 2017-08-30 19.20.03.png

さて、本日の1本目は、先日のWWDCでも発表にあったSwift 4について。
Swift 4になって新しくなった点をざっとコードを見ながらという形式で紹介されていました。

ざっくりと


- Swift 3 より変更点は少ないが、それなりの変更点
- ABI安定化はSwift 5に延期されたので依存関係のバイナリは再コンパイルが必要


JSON Encoding and Decoding


- Codableプロトコル追加、NSCodingプロトコルを使用したシリアライズが改善された

struct Person: Codable {
let name: String
let age: Int

init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let taylor = Person(name: "Taylor Swift", age: 27)
// 1
let encoder = JSONEncoder()
let data = try encoder.encode(taylor)
let json = String(data: data, encoding: .utf8)! // {"name":"Taylor
Swift”,"age":27}
// 2
let decoder = JSONDecoder()
let person = try decoder.decode(Person.self, from: data)
let info = "\(person.name) \(person.age)" // Taylor Swift 27


Key-Value Coding


- Objective-cを通さなくてもSwiftのみでKey-Value Codingが使えるようになった
struct Animal {
var name: String

init(name: String, type: AnimalType) {
self.name = name
self.type = type
}
}
var animal = Animal(name: "Cat", type: .cat)
// 1
let nameKeyPath = \Animal.name
let name = animal[keyPath: nameKeyPath] // Cat
// 2
animal[keyPath: nameKeyPath] = “Dog" // Dog


One-Sided Ranges


// Rangeの開始indexと終了indexを推測してくれるようになった
let array = [1, 5, 2, 8, 4, 10]
let firstThree = array[..<3] // [1, 5, 2]
let lastThree = array[3...] // [8, 4, 10]
// 開始Indexがカウント可能なタイプの場合、無限シーケンスを定義することができる
(range: 1...infinity)
let tupleArray = Array(zip(1..., array)) // [(1, 1), (2, 5), (3, 2),
(4, 8), (5, 4), (6, 10)]
// Pattern Matchingにも使える
let favouriteNumber = 10
switch favouriteNumber {
case ..<0:
print("Your favorite number is a negative one.")
case 0...:
print("Your favorite number is a positive one.") // Your favorite number is a positive one.
default:
break
}


Strings as Collections


// StringはCollectionプロトコルに準拠するように
// 1
let swift4String = "Swift 4"
let filteredSwift4String = swift4String.filter { Int(String($0)) == nil }
filteredSwift4String // Swift
// 2
swift4String.count // 7
swift4String.isEmpty // false
swift4String.dropFirst() // wait 4
String(swift4String.reversed()) // 4 tfiwsS


Improved Emoji Strings


// 絵文字の文字数が正しくカウント可能に!
"👩‍".count // Swift 4: 1, Swift 3: 2
"👍".count // Swift 4: 1, Swift 3: 2
"👨‍❤️‍💋‍👨".count // Swift 4: 1, Swift 3: 4


Multiple Line Strings


let star = "⭐"
let introString = """
A long time ago in a galaxy far,
far away....
You could write multi-lined strings
without "escaping" single quotes.
The indentation of the closing quotes
below deside where the text line
begins.
You can even dynamically add values
from properties: \(star)
"""


swap vs swapAt


var numbers = [1, 5, 2, 8, 4, 10]
// Swift 3(この関数は削除された)
swap(&numbers[0], &numbers[1]) // [5, 1, 2, 8, 4, 10]
// Swift 4
numbers.swapAt(0, 1) // [5, 1, 2, 8, 4, 10]
// ただし、変数のswap関数は残されている
var a = 1
var b = 2

// 1
swap(&a, &b)
// 2
(a, b) = (b, a) // この方法を推奨している


Improved Dictionaries Initialization 1


// (Key, Value)配列からdictioanryを生成
let tupleArray = [("Monday", 30), ("Tuesday", 25), ("Wednesday", 27),
("Thursday", 20), ("Friday", 24), ("Saturday", 22), ("Sunday", 26)]
let dictionary = Dictionary(uniqueKeysWithValues: tupleArray) //
["Tuesday": 25, "Saturday": 22, "Sunday": 26, "Friday": 24, "Monday":
30, "Wednesday": 27, "Thursday": 20]

// 2つの配列からdictionaryを生成
let keys = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday"]
let values = [30, 25, 27, 20, 24, 22, 26]
let newDictionary = Dictionary(uniqueKeysWithValues: zip(keys, values))
// ["Tuesday": 25, "Saturday": 22, "Sunday": 26, "Friday": 24,
"Monday": 30, "Wednesday": 27, "Thursday": 20]


Improved Dictionaries Initialization 2


// 重複したKeyをどうするかdictionary初期化時に選べるように
let duplicatesArray = [("Monday", 30), ("Tuesday", 25), ("Wednesday",
27), ("Thursday", 20), ("Friday", 24), ("Saturday", 22), ("Sunday",
26), ("Monday", 28)]
let noDuplicatesDictionary = Dictionary(duplicatesArray,
uniquingKeysWith: min) // ["Tuesday": 25, "Saturday": 22, "Sunday": 26,
"Friday": 24, "Monday": 28, "Wednesday": 27, "Thursday": 20]

// KeyとValueをマージしたdictionaryを得る
let newKeys = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday", "Monday"]
let mergedKeysAndValues = Dictionary(zip(newKeys, repeatElement(1,
count: newKeys.count)), uniquingKeysWith: +) // ["Tuesday": 1,
"Saturday": 1, "Sunday": 1, "Friday": 1, "Monday": 2, "Wednesday": 1,
"Thursday": 1]


Improved Dictionaries Initialization 3


// グルーピングしたDictionaryを作成可能に
let scores = [7, 20, 5, 30, 100, 40, 200]
let groupedDictionary = Dictionary(grouping: scores, by:
{ String($0).count }) // [2: [20, 30, 40], 3: [100, 200], 1: [7, 5]]


Dictionary Default Values


var seasons = ["Spring" : 20, "Summer" : 30, "Autumn" : 10]
// 1
// Swift 3
let winterTemperature = seasons["Winter"] ?? 0 // 0
// Swift 4
let anotherWinterTemperature = seasons["Winnter", default: 0] // 0
// 2
// Swift 3
if let autumnTemperature = seasons["Autumn"] {
seasons["Autumn"] = autumnTemperature + 5
}
// Swift 4
seasons["Autumn", default: 0] + 5


Map


var seasons = ["Spring" : 20, "Summer" : 30, "Autumn" : 10]
// dictionayを返すmapValuesの追加
let mappedDictionary = seasons.mapValues { $0 * 2 } // ["Summer": 60,
"Spring": 40, "Autumn": 30]


Filtering 1


// Dictionaryのfilterが返す戻り値の変更
var seasons = ["Spring" : 20, "Summer" : 30, "Autumn" : 10]
// Swift 3
let filteredArray = seasons.filter { $0.value > 15 } // [(key:
"Summer", value: 30), (key: "Spring", value: 20)] Arrayが返される

// Swift 4
let filterdDictionary = seasons.filter { $0.value > 15 } // ["Summer":
30, "Spring": 20] Dictionaryが返される


Filtering 2


// Setのfilterが返す戻り値の変更
var categories: Set = ["Swift", "iOS", "macOS", "watchOS",
"tvOS"]
// Swift 3
let filteredCategories = categories.filter { $0.hasSuffix("OS") } //
["watchOS", "iOS", "macOS", "tvOS"] Arrayが返される

// Swift 4
let filteredCategories = categories.filter { $0.hasSuffix("OS") } //
Set["watchOS", "iOS", "macOS", “tvOS”] Setが返される


Mixing Classes with Protocols


protocol Drawable {}
protocol Colourable {}
class Shape {}
// Swift 3
class Circle: Shape, Drawable, Colourable {} // サブクラス化が必要だった
let circle: Circle

// Swift 4
// 1
let newCircle: Shape & Drawable & Colourable
// 2
typealias DrawableColourableShape = Shape & Drawable & Colourable
let anotherNewCircle: DrawableColourableShape


Constrained Associated Types in Protocols


// プロトコルで定義したエイリアスにwhereで制約が付けられるように
protocol SequenceProtocol {
associatedtype SpecialSequence: Sequence where SpecialSequence.Element: Equatable
}


Generic Subscripts


struct GenericDictionary {
private var data: [Key: Value]

init(data: [Key: Value]) {
self.data = data
}

subscript(key: Key) -> T? {
return data[key] as? T
}
}
var earthData = GenericDictionary(data: ["name": "Earth", "population": 7500000000, "moons": 1])
let earthDataName: String? = earthData["name"] // Earth
let earthDataPopulation: Int? = earthData["population"] // 7500000000


Private vs Fileprivate in Extensions


class Person {
private let name: String // extension内でアクセスできるようになった
private let age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
extension Person {
func info() -> String {
return "\(self.name) \(self.age)" // Swift 3 ではエラーになる
}
}


Limiting @objc inference


// Swift APIをObjective-Cに公開、または、#selectorと#keyPath利用時は@objcを推
論され隠されていたが、明示させるように変更された
class ObjectiveCClass: NSObject {
@objc let objectiveCProperty: NSObject // 明示する
@objc func objectiveCMethod() {} // 明示する

init(objectiveCProperty: NSObject) {
self.objectiveCProperty = objectiveCProperty
}
}
let selector = #selector(ObjectiveCClass.objectiveCMethod)
let keyPath = #keyPath(ObjectiveCClass.objectiveCProperty)


NSNumber Improvements


// オーバーフローバグの修正
let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231(999%2^8=231)


参考


- https://www.raywenderlich.com/163857/whats-new-swift-4
- http://www.appcoda.com/swift4-changes/

まとめ


- モダンな言語は楽しいのでやりがいもある
- Swift いいぞー

とのことでした。

毎年絶対にバージョンアップしなきゃいけないというのがなかなか大変そうですが、
最近のモダンな言語のいいところがどんどん入って来ているようなので、
やってて楽しそうなのは間違い無さそうですね。

この記事へのコメント