diff --git a/Package.swift b/Package.swift index 51a6fa7..a7d0f4b 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "AsyncView", - platforms: [.iOS(.v15)], + platforms: [.macOS(.v12), .iOS(.v15)], products: [ .library( name: "AsyncView", diff --git a/Sources/AsyncView/AsyncModel.swift b/Sources/AsyncView/AsyncModel.swift index 35dcc34..83414fd 100644 --- a/Sources/AsyncView/AsyncModel.swift +++ b/Sources/AsyncView/AsyncModel.swift @@ -3,9 +3,9 @@ import SwiftUI open class AsyncModel: ObservableObject { @MainActor @Published public private(set) var result = AsyncResult.empty - public typealias AsyncOperation = () async throws -> Success + public typealias AsyncOperation = (Bool) async throws -> Success - private var asyncOperationBlock: AsyncOperation = { + private var asyncOperationBlock: AsyncOperation = { _ in fatalError("Override asyncOperation or pass a asyncOperationBlock to use async model") } @@ -15,17 +15,17 @@ open class AsyncModel: ObservableObject { } } - open func asyncOperation() async throws -> Success { - try await self.asyncOperationBlock() + open func asyncOperation(forceRefreshRequested: Bool) async throws -> Success { + try await self.asyncOperationBlock(forceRefreshRequested) } @MainActor - public func load() async { + public func load(forceRefreshRequested: Bool) async { if case .inProgress = self.result { return } self.result = .inProgress do { - self.result = .success(try await self.asyncOperation()) + self.result = .success(try await self.asyncOperation(forceRefreshRequested: forceRefreshRequested)) } catch { self.result = .failure(error) } @@ -35,7 +35,7 @@ open class AsyncModel: ObservableObject { public func loadIfNeeded() async { switch self.result { case .empty, .failure: - await self.load() + await self.load(forceRefreshRequested: false) case .inProgress, .success: break } diff --git a/Sources/AsyncView/AsyncModelView.swift b/Sources/AsyncView/AsyncModelView.swift index 532df5f..c1f55b9 100644 --- a/Sources/AsyncView/AsyncModelView.swift +++ b/Sources/AsyncView/AsyncModelView.swift @@ -12,14 +12,16 @@ public struct AsyncModelView: View { public var body: some View { AsyncResultView( result: model.result, - reloadAction: { Task { await model.load() } }, + reloadAction: { Task { await model.load(forceRefreshRequested: true) } }, content: content ) - .task { - await model.loadIfNeeded() + .onAppear { + Task { + await model.loadIfNeeded() + } } .refreshable { - await model.load() + await model.load(forceRefreshRequested: true) } } } diff --git a/Sources/AsyncView/ErrorView.swift b/Sources/AsyncView/ErrorView.swift index 996b3bb..7ea40a7 100644 --- a/Sources/AsyncView/ErrorView.swift +++ b/Sources/AsyncView/ErrorView.swift @@ -3,10 +3,23 @@ import SwiftUI struct ErrorView: View { let error: Error let reloadAction: (() -> Void)? + @State private var showingPopover = false var body: some View { VStack(spacing: 10) { - Text(error.localizedDescription) + Button(error.localizedDescription) { + showingPopover = true + } +#if os(iOS) + .buttonStyle(.borderedProminent) +#else + .buttonStyle(.link) +#endif + .popover(isPresented: $showingPopover) { + Text(String(NSString(string: "\(error)"))) + .font(.caption) + .padding() + } if let reloadAction = reloadAction { Button( action: reloadAction,