今更ながらSwift5の変更点について調べました（追加関数とコンパイラの更新編）

#前置き

前回の続きです❗️調べだすと結構横道に逸れる（経験不足の必然）のでなかなか大変です…
今回は追加関数とコンパイラの更新についての変更点をできるだけわかりやすく紹介します。

#＠unknownの追加
[[SE-0192]](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md)

switchステートメントは全てを網羅するしないとコンパイラがエラーを返却しますが、defaultに＠unknownをつけることによりエラーではなく警告を出すようにコンパイラの変更がなされました。
これによりまだ仕様的に未確定の状態でも＠unknownをつけておくことで警告として残したまま開発が続けられますね。

```swift
    func showNew(error: PasswordError) {
        switch error {
        case .short:
            print("Your password was too short.")
        case .obvious:
            print("Your password was too obvious.")
        @unknown default:
            print("Your password wasn't suitable.")
        }
    }
```

#リテラルの初期キャストの変更
[[SE-0213]](https://github.com/apple/swift-evolution/blob/master/proposals/0213-literal-init-via-coercion.md)

Swift4.2のコンパイラでは以下のコードはエラーになります。

```swift
// swift4.2ではエラー
print(UInt(0xffff_ffff_ffff_ffff))
```
これは0xffff_ffff_ffff_ffffをまずintにcastしようとするのですが、18446744073709551615なので桁あふれでオーバーフローしてしまいます。なので次のように明示的にcastしてあげる必要がありました。

```swift
print(UInt(0xffff_ffff_ffff_ffff as UInt))
```
しかしswift5のコンパイラからはイニシャライズのcastの型が優先される（UInt()のこと）ようになる為、明示的なcastが不要になります。

```swift
// swift5ではOK
print(UInt(0xffff_ffff_ffff_ffff))
```

#＠dynamicCallableの追加
[[SE-0216]](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md)
＠dynamicCallableは＠dynamicMemberLookupを拡張してPythonなどの他言語との連携性を強化したもので機能としては＠dynamicMemberLookupと同様の機能を実現できます。

＠dynamicMemberLookupをご存知ないかたもいらっしゃると思いますので（私も調べましたw）
こちらの記事が大変わかりやすかったです
[How to use Dynamic Member Lookup in Swift](https://www.hackingwithswift.com/articles/55/how-to-use-dynamic-member-lookup-in-swift)
ざっくり＠dynamicMemberLookupについて説明すると、メンバを動的に作成できるようにして存在しないメンバにもアクセスできるようになるってことです。

```swift
@dynamicMemberLookup
struct Person {
    subscript(dynamicMember member: String) -> String {
        let properties = ["name": "Taylor Swift", "city": "Nashville"]
        return properties[member, default: ""]
    }
}
```
subscriptでメンバの文字列に対応するもの返却しているので存在しない場合は""が返却されるのでエラーにはなりません。
どのように使うかと言うと例えばDBのカラムをfetchして返却するようにして動的に返却できるようにするとか、メンバは動的だけど共通のロジックを通したいってときとかですかね？
こういうのはライブラリとかを作成する方に取っては重宝するかも？です。


# < オペレーターの追加
[[SE-0224]](https://github.com/apple/swift-evolution/blob/master/proposals/0224-ifswift-lessthan-operator.md)
swiftバージョンやSwiftコンパイラのバージョン判定において、 < の演算子が追加されたそうです。

```swift
#if swift(<4.2)
// This will only be executed if the Swift version is less than 4.2.
#endif

#if compiler(<4.2)
// This will only be executed if the Swift compiler version is less than 4.2.
#endif
```
なかったの知りませんでした。。。

#Identity key path
[[SE-0227]](https://github.com/apple/swift-evolution/blob/master/proposals/0227-identity-keypath.md)
そもそも私みたいに経験浅い人はkey pathについて知らない方もいらっしゃると思いますので、そこからはじめましょう。
まず、swiftの変数にキーパスを生成するには以下のように\を使います。

```swift
    struct SwiftVersion {
        var version: String
    }

    var swiftVersion = SwiftVersion(version: "4.2")
    print(swiftVersion.version)
    // 出力結果： 4.2

    // キーパスを使って値を書き換える
    let keyPath = \SwiftVersion.version
    swiftVersion[keyPath: keyPath] = "5.0"

    print(swiftVersion.version)
    // 出力結果： 5.0

    // キーパスを使って参照も可能
    print(swiftVersion[keyPath: keyPath])
    // 出力結果： 5.0
```
このようにメンバの値をキーパスを使って書き換えることができます。
具体的な使用例が思いつかなかったので今回は割愛します🙇‍♂️
で、swift5では何ができるようになったかと言うと全ての変数はselfと言うキーパスでアクセスできるようになったと言うことらしいです。

```swift
var x = 1
print(x) 
// 出力結果： 1

x.self = 2

print(x) 
// 出力結果： 2
print(x.self) 
// 出力結果： 2

// selfが使えるということは以下のようにキーパスを指定することも可能
let id = \Int.self

x[keyPath: id] = 3
print(x[keyPath: id])
// 出力結果： 3
```
パッと具体的な使用例が思い当たらないので思いついたら加筆したいと思います。。。

#try?の仕様変更
[[SE-0230]](https://github.com/apple/swift-evolution/blob/master/proposals/0230-flatten-optional-try.md)
trayの仕様が変更されて冗長なオプショナルが簡略化されます。

```swift
// Swift 4: 'Int??'
// Swift 5: 'Int?'
let result = try? database?.countOfRows(matching: predicate)


// Swift 4: 'String??'
// Swift 5: 'String?'
let myString = try? String(data: someData, encoding: .utf8)
    
// Swift 4: '[String: Any]??'
// Swift 5: '[String: Any]?'
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
```
サンプルにもある通りjsonのキャストとかで使用しているので注意が必要ですね。
既存のコードでアンラップしているとこがあれば見直してみましょう。

#終わりに
普段コンパイラのことまで意識することがないかもしれませんが、そこを意識することによってなぜエラーになるかも理解できるようになるので調べて無駄なことはないですね。
最後は[パッケージマネージャ](https://qiita.com/ikuo-yamashita/items/f0979be6020f534f145e)について調べようと思います。
ではまた👋

#参考
大変お世話になりました。ありがとうございます🙇‍♂️

- [How to use Dynamic Member Lookup in Swift](https://www.hackingwithswift.com/articles/55/how-to-use-dynamic-member-lookup-in-swift)
- [What’s new in Swift 5](https://medium.com/@d_date/whats-new-in-swift-5-70225f063b87)
- [KeyPath 入門](https://www.gaprot.jp/pickup/keypath)