Skip to content

Commit

Permalink
Merge pull request #230 from uhooi/feature/update_actions
Browse files Browse the repository at this point in the history
Update actions
  • Loading branch information
uhooi committed Apr 1, 2024
2 parents 69f6f13 + 055792e commit 98d8e38
Show file tree
Hide file tree
Showing 15 changed files with 194 additions and 83 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
10 changes: 5 additions & 5 deletions LokiPackage/Sources/Core/UserDefaults/UserDefaultsClient.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Foundation

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

package final class DefaultUserDefaultsClient: Sendable {
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 @@ -24,14 +24,14 @@ extension DefaultUserDefaultsClient: UserDefaultsClient {
return try jsonDecoder.decode(V.self, from: data)
}

package func set<V: Encodable>(_ value: V, forKey key: UserDefaultsKey) throws {
package func set<V: Encodable>(_ value: V, forKey key: UserDefaultsKey) async throws {
let jsonEncoder = JSONEncoder()
jsonEncoder.keyEncodingStrategy = .convertToSnakeCase
let data = try jsonEncoder.encode(value)
userDefaults.set(data, forKey: key.rawValue)
}
}

// 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,8 +1,8 @@
import UserDefaultsCore

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

final class SakatsuUserDefaultsDataSource {
Expand All @@ -18,17 +18,17 @@ 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 {
throw error
}
}

func saveSakatsus(_ sakatsus: [Sakatsu]) throws {
try userDefaultsClient.set(sakatsus, forKey: .sakatsus)
func saveSakatsus(_ sakatsus: [Sakatsu]) async throws {
try await userDefaultsClient.set(sakatsus, forKey: .sakatsus)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import UserDefaultsCore

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

final class SaunaTimeSettingsUserDefaultsDataSource {
Expand All @@ -18,17 +18,17 @@ 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 {
throw error
}
}

func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) throws {
try userDefaultsClient.set(defaultSaunaTimes, forKey: .defaultSaunaTimes)
func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) async throws {
try await userDefaultsClient.set(defaultSaunaTimes, forKey: .defaultSaunaTimes)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package protocol SakatsuRepository: Sendable {
func sakatsus() throws -> [Sakatsu]
func saveSakatsus(_ sakatsus: [Sakatsu]) throws
func makeDefaultSaunaSet() -> SaunaSet
func sakatsus() async throws -> [Sakatsu]
func saveSakatsus(_ sakatsus: [Sakatsu]) async throws
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 saveSakatsus(_ sakatsus: [Sakatsu]) async throws {
try await 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,6 +1,6 @@
package protocol SaunaTimeSettingsRepository: Sendable {
func defaultSaunaTimes() throws -> DefaultSaunaTimes
func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) throws
func defaultSaunaTimes() async throws -> DefaultSaunaTimes
func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) async throws
}

package final class DefaultSaunaTimeSettingsRepository {
Expand All @@ -16,11 +16,11 @@ 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 {
try localDataSource.saveDefaultSaunaTimes(defaultSaunaTimes)
package func saveDefaultSaunaTimes(_ defaultSaunaTimes: DefaultSaunaTimes) async throws {
try await localDataSource.saveDefaultSaunaTimes(defaultSaunaTimes)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import SwiftUI
import LogCore
import UICore

// MARK: Action
// MARK: Actions

enum SakatsuInputScreenAction {
case onSaveButtonClick
case onErrorAlertDismiss
case onCancelButtonClick
}

enum SakatsuInputScreenAsyncAction {
case task
}

// MARK: - View

struct SakatsuInputScreen: View {
Expand All @@ -34,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 @@ -2,7 +2,7 @@ import SwiftUI
import Algorithms
import SakatsuData

// MARK: Action
// MARK: Actions

enum SakatsuInputViewAction {
case onAddNewSaunaSetButtonClick
Expand All @@ -23,6 +23,9 @@ enum SakatsuInputViewAction {
case onAddNewTemperatureButtonClick
}

enum SakatsuInputViewAsyncAction {
}

// MARK: - View

struct SakatsuInputView: View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import LogCore
// MARK: UI state

struct SakatsuInputUiState {
var sakatsu: Sakatsu
let editMode: SakatsuEditMode

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

Expand All @@ -14,13 +16,18 @@ enum SakatsuEditMode {
case edit(sakatsu: Sakatsu)
}

// MARK: - Action
// MARK: - Actions

enum SakatsuInputAction {
case screen(_ action: SakatsuInputScreenAction)
case view(_ action: SakatsuInputViewAction)
}

enum SakatsuInputAsyncAction {
case screen(_ asyncAction: SakatsuInputScreenAsyncAction)
case view(_ asyncAction: SakatsuInputViewAsyncAction)
}

// MARK: - Error

enum SakatsuInputError: LocalizedError {
Expand Down Expand Up @@ -65,40 +72,35 @@ 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
self.validator = validator
}

// swiftlint:disable:next cyclomatic_complexity function_body_length
func send(_ action: SakatsuInputAction) {
func send(_ action: SakatsuInputAction) { // swiftlint:disable:this cyclomatic_complexity function_body_length
let message = "\(#function) action: \(action)"
Logger.standard.debug("\(message, privacy: .public)")

switch action {
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 await 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 @@ -111,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 @@ -207,4 +211,27 @@ final class SakatsuInputViewModel: ObservableObject {
}
}
}

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):
switch viewAsyncAction {
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import SakatsuData
import LogCore
import UICore

// MARK: Action
// MARK: Actions

enum SakatsuListScreenAction {
case onAddButtonClick
Expand All @@ -16,6 +16,10 @@ enum SakatsuListScreenAction {
case onSettingsButtonClick
}

enum SakatsuListScreenAsyncAction {
case task
}

// MARK: - View

package struct SakatsuListScreen: View {
Expand Down Expand Up @@ -63,6 +67,9 @@ package struct SakatsuListScreen: View {
error: viewModel.uiState.sakatsuListError,
onDismiss: { viewModel.send(.screen(.onErrorAlertDismiss)) }
)
.task {
await viewModel.sendAsync(.screen(.task))
}
}

@MainActor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import SwiftUI
import SakatsuData

// MARK: Action
// MARK: Actions

enum SakatsuListViewAction {
case onCopySakatsuTextButtonClick(sakatsuIndex: Int)
case onEditButtonClick(sakatsuIndex: Int)
case onDelete(_ offsets: IndexSet)
}

enum SakatsuListViewAsyncAction {
}

// MARK: - View

struct SakatsuListView: View {
Expand Down

0 comments on commit 98d8e38

Please sign in to comment.