What’s New in Swift4.2 まとめ

Swift4.2で新しく追加されたものをざっくりまとめました。これからSwift4.2対応するプロジェクトなどがある時に参考程度になれば良いかなと思います。

### 実施環境
```console
Apple Swift version 4.2 (swiftlang-1000.0.16.7 clang-1000.10.25.3)
```

# Conditional conformances

[SE-0143](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md)

みなさんSwift4.1で大喜びした`struct`, `enum`, `class`への条件付きプロトコル適合(ある条件下の時のみプロトコルに準拠させる方法)ですが、下記のような動的なキャストやチェックを行うことはできませんでした。Swift4.2ではその実装が完了し、条件付きプロトコル適合が完成したみたいです。

```swift
// Swift4.1
protocol Bachelor {
    func givesRose()
}

extension Array: Bachelor where Element: Bachelor {
    func givesRose() { forEach { $0.givesRose() } }
}

class Man: Bachelor {
    func givesRose() { print("🌹") }
}

let men = [Man(), Man()]
men.givesRose() // 🌹🌹

// Swift4.2
func dynamicallyBachelor(_ men: Any) {
    if let men = men as? Bachelor {
        men.givesRose()
    } else {
        fatalError("Swift4.1")
    }
}

dynamicallyBachelor(men) // 🌹🌹
```

# Collection of enum cases

[SE-0194](https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md)

Enumの全てのケースが配列で受け取れる`CaseIterable`というプロトコルが追加されました。今まで@sgr-ksmtさんの[Enumのcaseを配列で返すProtocol Extension](https://qiita.com/sgr-ksmt/items/c1c6a91324cf12151256)にお世話になっていました。感謝です。

```swift
enum Fruit: CaseIterable {
    case apple
    case orange
    case banana
}

Fruit.allCases // [apple, orange, banana]
```

このプロトコルもまた*Associated Value*を持ったEnumは準拠できません。Associated Valueがある場合はcaseがいくらでも増やせるのと一緒ですからね、当たり前ですよね。

```swift
// error: type 'Fruit' does not conform to protocol 'CaseIterable'
enum Fruit: CaseIterable {
    case apple(Int)
}
```

# Dynamic member lookup

[SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md)

JavaScript(辞書も含まれちゃうけど)やPythonを書いている人には馴染みのある、動的なメンバへドット表記でアクセサ呼び出しを行う糖衣構文。これがついにSwiftでもできるようになりました。最初はちょっと恐ろしいと思ったのですが、型安全に宣言できるのでやたらむやみに多用しなければ活かせそうな気がします。

```swift
@dynamicMemberLookup
struct Response {
    private var raw = ["foo": "foo", "baz": "baz"]
    subscript(dynamicMember name: String) -> String? {
        get { return raw[name] }
        set { raw[name] = newValue }
    }
}

var response = Response()
response.foo // foo
response.bar // nil
response.bar = "bar"
response.bar // bar
```

APIのレスポンスをデバッグする時や、他の言語のライブラリから値を取得する場合などに使うのがいいのですかね。仕様変更には強くはなりますが、やはり乱用したくないです。

# Compiler diagnostic directives

[SE-0196](https://github.com/apple/swift-evolution/blob/master/proposals/0196-diagnostic-directives.md)

コンパイル時にカスタムなワーニングとエラーを定義できるようになりました。SwiftLintを入れている人は`TODO:`コメントなどでレビュワーに作業中の意思表明をすることありますよね。エラーはプラットフォームが変更された時に使えないものがある時に使うんですかね、他にイメージつかないです。

```swift
func doSomething() {
    #warning("TODO: Implement logic.")
}

if canImport(UIKit)
    // Do something.
#else
    #error("Requires UIKit.")
#endif
```

# Adding remove where method to Collection

[SE-0197](https://github.com/apple/swift-evolution/blob/master/proposals/0197-remove-where.md)

コレクションからある条件下のアイテムを全て削除する為のメソッドが追加されました。今までは`filter(where:)`を使っていましたが、可読性も悪く、メモリが無駄に割り当てられて効率も悪いらしいです。

```swift
var faces = ["😀", "😭", "😄"]
faces.removeAll(where: { $0 == "😭" }) // ["😀", "😄"]
```

# Methods of withUnsafePointer and withUnsafeBytes for immutable arguments

`withUnsafePointer(to:_:)`と`withUnsafeBytes(of:_:)`の引数が`inout`で宣言されてMutableだった為、コピーした値を受け渡したりすることで非効率だということでImmutableに扱えるメソッドが追加されました。

```swift
let x: UInt16 = 0xabcd
let (firstByte, secondByte) = withUnsafeBytes(of: x, { pointer in
    return (pointer[0], pointer[1])
})

String(firstByte, radix: 16) // cd
String(secondByte, radix: 16) // ab
```

# Toggle for Bool

[SE-0199](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md)

今までBool値のToggle処理を行う際は`flag = !flag`のように書いていましたが、`toggle()`メソッドが追加されたことで地味に可読性が高くなりました。(既にExtension定義している人が多いと思いますが)

```swift
var flag = false
// Swift4.1
// flag = !flag
// Swift4.2
flag.toggle() // true
```

# Random numbers and collections

[SE-0202](https://github.com/apple/swift-evolution/blob/master/proposals/0202-random-unification.md)

Swiftでランダムな値を生成する場合Foundationの`arc4random`系を使っていましたが、CのAPIでありクロスプラットフォームで使用できるものがなかったので改めてランダムな値を生成するメソッドと、独自のロジックでランダムな値を生成するGeneratorを定義できるプロトコルが追加されました。

```swift
let randomInt = Int.random(in: 1...10) // 1-10 random number
let randomInt32 = Int32.random(in: .min ... .max) // Int.min-.max random number
let randomDouble = Double.random(in: 0.0..<1.0) // 0.0-1.0 random number
let randomBool = Bool.random() // true or false
```

引数にRangeを渡すだけです。`Int`系だけでなく`Double`や`Float`も可能で`Bool`にまでも用意されています。また`Array`や`Dictionary`、この前仲間入りした`String`などの`Collection`プロトコルに準拠しているものは`randomElement()`でランダムに値を取り出せます。

```swift
let randomChar = "hello".randomElement() // Random character
let randomFruit = ["apple", "orange", "banana"].randomElement() // Random string
```

合わせて`shuffled()`と`shuffle()`(mutatingな方)も追加されました。何でなかったのですかね。ほとんどの人はExtension定義して使っていたんじゃないですかね。

```swift
let shuffledChar = "hello".shuffled() // Shuffled character
var fruits = ["apple", "orange", "banana"]
fruits.shuffle() // Shuffled array
```

上記のランダム関数は`Random.default`というデフォルトのGeneratorを介してランダマイズされたもので、これを独自のものに置き換えられるようになっています。その為には`RandomNumberGenerator`プロトコルに準拠しなくてはなりません。(基本的にDefaultを使うと思いますが)

```swift
/// Mimicking the old random function.
struct OldRandomNumberGenerator: RandomNumberGenerator {
    mutating func next() -> UInt64 {
        return UInt64(arc4random_uniform(.max))
    }
}

var oldGenerator = OldRandomNumberGenerator()
let oldRandomInt = Int.random(in: 0...10, using: &generator) // 1-10 random number
```

# Last item method for Sequence

[SE-0204](https://github.com/apple/swift-evolution/blob/master/proposals/0204-add-last-methods.md)

今まで`first(where:)`などの先頭のアイテムに対して行えていたことが、最後尾のアイテムに対して行えるメソッドが追加されました。

```swift
let range = 0...9
let isEven: (Int) -> Bool = { $0 % 2 == 0 }
let lastEven = range.last(where: isEven) // 8
let lastEvenIndex = range.lastIndex(where: isEven) // inRange(8)
```

それに合わせて先頭のインデックス操作系の下記のメソッドがリネームされました。

* `index(of:)` → `firstIndex(of:)`
* `index(where:)` → `firstIndex(where:)`

# Hashable enhancements

[SE-0206](https://github.com/apple/swift-evolution/blob/master/proposals/0206-hashable-enhancements.md)

Swift4.1の`Hashable`は下記の問題があったので`hashValue`の廃止と`Hasher`というハッシュ関数を持つ構造体が公開されました。よってカスタムな`hashValue`を定義する`Hashable`の準拠の仕方が変わります。

* `^`や`&*`によるビット演算が分かりにくい
* 私のような人間が理解しないで行うとハッシュの衝突が起こる
* 私のような人間が理解しないで行うとパフォーマンスを損なう

```swift
// Swift4.1
struct GridPoint {
    var x: Int
    var y: Int
}

extension GridPoint: Hashable {
    var hashValue: Int {
        return x.hashValue ^ y.hashValue &* 16777619
    }

    static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }
}
```

```swift
// swift4.2
struct GridPoint {
    var x: Int
    var y: Int
}

extension GridPoint: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(x)
        hasher.combine(y)
    }

    static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }
}
```

# All satisfy algorithm for Sequence

[SE-0207](https://github.com/apple/swift-evolution/blob/master/proposals/0207-containsOnly.md)

全てのアイテムが条件を満たしているか調べる際に、今までは`contains(where:)`で無理に行なっていたかと思いますが、かなり可読性が悪いので`Sequence`に`allSatisfy(_:)`メソッドが追加されました。

```swift
let range = 0...9
// Swift4.1
let isSmallerThanTen = !range.contains(where: { $0 >= 10 }) // true
// Swift4.2
let isSmallerThanTen = range.allSatisfy({ $0 < 10 }) // true
```

# Adding offset method to MemoryLayout

[SE-0210](https://github.com/apple/swift-evolution/blob/master/proposals/0210-key-path-offset.md)

`MemoryLayout`型はメモリのサイズやストライドなどを取得できるプロパティやメソッドを持ったEnum(ただの名前空間)みたいです。Swift4.2ではそこにStored Propertyのオフセットアドレスを、Swift4.0から追加された`PartialKeyPath`で取得できるメソッドが追加されました。使ったこともないのであまりよくわからないですが。

```swift
struct GridPoint {
    var x: Int // Offset: 0
    var y: Int // Offset: 8
}

let offset = MemoryLayout<GridPoint>.offset(of: \GridPoint.y) // 8
```

----

眠いからおしまい。