fetch - MemoClient
struct MemoClient {
typealias Memos = [Memo]
// fetch single Item
var fetchMemoItem: (_ id: String) -> Effect<Memo, Failure>
// fetch
var fetchMemos: () -> Effect<Memos, Failure>
struct Failure: Error, Equatable {}
}
extension MemoClient {
static let live = Self(
fetchMemoItem: { id in
Effect.task {
let (data, _) = try await URLSession.shared.data(from: URL(string: "....\(id)")!)
return try JSONDecoder().decode(Memo.self, from: data)
}
.mapError { _ in Failure() }
.eraseToEffect()
}, fetchMemos: {
Effect.task {
let (data, _) = try await URLSession.shared.data(from: URL(string: "....")!)
return try JSONDecoder().decode(Memos.self, from: data) // [Memo].self
}
.mapError { _ in Failure() }
.eraseToEffect()
}
) // MemoClient()
}
実装
struct MemoState: Equatable {
var memos: [Memo] = []
var selectedMemo: Memo? = nil
var isLoading: Bool = false
}
enum MemoAction: Equaltable {
case fetchItem(_ id: String) // action
case fetchItemRespose(Result<Memo, MemoClient.Failure>) // single respose
case fetchAll // action
case fetchAllRespose(Result<[Memo], MemoClient.FAilure>) // all respose
}
struct MemoEnviroment {
var memoClient: MemoClient
var mainQueue: AnySchedulerOf<DispatchQueue>
}
let memoReducer = Reducer<MemoState, MemoAction, MemoEnviroment> { state, action, enviroment in
switch action {
case .fetchItem(let memoId):
enum FetchItemID {}
state.isLoading = true
return enviroment.memoClient
.fetchMemoItem(memoId)
.debounce(id: FetchItemId.self, for: 0.3, scheduler: enviroment.mainQueue)
.catchToEffect(MemoAction.fetchItemResponse)
case .fetchItemRespose(.success(let memo)):
state.selectedMemo = memo
state.isLoading = false
return Effect.none
case .fetchItemRespose(.failure):
state.selectedMemo = nil
state.isLoading = false
return Effect.none
case .fetchAll:
enum FetchAllId {}
state.isLoading = true
return enviroment.memoClient
.fetchMemos
.debounce(id: FetchAllId.self, for: 0.3, scheduler: enviroment.mainQueue)
.catchToEffect(MemoAction.fetchAllResponse)
case .fetchAllRespose(.success(let memos)):
state.memos = memos
state.isLoading = false
return Effect.none
case .fetchAllRespose(.failure):
state.memos = nil
state.isLoading = false
return Effect.none
}
実装 - View
...
viewStore.send(.fetchItem("xxx")
viewStore.send(.fetchAll)
if viewStore.state.isLoading {
Color.black.opacity(0.3)
.edgesIgnoringSafeArea(.fill)
.overlay {
ProgressView().tint(.white)
.scaleEffect(1.7)
}.zIndex(1)
}
....