Skip to content

Commit

Permalink
Merge pull request #12 from Dean151/on-button-error-modifier
Browse files Browse the repository at this point in the history
feat: added onButtonError modifier
  • Loading branch information
Dean151 authored Jan 18, 2025
2 parents 1a7fb26 + 4a6f815 commit 4a9f853
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Demo/ButtonKitDemo/Buttons/ThrowableButtonDemo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ struct ThrowableButtonDemo: View {
.throwableButtonStyle(.none)
}
.buttonStyle(.borderedProminent)
.onButtonError { error in
// Do something with the error
print(error)
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions Sources/ButtonKit/Button+Async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public struct AsyncButton<P: TaskProgress, S: View>: View {
@State private var state: AsyncButtonState = .idle
@ObservedObject private var progress: P
@State private var errorCount = 0
@State private var lastError: Error?

public var body: some View {
let throwableLabelConfiguration = ThrowableButtonStyleLabelConfiguration(
Expand Down Expand Up @@ -110,6 +111,7 @@ public struct AsyncButton<P: TaskProgress, S: View>: View {
.allowsHitTesting(allowsHitTestingWhenLoading || !state.isLoading)
.disabled(disabledWhenLoading && state.isLoading)
.preference(key: AsyncButtonTaskPreferenceKey.self, value: state)
.preference(key: AsyncButtonErrorPreferenceKey.self, value: lastError.flatMap { .init(increment: errorCount, error: $0) })
.onAppear {
guard let id else {
return
Expand Down Expand Up @@ -150,6 +152,7 @@ public struct AsyncButton<P: TaskProgress, S: View>: View {
try await action(progress)
} catch {
errorCount += 1
lastError = error
}
// Reset progress
await progress.ended()
Expand Down
71 changes: 71 additions & 0 deletions Sources/ButtonKit/Modifiers/Button+AsyncError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// Button+AsyncError.swift
// ButtonKit
//
// Created by Thomas Durand on 12/01/2025.
//

import SwiftUI

// MARK: Public protocol

public typealias AsyncButtonErrorHandler = @MainActor @Sendable (Error) -> Void

extension View {
public func onButtonError(_ handler: @escaping AsyncButtonErrorHandler) -> some View {
modifier(OnAsyncButtonErrorChangeModifier(handler: { error in
handler(error)
}))
}
}

// MARK: - Internal implementation

struct AsyncButtonErrorPreferenceKey: PreferenceKey {
static let defaultValue: ErrorHolder? = nil

static func reduce(value: inout ErrorHolder?, nextValue: () -> ErrorHolder?) {
guard let newValue = nextValue() else {
return
}
value = .init(increment: (value?.increment ?? 0) + newValue.increment, error: newValue.error)
}
}

struct ErrorHolder: Equatable {
let increment: Int
let error: Error

static func == (lhs: ErrorHolder, rhs: ErrorHolder) -> Bool {
lhs.increment == rhs.increment
}
}

struct OnAsyncButtonErrorChangeModifier: ViewModifier {
let handler: AsyncButtonErrorHandler

init(handler: @escaping AsyncButtonErrorHandler) {
self.handler = handler
}

func body(content: Content) -> some View {
content
.onPreferenceChange(AsyncButtonErrorPreferenceKey.self) { value in
guard let error = value?.error else {
return
}
#if swift(>=5.10)
MainActor.assumeIsolated {
onError(error)
}
#else
onError(error)
#endif
}
}

@MainActor
func onError(_ error: Error) {
handler(error)
}
}
2 changes: 1 addition & 1 deletion Sources/ButtonKit/Modifiers/Button+AsyncTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extension View {
}
}

// Internal implementation
// MARK: - Internal implementation

struct AsyncButtonTaskPreferenceKey: PreferenceKey {
static let defaultValue: AsyncButtonState = .idle
Expand Down

0 comments on commit 4a9f853

Please sign in to comment.