Skip to content

Commit f33d701

Browse files
committed
update
1 parent 439aba5 commit f33d701

File tree

3 files changed

+40
-53
lines changed

3 files changed

+40
-53
lines changed

Package.resolved

+9
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
"version" : "1.5.0"
1010
}
1111
},
12+
{
13+
"identity" : "async-task",
14+
"kind" : "remoteSourceControl",
15+
"location" : "https://github.com/swiftuiux/async-task.git",
16+
"state" : {
17+
"revision" : "0e10077166d3b79d6fdffe4542ffc0196f63954f",
18+
"version" : "1.2.3"
19+
}
20+
},
1221
{
1322
"identity" : "retry-policy-service",
1423
"kind" : "remoteSourceControl",

Package.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ let package = Package(
1414
],
1515
dependencies: [
1616
// Dependencies declare other packages that this package depends on.
17-
.package(url: "https://github.com/swiftuiux/async-http-client.git", from: "1.5.0")
17+
.package(url: "https://github.com/swiftuiux/async-http-client.git", from: "1.5.0"),
18+
.package(url: "https://github.com/swiftuiux/async-task.git", from: "1.2.3")
1819
],
1920
targets: [
2021
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
2122
// Targets can depend on other targets in this package, and on products in packages this package depends on.
2223
.target(
2324
name: "openai-async-image-swiftui",
24-
dependencies: ["async-http-client"]),
25+
dependencies: ["async-http-client", "async-task"]),
2526
.testTarget(
2627
name: "openai-async-image-swiftuiTests",
2728
dependencies: ["openai-async-image-swiftui"]),

Sources/openai-async-image-swiftui/OpenAIAsyncImage.swift

+28-51
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,23 @@
66
//
77

88
import SwiftUI
9+
import async_task
910

1011
fileprivate typealias ImageSize = OpenAIImageSize
12+
fileprivate typealias TaskModel = Async.SingleTask<Image, AsyncImageErrors>
1113

1214
/// Async image component to load and show OpenAI image from OpenAI image API
1315
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
1416
public struct OpenAIAsyncImage<Content: View, T: IOpenAILoader>: View {
1517

18+
@StateObject private var taskModel = TaskModel(errorMapper: errorMapper)
19+
1620
/// Custom view builder template type alias
1721
public typealias ImageProcess = (ImageState) -> Content
1822

1923
/// Default loader, injected from environment
2024
@Environment(\.openAIDefaultLoader) var defaultLoader : OpenAIDefaultLoader
21-
22-
// MARK: - Private properties
23-
24-
/// State variable to hold the OpenAI image
25-
@State private var image: Image?
26-
27-
/// State variable to hold any errors encountered during loading
28-
@State private var error: Error?
29-
30-
/// State variable to hold the current task responsible for loading the image
31-
@State private var task : Task<Void, Never>?
32-
25+
3326
// MARK: - Config
3427

3528
/// A binding to the text prompt describing the desired image. The maximum length is 1000 characters
@@ -85,15 +78,13 @@ public struct OpenAIAsyncImage<Content: View, T: IOpenAILoader>: View {
8578
}
8679
}
8780
.onChange(of: prompt){ _ in
88-
cancelTask()
89-
clear()
90-
task = getTask()
81+
start()
9182
}
9283
.onAppear {
93-
task = getTask()
84+
start()
9485
}
9586
.onDisappear{
96-
cancelTask()
87+
cancel()
9788
}
9889
}
9990

@@ -102,8 +93,8 @@ public struct OpenAIAsyncImage<Content: View, T: IOpenAILoader>: View {
10293
/// - Returns: The current image state status
10394
private func getState () -> ImageState{
10495

105-
if let image { return .loaded(image) }
106-
else if let error { return .loadError(error)}
96+
if let image = taskModel.value { return .loaded(image) }
97+
else if let error = taskModel.error { return .loadError(error)}
10798

10899
return .loading
109100
}
@@ -139,42 +130,19 @@ public struct OpenAIAsyncImage<Content: View, T: IOpenAILoader>: View {
139130
}
140131
return try await loadImageDefault(prompt, with: size, model: model)
141132
}
142-
143-
/// Sets the image on the main thread
144-
/// - Parameter value: The image to be set
145-
@MainActor
146-
private func setImage(_ value : Image){
147-
image = value
148-
}
149-
150-
/// Clears the image and error state properties
151-
@MainActor
152-
private func clear(){
153-
image = nil
154-
error = nil
155-
}
156-
157-
/// Cancels the current loading task if any
158-
private func cancelTask(){
159-
task?.cancel()
160-
task = nil
161-
}
162-
133+
163134
/// Creates and returns a task to fetch the OpenAI image
164135
/// - Returns: A task that fetches the OpenAI image
165-
private func getTask() -> Task<Void, Never>{
166-
Task{
167-
do{
168-
if let image = try await loadImage(prompt, with: size, model: model){
169-
setImage(image)
170-
}
171-
}catch is CancellationError{
172-
self.error = AsyncImageErrors.cancellationError
173-
}catch{
174-
self.error = error
175-
}
136+
private func start(){
137+
taskModel.start{
138+
try await loadImage(prompt, with: size, model: model)
176139
}
177140
}
141+
142+
/// Cancel task
143+
private func cancel(){
144+
taskModel.cancel()
145+
}
178146
}
179147

180148
// MARK: - Public extensions -
@@ -232,3 +200,12 @@ fileprivate func imageTpl(_ state : ImageState) -> some View{
232200
case .loading : ProgressView()
233201
}
234202
}
203+
204+
@Sendable
205+
fileprivate func errorMapper(_ error : Error?) -> AsyncImageErrors?{
206+
if error is CancellationError{
207+
return .cancellationError
208+
}
209+
210+
return nil
211+
}

0 commit comments

Comments
 (0)