Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ A Swift package that provides elegant state management for asynchronous operatio

AsyncLoad provides components for handling asynchronous operations:
- `AsyncLoad<T>`: For loading data operations
- `AsyncLoad` (without type): For operations that track loading state without data (uses `NoContent`)
- `CachedAsyncLoad<T>`: For loading operations that preserve cached data during refreshes
- `AsyncLoadView`: A SwiftUI view component for displaying async states
- `CachedAsyncLoadView`: A SwiftUI view component for cached async states
Expand Down Expand Up @@ -80,6 +81,49 @@ class DataViewModel {
}
```

### AsyncLoad without Type Parameter (NoContent)

For operations that need to track loading state but don't have any data to return, you can use `AsyncLoad` without a type parameter. This uses the `NoContent` type internally.

```swift
public struct NoContent: Equatable, Sendable
public typealias AsyncLoadNoContent = AsyncLoad<NoContent>
```

This is useful for operations like:
- Delete operations
- Simple actions (e.g., mark as read, archive)
- Refresh operations without data
- Any operation where you only care about success/failure/loading state

#### Example Usage

```swift
import AsyncLoad

@Observable
class ActionViewModel {
var deleteStatus: AsyncLoad = .none // No type parameter needed

func deleteItem(id: String) async {
deleteStatus = .loading

do {
try await itemService.deleteItem(id: id)
deleteStatus = .loaded // Convenience property for NoContent
} catch {
deleteStatus = .error(error)
}
}
}
```

You can also use the type alias explicitly:

```swift
var deleteStatus: AsyncLoadNoContent = .none
```

### CachedAsyncLoad<T>

An enhanced version of AsyncLoad that preserves cached data during loading and error states.
Expand Down
17 changes: 17 additions & 0 deletions Sources/AsyncLoad/AsyncLoad/AsyncLoad+NoContent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Foundation

public typealias AsyncLoadNoContent = AsyncLoad<NoContent>

public struct NoContent: Equatable, Sendable {
public init() { }
}

public extension AsyncLoad where T == NoContent {
init(_ type: AsyncLoad = .none) {
self = type
}

static var loaded: AsyncLoad<NoContent> {
.loaded(NoContent())
}
}
34 changes: 34 additions & 0 deletions Tests/AsyncLoadTests/AsyncLoad+NoContentTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// AsyncLoad+NoContentTests.swift
// AsyncLoad
//
// Created by Alexander Kauer on 30.10.25.
//

import Foundation
import Testing

@Suite("Test no content AsyncLoad")
struct AsyncLoadNoContentTests {

@Test<[AsyncLoadNoContentParameter]>("Should be equal", arguments: [
.init(.none, .none),
.init(.loading, .loading),
.init(.loaded, .loaded),
.init(.error(TestingError.some), .error(TestingError.some)),
])
func noContentEqual(_ parameter: AsyncLoadNoContentParameter) {
#expect(parameter.load1 == parameter.load2)
}


@Test<[AsyncLoadNoContentParameter]>("Should not be equal", arguments: [
.init(.none, .loading),
.init(.loading, .loaded),
.init(.loaded, .error(TestingError.some)),
.init(.error(TestingError.some), .none),
])
func noContentNotEqual(_ parameter: AsyncLoadNoContentParameter) {
#expect(parameter.load1 != parameter.load2)
}
}
2 changes: 2 additions & 0 deletions Tests/AsyncLoadTests/AsyncLoadEquatableItemTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ struct AsyncLoadEquatableItemTests {
func nonEqualUser(param: AsyncLoadParameter<User>) async throws {
#expect(param.load1 != param.load2)
}


}
10 changes: 10 additions & 0 deletions Tests/AsyncLoadTests/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ struct AsyncLoadParameter<T: Equatable & Sendable> {
}
}

struct AsyncLoadNoContentParameter {
let load1: AsyncLoadNoContent
let load2: AsyncLoadNoContent

init(_ load1: AsyncLoadNoContent, _ load2: AsyncLoadNoContent) {
self.load1 = load1
self.load2 = load2
}
}

struct CachedAsyncLoadParameter<T: Equatable & Sendable> {
let load1: CachedAsyncLoad<T>
let load2: CachedAsyncLoad<T>
Expand Down