[SwiftUI][TCA] refreshable
概要
この記事ではTCA初心者の筆者が理解を深めていくために、
pointfreeco
公式のサンプルアプリを基に理解しやすく整理していきます。
今回はrefreshable
の使用例を整理して理解していきます。
前回の記事はこちら
今回扱うファイル
今回は公式サンプルの以下のファイルです。
Refreshable
基本的には前回記事のBasics
と同じ機能ですが、
APIコールの契機がボタンタップから、
画面を下スワイプによる画面更新(refreshable)になっています。
またそのrefreshableに対してCancel機能もあります。
State,Action
今回のRefreshable
でのポイントは、
アクションにあるcase refresh
のActionコールの契機の部分となります。
Viewの部分で詳しく整理します。
struct RefreshableState: Equatable {
var count = 0
var fact: String?
var isLoading = false
}
enum RefreshableAction: Equatable {
case cancelButtonTapped
case decrementButtonTapped
case factResponse(Result<String, FactClient.Error>)
case incrementButtonTapped
case refresh
}
Environment
前回のBasicsの内容と全く変更はありません。
Reducer
isLoading
をtrueにしAPIコール処理をしています。
またcancellable
の処理もあります。
case .refresh:
state.fact = nil
state.isLoading = true
return environment.fact.fetch(state.count)
.delay(for: .seconds(1), scheduler: environment.mainQueue.animation())
.catchToEffect(RefreshableAction.factResponse)
.cancellable(id: CancelId())
View,Store
まずアクションrefresh
の契機として、
refreshable
の中で記載があります。
Actionを送るsendメソッドのパラメータに今回は、
while
を使用しています。
struct RefreshableView: View {
let store: Store<RefreshableState, RefreshableAction>
var body: some View {
WithViewStore(self.store) { viewStore in
List {
Text(template: readMe, .body)
HStack {
Button("-") { viewStore.send(.decrementButtonTapped) }
Text("\(viewStore.count)")
Button("+") { viewStore.send(.incrementButtonTapped) }
}
.buttonStyle(.plain)
if let fact = viewStore.fact {
Text(fact)
.bold()
}
if viewStore.isLoading {
Button("Cancel") {
viewStore.send(.cancelButtonTapped, animation: .default)
}
}
}
.refreshable {
// ポイント
await viewStore.send(.refresh, while: \.isLoading)
}
}
}
}
send(_:while:)
以下公式のドキュメントになります。
https://pointfreeco.github.io/swift-composable-architecture/send(_:while:)
今回の場合ではsend(_:while:)
メソッドを使用して、
isLoading
状態がtrueである間、中断しています。
その状態がfalseに戻ると、このメソッドは再開し、
.refreshable
に作業が終了したことを通知し、インジケータを消します。
public func send(
_ action: Action,
while predicate: @escaping (State) -> Bool
) async {
self.send(action)
await self.suspend(while: predicate)
}
次回
3章としてNavigationを使用した場合のサンプルをまとめていきます。
記事はこちら
Discussion