@@ -44,17 +44,17 @@ import SwiftUI
4444///
4545/// - Note: This is only meant to be used in classes.
4646/// If you want to do this inside a SwiftUI view, please refer to the ``Processed/Loadable`` property wrapper.
47- @ MainActor public protocol LoadableSupport : AnyObject {
47+ public protocol LoadableSupport : AnyObject {
4848
4949 /// Cancels the task of an ongoing resource loading process.
5050 ///
5151 /// - Note: You are responsible for cooperating with the task cancellation within the loading closures.
52- func cancel< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > )
53-
52+ @ MainActor func cancel< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > )
53+
5454 /// Cancels the task of an ongoing resource loading process and resets the state to `.absent`.
5555 ///
5656 /// - Note: You are responsible for cooperating with the task cancellation within the loading closures.
57- func reset< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > )
57+ @ MainActor func reset< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > )
5858
5959 /// Starts a resource loading process in a new `Task`, waiting for a return value or thrown error from the
6060 /// `block` closure, while setting the ``Processed/LoadableState`` accordingly.
@@ -72,8 +72,8 @@ import SwiftUI
7272 /// - block: The asynchronous block to run.
7373 ///
7474 /// - Returns: The task that runs the asynchronous loading process. You don't have to store it, but you can.
75- @discardableResult func load< Value> (
76- _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
75+ @MainActor @ discardableResult func load< Value> (
76+ _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
7777 silently runSilently: Bool ,
7878 priority: TaskPriority ? ,
7979 block: @escaping ( ) async throws -> Value
@@ -95,8 +95,8 @@ import SwiftUI
9595 /// - loadableState: The key path to the ``Processed/LoadableState``.
9696 /// - runSilently: If `true`, the state will not be set to `.loading` initially.
9797 /// - block: The asynchronous block to run.
98- func load< Value> (
99- _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
98+ @ MainActor func load< Value> (
99+ _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
100100 silently runSilently: Bool ,
101101 block: @escaping ( ) async throws -> Value
102102 ) async
@@ -117,8 +117,8 @@ import SwiftUI
117117 /// The block exposes a `yield` closure you can call to continuously update the resource loading state over time.
118118 ///
119119 /// - Returns: The task that runs the asynchronous loading process. You don't have to store it, but you can.
120- @discardableResult func load< Value> (
121- _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
120+ @MainActor @ discardableResult func load< Value> (
121+ _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
122122 silently runSilently: Bool ,
123123 priority: TaskPriority ? ,
124124 block: @escaping ( _ yield: ( _ state: LoadableState < Value > ) -> Void ) async throws -> Void
@@ -139,72 +139,86 @@ import SwiftUI
139139 /// - runSilently: If `true`, the state will not be set to `.loading` initially.
140140 /// - block: The asynchronous block to run.
141141 /// The block exposes a `yield` closure you can call to continuously update the resource loading state over time.
142- func load< Value> (
143- _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
142+ @ MainActor func load< Value> (
143+ _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
144144 silently runSilently: Bool ,
145145 block: @escaping ( _ yield: ( _ state: LoadableState < Value > ) -> Void ) async throws -> Void
146146 ) async
147147}
148148
149149extension LoadableSupport {
150150
151- public func cancel< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ) {
152- let identifier = ProcessIdentifier (
153- identifier: ObjectIdentifier ( self ) ,
154- keyPath: loadableState
155- )
156- tasks [ identifier] ? . cancel ( )
157- tasks. removeValue ( forKey: identifier)
151+ @MainActor public func cancel< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ) {
152+ let identifier = TaskStore . shared. identifier ( for: loadableState, in: self )
153+ TaskStore . shared. tasks [ identifier] ? . cancel ( )
154+ TaskStore . shared. tasks. removeValue ( forKey: identifier)
158155 }
159156
160- public func reset< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ) {
157+ @ MainActor public func reset< Value> ( _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ) {
161158 if case . absent = self [ keyPath: loadableState] { } else {
162159 self [ keyPath: loadableState] = . absent
163160 }
164161 cancel ( loadableState)
165162 }
166163
167- @discardableResult public func load< Value> (
164+ @MainActor @ discardableResult public func load< Value> (
168165 _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
169166 silently runSilently: Bool = false ,
170167 priority: TaskPriority ? = nil ,
171168 block: @escaping ( ) async throws -> Value
172169 ) -> Task < Void , Never > {
173- let identifier = ProcessIdentifier (
174- identifier: ObjectIdentifier ( self ) ,
175- keyPath: loadableState
176- )
177- tasks [ identifier] ? . cancel ( )
178- if !runSilently {
179- if case . loading = self [ keyPath: loadableState] { } else {
180- self [ keyPath: loadableState] = . loading
181- }
170+ let identifier = TaskStore . shared. identifier ( for: loadableState, in: self )
171+ TaskStore . shared. tasks [ identifier] ? . cancel ( )
172+ setLoadingStateIfNeeded ( on: loadableState, runSilently: runSilently)
173+ TaskStore . shared. tasks [ identifier] = Task ( priority: priority) {
174+ defer { TaskStore . shared. tasks [ identifier] = nil }
175+ await runReturningTaskBody ( loadableState, silently: runSilently, block: block)
182176 }
183- tasks [ identifier] = Task ( priority: priority) {
184- defer { // Cleanup
185- tasks [ identifier] = nil
186- }
187- await load (
188- loadableState,
189- silently: runSilently,
190- block: block
191- )
177+
178+ return TaskStore . shared. tasks [ identifier] !
179+ }
180+
181+ @MainActor public func load< Value> (
182+ _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
183+ silently runSilently: Bool = false ,
184+ block: @escaping ( ) async throws -> Value
185+ ) async {
186+ setLoadingStateIfNeeded ( on: loadableState, runSilently: runSilently)
187+ await runReturningTaskBody ( loadableState, silently: runSilently, block: block)
188+ }
189+
190+ @MainActor @discardableResult public func load< Value> (
191+ _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
192+ silently runSilently: Bool = false ,
193+ priority: TaskPriority ? = nil ,
194+ block: @escaping ( _ yield: ( _ state: LoadableState < Value > ) -> Void ) async throws -> Void
195+ ) -> Task < Void , Never > {
196+ let identifier = TaskStore . shared. identifier ( for: loadableState, in: self )
197+ TaskStore . shared. tasks [ identifier] ? . cancel ( )
198+ setLoadingStateIfNeeded ( on: loadableState, runSilently: runSilently)
199+ TaskStore . shared. tasks [ identifier] = Task ( priority: priority) {
200+ defer { TaskStore . shared. tasks [ identifier] = nil }
201+ await runYieldingTaskBody ( loadableState, silently: runSilently, block: block)
192202 }
193203
194- return tasks [ identifier] !
204+ return TaskStore . shared . tasks [ identifier] !
195205 }
196206
197- public func load< Value> (
207+ @MainActor public func load< Value> (
208+ _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
209+ silently runSilently: Bool = false ,
210+ block: @escaping ( _ yield: ( _ state: LoadableState < Value > ) -> Void ) async throws -> Void
211+ ) async {
212+ setLoadingStateIfNeeded ( on: loadableState, runSilently: runSilently)
213+ await runYieldingTaskBody ( loadableState, silently: runSilently, block: block)
214+ }
215+
216+ @MainActor private func runReturningTaskBody< Value> (
198217 _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
199218 silently runSilently: Bool = false ,
200219 block: @escaping ( ) async throws -> Value
201220 ) async {
202221 do {
203- if !runSilently {
204- if case . loading = self [ keyPath: loadableState] { } else {
205- self [ keyPath: loadableState] = . loading
206- }
207- }
208222 self [ keyPath: loadableState] = try await . loaded( block ( ) )
209223 } catch is CancellationError {
210224 // Task was cancelled. Don't change the state anymore
@@ -215,50 +229,13 @@ extension LoadableSupport {
215229 }
216230 }
217231
218- @discardableResult public func load< Value> (
219- _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
220- silently runSilently: Bool = false ,
221- priority: TaskPriority ? = nil ,
222- block: @escaping ( _ yield: ( _ state: LoadableState < Value > ) -> Void ) async throws -> Void
223- ) -> Task < Void , Never > {
224- let identifier = ProcessIdentifier (
225- identifier: ObjectIdentifier ( self ) ,
226- keyPath: loadableState
227- )
228- tasks [ identifier] ? . cancel ( )
229- if !runSilently {
230- if case . loading = self [ keyPath: loadableState] { } else {
231- self [ keyPath: loadableState] = . loading
232- }
233- }
234- tasks [ identifier] = Task ( priority: priority) {
235- defer { // Cleanup
236- tasks [ identifier] = nil
237- }
238- await load (
239- loadableState,
240- silently: runSilently,
241- block: block
242- )
243- }
244-
245- return tasks [ identifier] !
246- }
247-
248- public func load< Value> (
232+ @MainActor private func runYieldingTaskBody< Value> (
249233 _ loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
250234 silently runSilently: Bool = false ,
251235 block: @escaping ( _ yield: ( _ state: LoadableState < Value > ) -> Void ) async throws -> Void
252236 ) async {
253237 do {
254- if !runSilently {
255- if case . loading = self [ keyPath: loadableState] { } else {
256- self [ keyPath: loadableState] = . loading
257- }
258- }
259- try await block { state in
260- self [ keyPath: loadableState] = state
261- }
238+ try await block { self [ keyPath: loadableState] = $0 }
262239 } catch is CancellationError {
263240 // Task was cancelled. Don't change the state anymore
264241 } catch is CancelLoadable {
@@ -267,4 +244,15 @@ extension LoadableSupport {
267244 self [ keyPath: loadableState] = . error( error)
268245 }
269246 }
247+
248+ @MainActor private func setLoadingStateIfNeeded< Value> (
249+ on loadableState: ReferenceWritableKeyPath < Self , LoadableState < Value > > ,
250+ runSilently: Bool
251+ ) {
252+ if !runSilently {
253+ if case . loading = self [ keyPath: loadableState] { } else {
254+ self [ keyPath: loadableState] = . loading
255+ }
256+ }
257+ }
270258}
0 commit comments