Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
uhooi committed Apr 1, 2024
1 parent 01fd12c commit c9b45fd
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 67 deletions.
2 changes: 1 addition & 1 deletion LokiPackage/Sources/Core/UI/Extensions/View+Alert.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SwiftUI

extension View {
/// - Important: After this alert disappears, should set `error` to `nil`.
/// - Important: After this alert disappears, should set `error` to `nil`
package func errorAlert(
error: (some LocalizedError)?,
onDismiss: @escaping () -> Void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

package protocol UserDefaultsClient: Sendable {
func object<V: Decodable>(forKey key: UserDefaultsKey) throws -> V
func object<V: Decodable>(forKey key: UserDefaultsKey) async throws -> V
func set<V: Encodable>(_ value: V, forKey key: UserDefaultsKey) throws
}

Expand All @@ -14,7 +14,7 @@ package final class DefaultUserDefaultsClient: Sendable {
}

extension DefaultUserDefaultsClient: UserDefaultsClient {
package func object<V: Decodable>(forKey key: UserDefaultsKey) throws -> V {
package func object<V: Decodable>(forKey key: UserDefaultsKey) async throws -> V {
guard let data = userDefaults.data(forKey: key.rawValue) else {
throw UserDefaultsError.missingValue(key: key)
}
Expand All @@ -32,6 +32,6 @@ extension DefaultUserDefaultsClient: UserDefaultsClient {
}
}

// The UserDefaults class is thread-safe.
// The UserDefaults class is thread-safe
// ref: https://developer.apple.com/documentation/foundation/userdefaults#2926903
extension UserDefaults: @unchecked Sendable {}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UserDefaultsCore

protocol SakatsuDataSource: Sendable {
func sakatsus() throws -> [Sakatsu]
func sakatsus() async throws -> [Sakatsu]
func saveSakatsus(_ sakatsus: [Sakatsu]) throws
}

Expand All @@ -18,9 +18,9 @@ final class SakatsuUserDefaultsDataSource {
}

extension SakatsuUserDefaultsDataSource: SakatsuDataSource {
func sakatsus() throws -> [Sakatsu] {
func sakatsus() async throws -> [Sakatsu] {
do {
return try userDefaultsClient.object(forKey: .sakatsus)
return try await userDefaultsClient.object(forKey: .sakatsus)
} catch UserDefaultsError.missingValue {
return []
} catch {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UserDefaultsCore

protocol SaunaTimeSettingsLocalDataSource: Sendable {
func defaultSaunaTimes() throws -> DefaultSaunaTimes
func defaultSaunaTimes() async throws -> DefaultSaunaTimes
func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) throws
}

Expand All @@ -18,9 +18,9 @@ final class SaunaTimeSettingsUserDefaultsDataSource {
}

extension SaunaTimeSettingsUserDefaultsDataSource: SaunaTimeSettingsLocalDataSource {
func defaultSaunaTimes() throws -> DefaultSaunaTimes {
func defaultSaunaTimes() async throws -> DefaultSaunaTimes {
do {
return try userDefaultsClient.object(forKey: .defaultSaunaTimes)
return try await userDefaultsClient.object(forKey: .defaultSaunaTimes)
} catch UserDefaultsError.missingValue {
return .init()
} catch {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package protocol SakatsuRepository: Sendable {
func sakatsus() throws -> [Sakatsu]
func sakatsus() async throws -> [Sakatsu]
func saveSakatsus(_ sakatsus: [Sakatsu]) throws
func makeDefaultSaunaSet() -> SaunaSet
func makeDefaultSaunaSet() async -> SaunaSet
}

package final class DefaultSakatsuRepository {
Expand All @@ -20,17 +20,17 @@ package final class DefaultSakatsuRepository {
}

extension DefaultSakatsuRepository: SakatsuRepository {
package func sakatsus() throws -> [Sakatsu] {
try sakatsuDataSource.sakatsus()
package func sakatsus() async throws -> [Sakatsu] {
try await sakatsuDataSource.sakatsus()
}

package func saveSakatsus(_ sakatsus: [Sakatsu]) throws {
try sakatsuDataSource.saveSakatsus(sakatsus)
}

package func makeDefaultSaunaSet() -> SaunaSet {
package func makeDefaultSaunaSet() async -> SaunaSet {
do {
let defaultSaunaTimes = try saunaTimeSettingsRepository.defaultSaunaTimes()
let defaultSaunaTimes = try await saunaTimeSettingsRepository.defaultSaunaTimes()
var defaultSaunaSet = SaunaSet()
defaultSaunaSet.sauna.time = defaultSaunaTimes.saunaTime
defaultSaunaSet.coolBath.time = defaultSaunaTimes.coolBathTime
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package protocol SaunaTimeSettingsRepository: Sendable {
func defaultSaunaTimes() throws -> DefaultSaunaTimes
func defaultSaunaTimes() async throws -> DefaultSaunaTimes
func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) throws
}

Expand All @@ -16,8 +16,8 @@ package final class DefaultSaunaTimeSettingsRepository {
}

extension DefaultSaunaTimeSettingsRepository: SaunaTimeSettingsRepository {
package func defaultSaunaTimes() throws -> DefaultSaunaTimes {
try localDataSource.defaultSaunaTimes()
package func defaultSaunaTimes() async throws -> DefaultSaunaTimes {
try await localDataSource.defaultSaunaTimes()
}

package func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum SakatsuInputScreenAction {
}

enum SakatsuInputScreenAsyncAction {
case task
}

// MARK: - View
Expand All @@ -37,6 +38,9 @@ struct SakatsuInputScreen: View {
error: viewModel.uiState.sakatsuInputError,
onDismiss: { viewModel.send(.screen(.onErrorAlertDismiss)) }
)
.task {
await viewModel.sendAsync(.screen(.task))
}
}

@MainActor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import LogCore

// MARK: UI state

struct SakatsuInputUiState: Sendable {
var sakatsu: Sakatsu
struct SakatsuInputUiState {
let editMode: SakatsuEditMode

var sakatsu: Sakatsu = .init(saunaSets: [])
var sakatsuInputError: SakatsuInputError?
}

Expand Down Expand Up @@ -70,13 +72,7 @@ final class SakatsuInputViewModel: ObservableObject {
sakatsuRepository: some SakatsuRepository = DefaultSakatsuRepository.shared,
validator: some SakatsuValidator = DefaultSakatsuValidator()
) {
switch sakatsuEditMode {
case .new:
let defaultSaunaSet = sakatsuRepository.makeDefaultSaunaSet()
self.uiState = SakatsuInputUiState(sakatsu: .init(saunaSets: [defaultSaunaSet]))
case let .edit(sakatsu: sakatsu):
self.uiState = SakatsuInputUiState(sakatsu: sakatsu)
}
self.uiState = .init(editMode: sakatsuEditMode)
self.onSakatsuSave = onSakatsuSave
self.onCancelButtonClick = onCancelButtonClick
self.sakatsuRepository = sakatsuRepository
Expand All @@ -91,18 +87,20 @@ final class SakatsuInputViewModel: ObservableObject {
case let .screen(screenAction):
switch screenAction {
case .onSaveButtonClick:
do {
var sakatsus = (try? sakatsuRepository.sakatsus()) ?? []
if let index = sakatsus.firstIndex(of: uiState.sakatsu) {
sakatsus[index] = uiState.sakatsu
} else {
sakatsus.append(uiState.sakatsu)
Task {
do {
var sakatsus = (try? await sakatsuRepository.sakatsus()) ?? []
if let index = sakatsus.firstIndex(of: uiState.sakatsu) {
sakatsus[index] = uiState.sakatsu
} else {
sakatsus.append(uiState.sakatsu)
}
try sakatsuRepository.saveSakatsus(sakatsus.sorted(by: { $0.visitingDate > $1.visitingDate }))
} catch {
uiState.sakatsuInputError = .sakatsuSaveFailed
}
try sakatsuRepository.saveSakatsus(sakatsus.sorted(by: { $0.visitingDate > $1.visitingDate }))
} catch {
uiState.sakatsuInputError = .sakatsuSaveFailed
onSakatsuSave()
}
onSakatsuSave()

case .onErrorAlertDismiss:
uiState.sakatsuInputError = nil
Expand All @@ -115,7 +113,9 @@ final class SakatsuInputViewModel: ObservableObject {
switch viewAction {

case .onAddNewSaunaSetButtonClick:
uiState.sakatsu.saunaSets.append(sakatsuRepository.makeDefaultSaunaSet())
Task {
uiState.sakatsu.saunaSets.append(await sakatsuRepository.makeDefaultSaunaSet())
}

case let .onFacilityNameChange(facilityName):
guard validator.validate(facilityName: facilityName) else {
Expand Down Expand Up @@ -212,14 +212,21 @@ final class SakatsuInputViewModel: ObservableObject {
}
}

nonisolated
func sendAsync(_ asyncAction: SakatsuInputAsyncAction) async {
let message = "\(#function) asyncAction: \(asyncAction)"
Logger.standard.debug("\(message, privacy: .public)")

switch asyncAction {
case let .screen(screenAsyncAction):
switch screenAsyncAction {
case .task:
switch uiState.editMode {
case .new:
let defaultSaunaSet = await sakatsuRepository.makeDefaultSaunaSet()
uiState.sakatsu = Sakatsu(saunaSets: [defaultSaunaSet])
case let .edit(sakatsu: sakatsu):
uiState.sakatsu = sakatsu
}
}

case let .view(viewAsyncAction):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import LogCore

// MARK: UI state

struct SakatsuListUiState: Sendable {
struct SakatsuListUiState {
var sakatsus: [Sakatsu] = []
var selectedSakatsu: Sakatsu?
var sakatsuText: String?
Expand Down Expand Up @@ -139,7 +139,6 @@ final class SakatsuListViewModel: ObservableObject {
}
}

nonisolated
func sendAsync(_ asyncAction: SakatsuListAsyncAction) async {
let message = "\(#function) asyncAction: \(asyncAction)"
Logger.standard.debug("\(message, privacy: .public)")
Expand All @@ -161,22 +160,14 @@ final class SakatsuListViewModel: ObservableObject {
// MARK: - Privates

private extension SakatsuListViewModel {
nonisolated
func refreshSakatsus() async {
do {
#if DEBUG
try await Task.sleep(for: .seconds(3))
#endif
let sakatsus = try repository.sakatsus()
Task { @MainActor in
uiState.sakatsus = sakatsus
}
let sakatsus = try await repository.sakatsus()
uiState.sakatsus = sakatsus
} catch is CancellationError {
return
// Do nothing when cancelled
} catch {
Task { @MainActor in
uiState.sakatsuListError = .sakatsuFetchFailed(localizedDescription: error.localizedDescription)
}
uiState.sakatsuListError = .sakatsuFetchFailed(localizedDescription: error.localizedDescription)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import LogCore

// MARK: UI state

struct SettingsUiState: Sendable {
struct SettingsUiState {
var defaultSaunaTimes: DefaultSaunaTimes = .init()
var settingsError: SettingsError?
}
Expand Down Expand Up @@ -96,7 +96,6 @@ final class SettingsViewModel: ObservableObject {
}
}

nonisolated
func sendAsync(_ asyncAction: SettingsAsyncAction) async {
let message = "\(#function) asyncAction: \(asyncAction)"
Logger.standard.debug("\(message, privacy: .public)")
Expand All @@ -118,22 +117,14 @@ final class SettingsViewModel: ObservableObject {
// MARK: - Privates

private extension SettingsViewModel {
nonisolated
func refreshDefaultSaunaTimes() async {
do {
#if DEBUG
try await Task.sleep(for: .seconds(3))
#endif
let defaultSaunaTimes = try repository.defaultSaunaTimes()
Task { @MainActor in
uiState.defaultSaunaTimes = defaultSaunaTimes
}
let defaultSaunaTimes = try await repository.defaultSaunaTimes()
uiState.defaultSaunaTimes = defaultSaunaTimes
} catch is CancellationError {
return
} catch {
Task { @MainActor in
uiState.settingsError = .defaultSaunaSetFetchFailed(localizedDescription: error.localizedDescription)
}
uiState.settingsError = .defaultSaunaSetFetchFailed(localizedDescription: error.localizedDescription)
}
}

Expand Down

0 comments on commit c9b45fd

Please sign in to comment.