Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SwiftUIでの非同期処理データの状態管理を考える

kntk
September 02, 2023

 SwiftUIでの非同期処理データの状態管理を考える

iOSDC2023 Day1 TrackC 17:25〜

kntk

September 02, 2023
Tweet

More Decks by kntk

Other Decks in Programming

Transcript

  1. ࿩͢͜ͱ w ඇಉظॲཧσʔλͷঢ়ଶ؅ཧํ๏Λ༷ʑͳ࢓༷Λݩʹ࣮૷ɾߟ࡯ w ख๏ w FOVNΛ༻͍ͨख๏ w ίϯϙʔωϯτΛ༻͍ͨख๏ w

    ओޠ͕େ͖͍ςʔϚͳͷͰɺߟ͑ํͷҰͭͱͯ͠ଊ͑ͭͭ 
 վળ఺΍৽ͨͳࢹ఺͕͋Ε͹࣭໰ɾίϝϯτ௖͚ͨΒخ͍͠Ͱ͢👍
  2. $BTFFOVNͷಋೖ#FGPSF 7JFXͷग़͠Θ͚ var body: some View { List { if

    isLoading { EmptyView() } else { if let data { content(user: data) } else if error != nil { ErrorStateView() } } } .overlay { if isLoading { ProgressView() } } }
  3. $BTFFOVNͷಋೖ#FGPSF var body: some View { List { if isLoading

    { EmptyView() } else { if let data { content(user: data) } else if error != nil { ErrorStateView() } } } .overlay { if isLoading { ProgressView() } } } ϩʔυத 7JFXͷग़͠Θ͚
  4. $BTFFOVNͷಋೖ#FGPSF var body: some View { List { if isLoading

    { EmptyView() } else { if let data { content(user: data) } else if error != nil { ErrorStateView() } } } .overlay { if isLoading { ProgressView() } } } ϩʔυத ੒ޭ 7JFXͷग़͠Θ͚
  5. $BTFFOVNͷಋೖ#FGPSF var body: some View { List { if isLoading

    { EmptyView() } else { if let data { content(user: data) } else if error != nil { ErrorStateView() } } } .overlay { if isLoading { ProgressView() } } } ϩʔυத ੒ޭ ࣦഊ 7JFXͷग़͠Θ͚
  6. $BTFFOVNͷಋೖ#FGPSF ঢ়ଶͷૢ࡞ʢඇಉظॲཧʣ func fetchUser() async { if isLoading { return

    } isLoading = true data = nil error = nil do { data = try await API.getUser() } catch { self.error = error } isLoading = false }
  7. $BTFFOVNͷಋೖ#FGPSF ঢ়ଶͷૢ࡞ʢඇಉظॲཧʣ func fetchUser() async { if isLoading { return

    } isLoading = true data = nil error = nil do { data = try await API.getUser() } catch { self.error = error } isLoading = false } ϩʔυ։࢝
  8. $BTFFOVNͷಋೖ#FGPSF ঢ়ଶͷૢ࡞ʢඇಉظॲཧʣ func fetchUser() async { if isLoading { return

    } isLoading = true data = nil error = nil do { data = try await API.getUser() } catch { self.error = error } isLoading = false } "1*Λୟ͘ ϩʔυ։࢝
  9. $BTFFOVNͷಋೖ#FGPSF ঢ়ଶͷૢ࡞ʢඇಉظॲཧʣ func fetchUser() async { if isLoading { return

    } isLoading = true data = nil error = nil do { data = try await API.getUser() } catch { self.error = error } isLoading = false } "1*Λୟ͘ ࣦഊͳΒFSSPS ϩʔυ։࢝
  10. $BTFFOVNͷಋೖ#FGPSF ঢ়ଶͷૢ࡞ʢඇಉظॲཧʣ func fetchUser() async { if isLoading { return

    } isLoading = true data = nil error = nil do { data = try await API.getUser() } catch { self.error = error } isLoading = false } "1*Λୟ͘ ࣦഊͳΒFSSPS ϩʔυऴྃ ϩʔυ։࢝
  11. $BTFFOVNͷಋೖ#FGPSF ໰୊఺JGͷωετ var body: some View { List { if

    isLoading { EmptyView() } else { if let data { content(user: data) } else if error != nil { ErrorStateView() } } } .overlay { if isLoading { ProgressView() } } } ωετ͕ਂ͍
  12. $BTFFOVNͷಋೖ#FGPSF ໰୊఺ม਺Λผʑʹૢ࡞͢Δͷ͕൥ࡶ func fetchUser() async { if isLoading { return

    } isLoading = true data = nil error = nil do { data = try await API.getUser() } catch { self.error = error } isLoading = false } લճͷঢ়ଶΛ࡟আ
  13. $BTFFOVNͷಋೖ#FGPSF ໰୊఺ม਺Λผʑʹૢ࡞͢Δͷ͕൥ࡶ func fetchUser() async { if isLoading { return

    } isLoading = true data = nil error = nil do { data = try await API.getUser() } catch { self.error = error } isLoading = false } ݁Ռͷ୅ೖͱ ϩʔυऴ͕ྃಠཱ લճͷঢ়ଶΛ࡟আ
  14. $BTFFOVNͷಋೖ"GUFS ঢ়ଶͷએݴ enum DataState<V, E: Error> { case idle case

    loading case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  15. $BTFFOVNͷಋೖ"GUFS ঢ়ଶͷએݴ enum DataState<V, E: Error> { case idle case

    loading case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle ม਺͕ͭ
  16. $BTFFOVNͷಋೖ"GUFS 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .loading: EmptyView() case .success(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isLoading { ProgressView() } } }
  17. $BTFFOVNͷಋೖ"GUFS 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .loading: EmptyView() case .success(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isLoading { ProgressView() } } } ωετ͕ͭ
  18. $BTFFOVNͷಋೖ"GUFS 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .loading: EmptyView() case .success(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isLoading { ProgressView() } } } extension DataState { var isLoading: Bool { if case .loading = self { return true } return false } }
  19. $BTFFOVNͷಋೖ"GUFS ঢ়ଶͷૢ࡞ʢඇಉظॲཧʣ func fetchUser() async { if dataState.isLoading { return

    } dataState = .loading do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } }
  20. $BTFFOVNͷಋೖ"GUFS ঢ়ଶͷૢ࡞ʢඇಉظॲཧʣ func fetchUser() async { if dataState.isLoading { return

    } dataState = .loading do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } } લճͷঢ়ଶ΋࡟আ JT-PBEJOHGBMTF ΋ෆཁ
  21. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷ enum DataState<V, E: Error> { case idle case

    loading case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  22. var body: some View { List { switch dataState {

    case .idle, .loading: EmptyView() case .success(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isLoading { ProgressView() } } } $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷
  23. var body: some View { List { switch dataState {

    case .idle, .loading: EmptyView() case .success(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isLoading { ProgressView() } } } $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷ ɾϩʔυதͩͱσʔλ͕ফ͑Δ ɾϓϧϦϑͱೋॏʹϩʔυ͕දࣔ
  24. var body: some View { List { switch dataState {

    case .idle, .loading: EmptyView() case .success(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isLoading { ProgressView() } } } $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷ MPBEJOHʹ͸ σʔλ͕ͳ͍
  25. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS SF-PBEJOH 7 Λ௥Ճ enum DataState<V, E: Error> { case

    idle case loading case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  26. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS SF-PBEJOH 7 Λ௥Ճ enum DataState<V, E: Error> { case

    idle case loading case reLoading(V) case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  27. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS SF-PBEJOH 7 Λ௥Ճ enum DataState<V, E: Error> { case

    idle case initialLoading case reLoading(V) case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  28. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .loading: EmptyView() case .success(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } }
  29. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } } ࠶ಡΈࠐΈத΋ σʔλΛදࣔ
  30. func fetchUser() async { if dataState.isLoading { return } dataState

    = .loading do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } } $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS ঢ়ଶૢ࡞
  31. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS ঢ়ଶૢ࡞ func fetchUser() async { switch dataState { case

    .idle, .failure: dataState = .initialLoading case .success(let value): dataState = .reLoading(value) case .initialLoading, .reLoading: return } do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } }
  32. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS ঢ়ଶૢ࡞ func fetchUser() async { switch dataState { case

    .idle, .failure: dataState = .initialLoading case .success(let value): dataState = .reLoading(value) case .initialLoading, .reLoading: return } do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } } TVDDFTT͔Βϩʔυ͢Δ࣌͸ SF-PBEJOH
  33. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS ঢ়ଶૢ࡞ func fetchUser() async { if dataState.isLoading { return

    } dataState.startLoading() do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } } extension DataState { mutating func startLoading() { switch self { case .idle, .failure: self = .initialLoading case .success(let value): self = .reLoading(value) default: return } } }
  34. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS ঢ়ଶૢ࡞ func fetchUser() async { if dataState.isLoading { return

    } dataState.startLoading() do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } }
  35. $BTF࠶ಡΈࠐΈத΋σʔλΛදࣔ"GUFS ࣮૷׬ྃ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } }
  36. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷ enum DataState<V, E: Error> { case idle case

    initialLoading case reLoading(V) case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  37. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } }
  38. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } } ɾϩʔυதͩͱΤϥʔ͕ফ͑Δ ɾϓϧϦϑͱೋॏʹϩʔυ͕දࣔ
  39. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ#FGPSF $BTFͷFOVNͰ࣮૷ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } } JOJUJBM-PBEJOHʹ͸Τϥʔ͕ͳ͍
  40. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ"GUFS SFUSZ-PBEJOH & Λ௥Ճ enum DataState<V, E: Error> { case

    idle case initialLoading case reLoading(V) case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  41. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ"GUFS SFUSZ-PBEJOH & Λ௥Ճ enum DataState<V, E: Error> { case

    idle case initialLoading case reLoading(V) case retryLoading(E) case success(V) case failure(E) } @State private var dataState: DataState<User, any Error> = .idle
  42. var body: some View { List { switch dataState {

    case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } } $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ"GUFS 7JFXͷग़͠Θ͚
  43. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ"GUFS 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure, .retryLoading: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } } ࠶ಡΈࠐΈத΋ ΤϥʔΛදࣔ
  44. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ"GUFS ঢ়ଶૢ࡞ func fetchUser() async { if dataState.isLoading { return

    } dataState.startLoading() do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } }
  45. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ"GUFS ঢ়ଶૢ࡞ func fetchUser() async { if dataState.isLoading { return

    } dataState.startLoading() do { dataState = .success(try await API.getUser()) } catch { dataState = .failure(error) } } extension DataState { mutating func startLoading() { switch self { case .idle: self = .initialLoading case .success(let value): self = .reLoading(value) case .failure(let error): self = .retryLoading(error) default: return } } } GBJMVSF͔Βϩʔυ͢Δ࣌͸ SFUSZ-PBEJOH
  46. $BTF࠶ಡΈࠐΈதͰ΋ΤϥʔΛදࣔ"GUFS ࣮૷׬ྃ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): content(user: value) case .failure, .retryLoading: ErrorStateView() } } .overlay { if dataState.isInitialLoading { ProgressView() } } }
  47. ঢ়ଶ͕૿͑ͯෳࡶʹͳͬͨʁ enum DataState<V, E: Error> { case idle case initialLoading

    case reLoading(V) case retryLoading(E) case success(V) case failure(E) }
  48. $BTFϖʔδϯά QBHJOH 7 QBHJOH'BJMVSF 7 & Λ௥Ճ enum DataState<V, E:

    Error> { case idle case initialLoading case reLoading(V) case retryLoading(E) case success(V) case failure(E) } @State private var dataState: DataState<[Post], any Error> = .idle
  49. $BTFϖʔδϯά QBHJOH 7 QBHJOH'BJMVSF 7 & Λ௥Ճ enum DataState<V, E:

    Error> { case idle case initialLoading case reLoading(V) case retryLoading(E) case success(V) case failure(E) case paging(V) case pagingFailure(V, E) } @State private var dataState: DataState<[Post], any Error> = .idle
  50. $BTFϖʔδϯά QBHJOH 7 QBHJOH'BJMVSF 7 & Λ௥Ճ enum PagingDataState<V, E:

    Error> { case idle case initialLoading case reLoading(V) case retryLoading(E) case success(V) case loadingFailure(E) case paging(V) case pagingFailure(V, E) } @State private var dataState: PagingDataState<[Post], any Error> = .idle
  51. $BTFϖʔδϯά 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): listContent(posts: value) case .failure, .retryLoading: ErrorStateView() } .overlay { if dataState.isInitialLoading { ProgressView() } } }
  52. $BTFϖʔδϯά 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): listContent(posts: value) case .failure, .retryLoading: ErrorStateView() } .overlay { if dataState.isInitialLoading { ProgressView() } } } func listContent(posts: [Post]) -> some View { ForEach(posts) { post in Text(post.title) } }
  53. $BTFϖʔδϯά 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value): listContent(posts: value) case .failure, .retryLoading: ErrorStateView() } .overlay { if dataState.isInitialLoading { ProgressView() } } }
  54. $BTFϖʔδϯά 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value), .paging(let value), .pagingFailure(let value, _): listContent(posts: value) case .loadingFailure, .retryLoading: ErrorStateView() } .overlay { if dataState.isInitialLoading { ProgressView() } } } ϖʔδϯάதɾϖʔδϯάࣦഊ Ͱ΋લϖʔδͷσʔλΛදࣔ
  55. $BTFϖʔδϯά 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value), .paging(let value), .pagingFailure(let value, _): listContent(posts: value) case .loadingFailure, .retryLoading: ErrorStateView() } .overlay { if dataState.isInitialLoading { ProgressView() } } }
  56. $BTFϖʔδϯά 7JFXͷग़͠Θ͚ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value), .paging(let value), .pagingFailure(let value, _): listContent(posts: value) if dataState.isPagingFailure { ErrorStateView(…) } else { ProgressView() .onAppear { Task { await fetchMore() } } } case .loadingFailure, .retryLoading: ErrorStateView() -JTUԼ෦ʹ Τϥʔදࣔ ϖʔδϯάݕ஌
  57. $BTFϖʔδϯά ঢ়ଶૢ࡞ʢॳճऔಘɾϦϑϨογϡʣ func fetchInitial() async { if dataState.isLoading || dataState.isPaging

    { return } dataState.startLoading() do { dataState = .success(try await API.getPosts(minId: 0, count: 30)) } catch { dataState = .loadingFailure(error) } }
  58. $BTFϖʔδϯά ঢ়ଶૢ࡞ʢॳճऔಘɾϦϑϨογϡʣ func fetchInitial() async { if dataState.isLoading || dataState.isPaging

    { return } dataState.startLoading() do { dataState = .success(try await API.getPosts(minId: 0, count: 30)) } catch { dataState = .loadingFailure(error) } } extension PagingDataState { mutating func startLoading() { switch self { case .idle: self = .initialLoading case .success(let value), .pagingFailure(let value, _): self = .reLoading(value) case .loadingFailure(let error): self = .retryLoading(error) default: return } } } QBHJOH'BJMVSF͔Βϩʔυ͢Δ࣌͸ SF-PBEJOHʹ
  59. $BTFϖʔδϯά ঢ়ଶૢ࡞ʢϖʔδϯάʣ func fetchMore() async { if dataState.isLoading || dataState.isPaging

    { return } guard let users = dataState.value, let lastId = users.last?.id else { return } dataState.startPaging() do { let newUsers = try await API.getPosts(minId: lastId + 1, count: 30) dataState = .success(users + newUsers) } catch { dataState = .pagingFailure(users, error) } }
  60. $BTFϖʔδϯά ঢ়ଶૢ࡞ʢϖʔδϯάʣ func fetchMore() async { if dataState.isLoading || dataState.isPaging

    { return } guard let users = dataState.value, let lastId = users.last?.id else { return } dataState.startPaging() do { let newUsers = try await API.getPosts(minId: lastId + 1, count: 30) dataState = .success(users + newUsers) } catch { dataState = .pagingFailure(users, error) } } extension PagingDataState { mutating func startPaging() { switch self { case .success(let value), .pagingFailure(let value, _): self = .paging(value) default: return } } }
  61. $BTFϖʔδϯά ঢ়ଶૢ࡞ʢϖʔδϯάʣ func fetchMore() async { if dataState.isLoading || dataState.isPaging

    { return } guard let users = dataState.value, let lastId = users.last?.id else { return } dataState.startPaging() do { let newUsers = try await API.getPosts(minId: lastId + 1, count: 30) dataState = .success(users + newUsers) } catch { dataState = .pagingFailure(users, error) } } MPBEJOH'BJMVSFͰ͸ͳ͘ QBHJOH'BJMVSF
  62. $BTFϖʔδϯά ࣮૷׬ྃ var body: some View { List { switch

    dataState { case .idle, .initialLoading: EmptyView() case .success(let value), .reLoading(let value), .paging(let value), .pagingFailure(let value, _): listContent(posts: value) if dataState.isPagingFailure { ErrorStateView(…) } else { ProgressView() .onAppear { Task { await fetchMore() } } } case .loadingFailure, .retryLoading: ErrorStateView()
  63. var body: some View { PagingList( success: listContent, fetchInitial: {

    try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) } ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ
  64. var body: some View { PagingList( success: listContent, fetchInitial: {

    try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) } ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ ඇಉظॲཧΛ౉͢ ϨΠΞ΢τΛ౉͢
  65. FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ 4XJUDI7JFX4UZMF w ֤ը໘ͰTXJUDIͷϘΠϥʔϓϨʔτΛॻ͘ඞཁੑ w ࣮ࡍͷ։ൃͰ͸ʢଟ͘ͷը໘Ͱʣ࢓༷͕౷Ұ͞Ε͍ͯΔέʔε͕ଟ͍ w ঢ়ଶૢ࡞ͷϛε͕ͳ͍Θ͚Ͱ͸ͳ͍ @State

    private var dataState: PagingDataState<[Post], any Error> = .idle var body: some View { PagingList(dataState: dataState, listContent: listContent) { Task { await fetchMore() } } .refreshable { await fetchInitial() } .task { await fetchInitial() } .onChange(of: dataState.isFailure) { bool in guard bool, let error = dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } }
  66. FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ 4XJUDI7JFX4UZMF w ֤ը໘ͰTXJUDIͷϘΠϥʔϓϨʔτΛॻ͘ඞཁੑ w ࣮ࡍͷ։ൃͰ͸ʢଟ͘ͷը໘Ͱʣ࢓༷͕౷Ұ͞Ε͍ͯΔέʔε͕ଟ͍ w ঢ়ଶૢ࡞ͷϛε͕ͳ͍Θ͚Ͱ͸ͳ͍ @State

    private var dataState: PagingDataState<[Post], any Error> = .idle var body: some View { PagingList(dataState: dataState, listContent: listContent) { Task { await fetchMore() } } .refreshable { await fetchInitial() } .task { await fetchInitial() } .onChange(of: dataState.isFailure) { bool in guard bool, let error = dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } ϖʔδϯά ϓϧϦϑ ॳճऔಘ ΤϥʔΛόφʔͰදࣔ
  67. 'FUDI7JFX4UZMF w ֤ը໘ͰTXJUDIͷϘΠϥʔϓϨʔτΛॻ͘ඞཁੑ w ࣮ࡍͷ։ൃͰ͸ʢଟ͘ͷը໘Ͱʣ࢓༷͕౷Ұ͞Ε͍ͯΔέʔε͕ଟ͍ w ঢ়ଶૢ࡞ͷϛε͕ͳ͍Θ͚Ͱ͸ͳ͍ @StateObject private var

    viewStore: LoadingContentViewStore<Post> = .init( fetchInitial: { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) var body: some View { PagingList(viewStore: viewStore, success: listContent) .task { await viewStore.loadInitial() } .onChange(of: viewStore.dataState.isFailure) { bool in guard bool, let error = viewStore.dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ
  68. 'FUDI7JFX4UZMF w ֤ը໘ͰTXJUDIͷϘΠϥʔϓϨʔτΛॻ͘ඞཁੑ w ࣮ࡍͷ։ൃͰ͸ʢଟ͘ͷը໘Ͱʣ࢓༷͕౷Ұ͞Ε͍ͯΔέʔε͕ଟ͍ w ঢ়ଶૢ࡞ͷϛε͕ͳ͍Θ͚Ͱ͸ͳ͍ @StateObject private var

    viewStore: LoadingContentViewStore<Post> = .init( fetchInitial: { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) var body: some View { PagingList(viewStore: viewStore, success: listContent) .task { await viewStore.loadInitial() } .onChange(of: viewStore.dataState.isFailure) { bool in guard bool, let error = viewStore.dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ ঢ়ଶ؅ཧΛ͢Δ4UBUF0CKFDUΛ౉͢
  69. 'FUDI7JFX4UZMF w ֤ը໘ͰTXJUDIͷϘΠϥʔϓϨʔτΛॻ͘ඞཁੑ w ࣮ࡍͷ։ൃͰ͸ʢଟ͘ͷը໘Ͱʣ࢓༷͕౷Ұ͞Ε͍ͯΔέʔε͕ଟ͍ w ঢ়ଶૢ࡞ͷϛε͕ͳ͍Θ͚Ͱ͸ͳ͍ @StateObject private var

    viewStore: LoadingContentViewStore<Post> = .init( fetchInitial: { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) var body: some View { PagingList(viewStore: viewStore, success: listContent) .task { await viewStore.loadInitial() } .onChange(of: viewStore.dataState.isFailure) { bool in guard bool, let error = viewStore.dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ ར༻ऀଆ͔Β΋ൃՐՄೳ
  70. 'FUDI7JFX4UZMF w ֤ը໘ͰTXJUDIͷϘΠϥʔϓϨʔτΛॻ͘ඞཁੑ w ࣮ࡍͷ։ൃͰ͸ʢଟ͘ͷը໘Ͱʣ࢓༷͕౷Ұ͞Ε͍ͯΔέʔε͕ଟ͍ w ঢ়ଶૢ࡞ͷϛε͕ͳ͍Θ͚Ͱ͸ͳ͍ @StateObject private var

    viewStore: LoadingContentViewStore<Post> = .init( fetchInitial: { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) var body: some View { PagingList(viewStore: viewStore, success: listContent) .task { await viewStore.loadInitial() } .onChange(of: viewStore.dataState.isFailure) { bool in guard bool, let error = viewStore.dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ ॳճऔಘ΍Τϥʔදࣔ͸ը໘͝ͱ ར༻ऀଆ͔Β΋ൃՐՄೳ
  71. #JOEBCMF'FUDI7JFX4UZMF @State private var dataState: PagingDataState<[Post], any Error> = .idle

    var body: some View { PagingList( dataState: $dataState, success: listContent, fetchInitial: { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) .onChange(of: dataState.isFailure) { isFailure in guard isFailure, let error = dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ
  72. #JOEBCMF'FUDI7JFX4UZMF @State private var dataState: PagingDataState<[Post], any Error> = .idle

    var body: some View { PagingList( dataState: $dataState, success: listContent, fetchInitial: { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) .onChange(of: dataState.isFailure) { isFailure in guard isFailure, let error = dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ #JOEJOHͰঢ়ଶ͚ͩ౉͢
  73. #JOEBCMF'FUDI7JFX4UZMF @State private var dataState: PagingDataState<[Post], any Error> = .idle

    var body: some View { PagingList( dataState: $dataState, success: listContent, fetchInitial: { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) .onChange(of: dataState.isFailure) { isFailure in guard isFailure, let error = dataState.error else { return } MessageBanner.showError("Τϥʔ͕ൃੜ͠·ͨ͠", with: error.localizedDescription) } } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ Τϥʔදࣔ͸ը໘͝ͱ
  74. 4JNQMF'FUDI7JFX4UZMF var body: some View { PagingList( success: listContent, fetchInitial:

    { try await API.getPosts(minId: 0, count: 30) }, fetchMore: { try await API.getPosts(minId: $0.id + 1, count: 30) } ) } FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ
  75. 4XJUDI 'FUDI #JOEBCMF'FUDI 4JNQMF'FUDI ঢ়ଶͷૢ࡞ 
 ʢඇಉظॲཧʣ ར༻ऀ👨💻 ಺෦🤖 ར༻ऀ΋Մೳ👨💻

    ಺෦🤖 ಺෦🤖 ঢ়ଶͷऔಘ Մೳ⭕ Մೳ⭕ Մೳ⭕ ෆՄೳ❌ FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ
  76. FOVN ίϯϙʔωϯτʹΑΔঢ়ଶ؅ཧ ؆ܿ͞ ΧελϚΠζੑ 4XJUDI 'FUDI #JOEBCMF'FUDI 4JNQMF'FUDI ঢ়ଶͷૢ࡞ 


    ʢඇಉظॲཧʣ ར༻ऀ👨💻 ಺෦🤖 ར༻ऀ΋Մೳ👨💻 ಺෦🤖 ಺෦🤖 ঢ়ଶͷऔಘ Մೳ⭕ Մೳ⭕ Մೳ⭕ ෆՄೳ❌
  77. ࢀߟ w ϞόΠϧΞϓϦʹ͓͚ΔಡΈࠐΈॲཧपΓͷ6*ύλʔϯͱ4XJGU6*ʹΑΔ࣮૷ྫ w IUUQT[FOOEFWSPDLOBNFBSUJDMFTBFF f  w "TZOD w

    IUUQTHJUIVCDPNCBOO[BJ"TZOD w #VJMEJOHSFVTBCMFDPOUFOUMPBEJOHWJFXJO4XJGU6* w IUUQTENZUSPBOPLIJONFEJVNDPNCVJMEJOHSFVTBCMFDPOUFOUMPBEJOHWJFXXJUITXJGUVJBOEDPNCJOF GGFFC w 4XJGU6*Ͱʮάϧʔϓελϯϓʯͱ͍͏νϟοτػೳΛ೔ؒͰ࡞ͬͨ࿩ w IUUQTNFEJVNDPNLBVDIFTXJGUVJHSPVQDIBUDEF