TCAバージョン1.4のマイグレーションまとめ
何があった?
- TCAの1.4のアップデートが来ました(2023/11/22時点で1.4.2が最新リリース)
-
Macros
の対応が主な内容でマイグレーションガイドが公開されています- 本稿はこの記事の内容と同じです
Reducerマクロ
-
Reducerのプロトコルを外して
@Reducer
をつけるだけ+@Reducer -struct Counter: Reducer { +struct Counter { // ... }
-
Reducerマクロ単体ではほぼReducerプロトコルと同じだけど、マクロでしか利用できない追加機能があります(以下に続く)
CaseKeyPathの導入
-
KeyPathでCasePathを記述できるようになった
Reduce { state, action in // ... } -.ifLet(\.child, action: /Action.child) { +.ifLet(\.child, action: \.child) { ChildFeature() }
-
1.4~ではCasePathが
soft-deprecated
に- soft-deprecated: 将来的に廃止…くらいのやつ
-
Reducerマクロを適用したReducerでのみ利用できる
-
CasePathable
マクロの導入によって、実行時のリフレクションの必要がなくなった- リフレクション: 自分自身の構造やプロパティを調査すること
- https://github.com/pointfreeco/swift-case-paths/blob/1.0.0/Sources/CasePaths/EnumReflection.swift
TestStoreの改善
-
CaseKeyPathによってTestStoreの
receive
メソッドで具体的なenumを記述する必要がなくなった-store.receive(.child(.presented(.response(.success("Hello!"))))) +store.receive(\.child.presented.response.success)
-
PresentationActionであればさらに省略した記述ができる
store.receive(\.child.response.success)
-
receiveで具体的なenumを受け取る(=比較する)必要がなくなったため、
Action: Equatable
の必要がなくなった-
if you use this style of action receiving exclusively
という条件が書かれていたが、いまいちよくわからなかった- たぶんActionをEquatableで比較しなくなったため、receiveメソッドを同時に処理すると意図しない方に入るかもしれないよ、ってことかな?
-
-
ただし
IdentifiedAction
(後述)やStackAction
を含むActionについては、subscript(id:)
を用いることで具体的なenumを受け取ることができるstore.receive(\.rows[id: 0].response.success)
TaskResultの廃止
-
Action: Equatable
の必要がなくなり(上記TestStoreの改善を参照)、TaskResult
の存在意義がおおよそなくなったためsoft-deprecatedとなった- TaskResultの主な役割は、Errorを含む型でありながらEquatableに準拠できることだった。これによってActionをEquatableに準拠させやすかった
- 今後はSwiftの
Result
の使用を推奨
IdentifiedActionの導入
-
今までListで表現されるような、複数存在しているUIからのActionは以下のように表現されていた
enum Action { // ... case row(id: State.ID, action: Action) } // ... Reduce { state, action in // ... } .forEach(\.rows, action: /Action.row(id:action:)) { RowFeature() }
-
IdentifiedAction
によって以下のように書き換えられる。CaseKeyPathを利用しているのでReducerマクロを利用する必要があるenum Action { // ... case rows(IdentifiedActionOf<Nested>) } // ... Reduce { state, action in // ... } .forEach(\.rows, action: \.rows) { RowFeature() }
-
以前のように
id
とaciton
にアクセスしたい場合以下のような差分となる-case let .row(id: id, action: .buttonTapped): +case let .rows(.element(id: id, action: .buttonTapped)):
Reducerマクロのバグ(2023/11/22時点)
補完がバグる
-
ビルダーの型チェックとマクロの展開の順序の問題かもとのこと
-
Reducer内部の補完であれば書き方で解決できる
var body: some Reducer<State, Action> { - Reduce { state, action in + Reduce<State, Action> { state, action in
循環参照エラー
-
同じファイル内で適用されるマクロのコードに対して、拡張を追加することができない
@Reducer struct Feature { struct State { /* ... */ } // ... } extension Feature.State { // 🛑 循環参照エラー // ... }
-
Swiftとマクロのバグとのこと
-
別のファイルに移すか、直接記述するしかないとのこと
まとめ
- Macrosを適用して一部記法が変わった
- ちょっとバグがある
- swift 5.9~のみ利用可能
- 使わない限りは関係なく、今まで通りの書き方もできる。がsoft-deprecatedなのでいつまでも放置はできない
- ここで紹介していない変更点もあるので注意
Discussion