-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update actions #230
Update actions #230
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ import SakatsuData | |
import LogCore | ||
import UICore | ||
|
||
// MARK: Action | ||
// MARK: Actions | ||
|
||
enum SakatsuListScreenAction { | ||
case onAddButtonClick | ||
|
@@ -16,6 +16,10 @@ enum SakatsuListScreenAction { | |
case onSettingsButtonClick | ||
} | ||
|
||
enum SakatsuListScreenAsyncAction { | ||
case task | ||
} | ||
|
||
// MARK: - View | ||
|
||
package struct SakatsuListScreen: View { | ||
|
@@ -63,6 +67,9 @@ package struct SakatsuListScreen: View { | |
error: viewModel.uiState.sakatsuListError, | ||
onDismiss: { viewModel.send(.screen(.onErrorAlertDismiss)) } | ||
) | ||
.task { | ||
await viewModel.sendAsync(.screen(.task)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 今回は task のみですが、 |
||
} | ||
} | ||
|
||
@MainActor | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ import LogCore | |
|
||
// MARK: UI state | ||
|
||
struct SakatsuListUiState { | ||
struct SakatsuListUiState: Sendable { | ||
var sakatsus: [Sakatsu] = [] | ||
var selectedSakatsu: Sakatsu? | ||
var sakatsuText: String? | ||
|
@@ -31,13 +31,18 @@ struct SakatsuListUiState { | |
} | ||
} | ||
|
||
// MARK: - Action | ||
// MARK: - Actions | ||
|
||
enum SakatsuListAction { | ||
case screen(_ action: SakatsuListScreenAction) | ||
case view(_ action: SakatsuListViewAction) | ||
} | ||
|
||
enum SakatsuListAsyncAction { | ||
case screen(_ asyncAction: SakatsuListScreenAsyncAction) | ||
case view(_ asyncAction: SakatsuListViewAsyncAction) | ||
} | ||
|
||
// MARK: - Error | ||
|
||
enum SakatsuListError: LocalizedError { | ||
|
@@ -68,8 +73,6 @@ final class SakatsuListViewModel: ObservableObject { | |
self.uiState = SakatsuListUiState() | ||
self.onSettingsButtonClick = onSettingsButtonClick | ||
self.repository = repository | ||
|
||
refreshSakatsus() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. リフレッシュ処理を ViewModel の init に書くとキャンセルできないため、task へ移動しました。 |
||
} | ||
|
||
func send(_ action: SakatsuListAction) { // swiftlint:disable:this cyclomatic_complexity | ||
|
@@ -88,7 +91,9 @@ final class SakatsuListViewModel: ObservableObject { | |
|
||
case .onSakatsuSave: | ||
uiState.shouldShowInputScreen = false | ||
refreshSakatsus() | ||
Task { | ||
await refreshSakatsus() | ||
} | ||
|
||
case .onInputScreenCancelButtonClick: | ||
uiState.shouldShowInputScreen = false | ||
|
@@ -133,16 +138,45 @@ final class SakatsuListViewModel: ObservableObject { | |
} | ||
} | ||
} | ||
|
||
nonisolated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ViewModel 全体に There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. あとリフレッシュ中に画面を固まらせない場合、インジケータを表示したり、ビューを There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 非同期メソッドの実装次第ですが、 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. リフレッシュ中は特に制御しなくてもよさそうです。 |
||
func sendAsync(_ asyncAction: SakatsuListAsyncAction) async { | ||
let message = "\(#function) asyncAction: \(asyncAction)" | ||
Logger.standard.debug("\(message, privacy: .public)") | ||
|
||
switch asyncAction { | ||
case let .screen(screenAsyncAction): | ||
switch screenAsyncAction { | ||
case .task: | ||
await refreshSakatsus() | ||
} | ||
|
||
case let .view(viewAsyncAction): | ||
switch viewAsyncAction { | ||
} | ||
} | ||
} | ||
} | ||
|
||
// MARK: - Privates | ||
|
||
private extension SakatsuListViewModel { | ||
func refreshSakatsus() { | ||
nonisolated | ||
func refreshSakatsus() async { | ||
do { | ||
uiState.sakatsus = try repository.sakatsus() | ||
#if DEBUG | ||
try await Task.sleep(for: .seconds(3)) | ||
uhooi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#endif | ||
let sakatsus = try repository.sakatsus() | ||
Task { @MainActor in | ||
uiState.sakatsus = sakatsus | ||
uhooi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} catch is CancellationError { | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. キャンセルはエラー扱いにしません。 |
||
} catch { | ||
uiState.sakatsuListError = .sakatsuFetchFailed(localizedDescription: error.localizedDescription) | ||
Task { @MainActor in | ||
uiState.sakatsuListError = .sakatsuFetchFailed(localizedDescription: error.localizedDescription) | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sakatsu
やSakatsuInputError
がSendable
に準拠していないのに、SakatsuInputUiState
がSendable
に準拠できるのってどういう理由でしたっけ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
○○UiState
をSendable
に準拠させるのは、メインアクター外からゲットしたいためです。セットは
Task { @MainActor in ... }
内で行えばいいです。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SakatsuとSakatsuInputErrorすべてがSendableプロトコルに準拠しているなら、struct SakatsuInputUiStateをSendableプロトコルに準拠してもコンパイルエラーが起こらないので、SakatsuとSakatsuInputErrorもSendableプロトコルに暗黙的に準拠していると考えられます!Takeshiさんの本参考にさせてもらいました。
https://github.com/SatoTakeshiX/first-step-swift-concurrency/blob/main/try-concurrency.playground/Pages/5-1-sendable.xcplaygroundpage/Contents.swift
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public出ないなら暗黙的に準拠できると書いてありました。
なんでpublicだったら暗黙的に準拠できているかまではわからなかったので、また教えてください!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
それだー! ありがと 🙏
暗黙的に準拠しているなら
Sendable
を明示的に付けるのをやめました! c9b45fdThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public
だと暗黙的に準拠しない理由は…わからないwライブラリとして提供する場合にわかりづらいからとか、、?