Skip to content

Commit

Permalink
Implement a generic AsyncButton with support for throwing closures (#8)
Browse files Browse the repository at this point in the history
Co-authored-by: Paul Schmiedmayer <[email protected]>
  • Loading branch information
Supereg and PSchmiedmayer authored Jul 13, 2023
1 parent bae5330 commit 10ae4b1
Show file tree
Hide file tree
Showing 17 changed files with 549 additions and 34 deletions.
6 changes: 0 additions & 6 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
only_rules:
# All Images that provide context should have an accessibility label. Purely decorative images can be hidden from accessibility.
- accessibility_label_for_image
# Attributes should be on their own lines in functions and types, but on the same line as variables and imports.
- attributes
# Prefer using Array(seq) over seq.map { $0 } to convert a sequence into an Array.
- array_init
# Prefer the new block based KVO API with keypaths when using Swift 3.2 or later.
Expand Down Expand Up @@ -141,8 +139,6 @@ only_rules:
- implicitly_unwrapped_optional
# Identifiers should use inclusive language that avoids discrimination against groups of people based on race, gender, or socioeconomic status
- inclusive_language
# If defer is at the end of its parent scope, it will be executed right where it is anyway.
- inert_defer
# Prefer using Set.isDisjoint(with:) over Set.intersection(_:).isEmpty.
- is_disjoint
# Discouraged explicit usage of the default separator.
Expand Down Expand Up @@ -329,8 +325,6 @@ only_rules:
- unowned_variable_capture
# Catch statements should not declare error variables without type casting.
- untyped_error_in_catch
# Unused reference in a capture list should be removed.
- unused_capture_list
# Unused parameter in a closure should be replaced with _.
- unused_closure_parameter
# Unused control flow label should be removed.
Expand Down
3 changes: 2 additions & 1 deletion Sources/SpeziViews/Environment/DefaultErrorDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

import SwiftUI


/// An `EnvironmentKey` that provides access to the default, localized error description.
///
/// This might be helpful for views that rely on ``AnyLocalizedError``. Outer views can define a
/// sensible default for a localized default error description in the case that a sub-view has to display
/// an ``AnyLocalizedError`` for a generic error.
public struct DefaultErrorDescription: EnvironmentKey {
public static var defaultValue: LocalizedStringResource?
public static let defaultValue: LocalizedStringResource? = nil
}

extension EnvironmentValues {
Expand Down
31 changes: 31 additions & 0 deletions Sources/SpeziViews/Environment/ProcessingDebounceDuration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import SwiftUI


/// An `EnvironmentKey` that provides a generalized configuration for debounce durations for any processing-related operations.
///
/// This might be helpful to provide extensive customization points without introducing clutter in the initializer of views.
/// The ``AsyncButton`` is one example where this `EnvironmentKey` is used.
public struct ProcessingDebounceDuration: EnvironmentKey {
public static let defaultValue: Duration = .milliseconds(150)
}


extension EnvironmentValues {
/// Refer to the documentation of ``ProcessingDebounceDuration``.
public var processingDebounceDuration: Duration {
get {
self[ProcessingDebounceDuration.self]
}
set {
self[ProcessingDebounceDuration.self] = newValue
}
}
}
35 changes: 20 additions & 15 deletions Sources/SpeziViews/Model/ViewState.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//
Expand All @@ -10,15 +10,30 @@ import Foundation


/// The ``ViewState`` allows SwiftUI views to keep track of their state and possible communicate it to outside views, e.g., using `Binding`s.
public enum ViewState: Equatable {
public enum ViewState {
/// The view is idle and displaying content.
case idle
/// The view is in a processing state, e.g. loading content.
case processing
/// The view is in an error state, e.g., loading the content failed.
case error(LocalizedError)


}

// MARK: - ViewState Extensions

extension ViewState: Equatable {
public static func == (lhs: ViewState, rhs: ViewState) -> Bool {
switch (lhs, rhs) {
case (.idle, .idle), (.processing, .processing), (.error, .error):
return true
default:
return false
}
}
}

// MARK: - ViewState + Error
extension ViewState {
/// The localized error title of the view if it is in an error state. An empty string if it is in an non-error state.
public var errorTitle: String {
switch self {
Expand All @@ -37,7 +52,7 @@ public enum ViewState: Equatable {
return String(localized: "VIEW_STATE_DEFAULT_ERROR_TITLE", bundle: .module)
}
}

/// The localized error description of the view if it is in an error state. An empty string if it is in an non-error state.
public var errorDescription: String {
switch self {
Expand All @@ -60,14 +75,4 @@ public enum ViewState: Equatable {
return ""
}
}


public static func == (lhs: ViewState, rhs: ViewState) -> Bool {
switch (lhs, rhs) {
case (.idle, .idle), (.processing, .processing), (.error, .error):
return true
default:
return false
}
}
}
54 changes: 54 additions & 0 deletions Sources/SpeziViews/ViewModifier/ProcessingOverlay.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import SwiftUI


private struct ProcessingOverlay<Overlay: View>: ViewModifier {
fileprivate var isProcessing: Bool
@ViewBuilder fileprivate var overlay: () -> Overlay


func body(content: Content) -> some View {
content
.opacity(isProcessing ? 0.0 : 1.0)
.overlay {
if isProcessing {
overlay()
}
}
}
}


extension View {
/// Modifies the view to be replaced by an processing indicator based on the supplied condition.
/// - Parameters:
/// - state: The `ViewState` that is used to determine whether the view is replaced by the processing overlay.
/// We consider the view to be processing if the state is ``ViewState/processing``.
/// - overlay: A view which which the modified view is overlayed with when state is processing.
/// - Returns: A view that may render processing state.
public func processingOverlay<Overlay: View>(
isProcessing state: ViewState,
@ViewBuilder overlay: @escaping () -> Overlay = { ProgressView() }
) -> some View {
processingOverlay(isProcessing: state == .processing, overlay: overlay)
}

/// Modifies the view to be replaced by an processing indicator based on the supplied condition.
/// - Parameters:
/// - processing: A Boolean value that determines whether the view is replaced by the processing overlay.
/// - overlay: A view which which the modified view is overlayed with when state is processing.
/// - Returns: A view that may render processing state.
public func processingOverlay<Overlay: View>(
isProcessing processing: Bool,
@ViewBuilder overlay: @escaping () -> Overlay = { ProgressView() }
) -> some View {
modifier(ProcessingOverlay(isProcessing: processing, overlay: overlay))
}
}
Loading

0 comments on commit 10ae4b1

Please sign in to comment.