この記事は セーフィー株式会社 Advent Calendar 2023 7 日目の記事です。
はじめに
今年は Swift 5.9 が発表されました。
Macros
実装も AST も使いどころも難しい😣
Observation
iOS 17.0+
だとプロダクション投入はちょっと。。😓
そんな我々のために、 Swift 5.9 を含むここ 1 〜 2 年ほどの Swift アップデートで可能となった、明日からでも使える Swift コードの書き方をいくつか紹介します。
- 個人的によく使う / よく使われるであろうもの
- Swift 5.9 (Xcode 15.0) 以降なら Deployment Target を気にせず利用できる
といったものを中心にまとめました。
Optional Binding
if let hoge = hoge
if let
の shorthand 記法が導入されたため、同名の変数名で unwrap されたものを簡単に用意できるようになりました。
let hoge: Hoge? = ...
if let hoge = hoge {
// unwrap された `hoge` を利用可能
}
let hoge: Hoge? = ...
if let hoge {
// unwrap された `hoge` を利用可能
}
// 複数個も対応
let fuga: Fuga? = ...
if let hoge, let fuga {
// unwrap された `hoge`, `fuga` を利用可能
}
また後述するのですが、 guard let
や、 while let
といった各種 Optional Binding でも同様の記述ができるようになっています。
対応バージョン、関連 Proposal は以下の通りです。
- Swift 5.7 (Xcode 14.0) 〜
- SE-0345
if let
shorthand for shadowing an existing optional variable
guard let self = self else { return } ...
つい手癖で書いてしまう guard let self = self else { return } ...
の処理ですが、段階を踏んでかなり簡潔に記述できるようになりました。
hoge.callback = { [weak self] in
guard let `self` = self else { return }
self.fuga()
}
hoge.callback = { [weak self] in
guard let self else { return } // shorthand 記法
fuga() // `self.` を省略可能
}
だいぶ以前は guard let self = self
ができてしまうのはバグといったような話もあったのですが、複数の Proposal を通して、上述のように Swift らしいシュッっとしたコードに収まるようになりました。
関連する Proposal としては、
-
guard let self = self
が正式に OK になる- Swift 4.2 (Xcode 10.0) 〜
- SE-0079 Allow using optional binding to upgrade
self
from a weak to strong reference
-
guard let self
の shorthand 記法導入 (前セクションの通り)- Swift 5.7 (Xcode 14.0) 〜
- SE-0345
if let
shorthand for shadowing an existing optional variable
-
self.
が省略可能となる- Swift 5.8 (Xcode 14.3) 〜
- SE-0365 Allow implicit
self
forweak self
captures, afterself
is unwrapped
で、 Swift 5.8 で一応の完成を見ています。
if 式 / switch 式
制限はありますが、 if / switch が式 (expression) としても扱えるようになりました。
具体的に何が嬉しいかというと、以下のような書き方ができるようになります。
func hoge(type: MetaType) {
let evaluation: String
switch type {
case .hoge:
evaluation = "good!"
case .fuga:
evaluation = "not bad."
case .piyo:
evaluation = "very bad..."
}
...
}
func hoge(type: MetaType) {
let evaluation: String = switch type {
case .hoge:
"good!"
case .fuga:
"not bad."
case .piyo:
"very bad..."
}
...
}
これまでもコンパイラレベルで変数初期化時の網羅性チェックにてエラーとしてくれていたのですが、より直感的な表現での記述が可能となりました。
また、 Proposal での動機の一つとしても挙げられているのですが、 Kotlin / Android Studio でよく suggest される return
の lift out についても可能となりました。
func hoge(type: MetaType) -> String {
if type == .hoge {
return "hoge"
} else if type == .fuga {
return "fuga"
} else {
return "unknown"
}
}
func hoge(type: MetaType) -> String {
return if type == .hoge {
"hoge"
} else if type == .fuga {
"fuga"
} else {
"unknown"
}
}
注意点として、
Each branch of the if, or each case of the switch, must be a single expression.
であることが求められます。
このため、複数行に渡る複雑な処理やログ出力を仕込むために 1 行追加するといった場合への適用が難しく、他言語での if / switch 式の感覚とは異なる部分があります。
対応バージョン、関連 Proposal は以下の通りです。
- Swift 5.9 (Xcode 15.0) 〜
- SE-0380
if
andswitch
expressions
extension Array where Element = ... { }
配列で表現されるモデルに対して、 extension Array
や extension Sequence
等で便利メソッドを生やすといったことがあるかと思います。
この際、 where Element == ...
でやや冗長な記述が発生するのですが、これについても直感的な記述が可能となりました。
extension Array where Element == Hoge {
func fuga() -> String {
...
}
}
extension Array<Hoge> {
func fuga() -> String {
...
}
}
対応バージョン、関連 Proposal は以下の通りです。
- Swift 5.7 (Xcode 14.0) 〜
- SE-0346 Lightweight same-type requirements for primary associated types
- SE-0361 Extensions on bound generic types
ViewBuilder
に並べられる View の数
SwiftUI に入門すると必ずぶつかる View
が 10 個まで並べられない問題が解消されました。
var body: some View {
VStack {
Text("01")
Text("02")
Text("02")
Text("04")
Text("05")
Text("06")
Text("07")
Text("08")
Text("09")
Group { // `Group` でラップするワークアラウンド
Text("10")
Text("11")
}
}
}
var body: some View {
VStack {
Text("01")
Text("02")
Text("02")
Text("04")
Text("05")
Text("06")
Text("07")
Text("08")
Text("09")
Text("10")
Text("11")
}
}
以前は、
で宣言されていた ViewBuilder.buildBlock
の処理が
でまとまってくれたためで、 Swift 5.9 で導入された Value and Type Parameter Packs が寄与しています。
よくある iOS 17.0+
な 新 API への置き換えではないため、 Deployment Target を気にせず利用できます。
対応バージョン、関連 Proposal は以下の通りです。
- Swift 5.9 (Xcode 15.0) 〜
- SE-0393 Value and Type Parameter Packs
- SE-0398 Allow Generic Types to Abstract Over Packs
参考記事