Skip to content

Commit 1877cbe

Browse files
author
Hoang Pham
committed
fix: fix revert state doesn't reset after a successful fetch
1 parent f2da2b3 commit 1877cbe

5 files changed

Lines changed: 139 additions & 37 deletions

File tree

Example/swiftui-query-demo/InitialDataDemo.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ struct InitialDataDemoView: View {
132132
queryKey: "pokemon-initial-\(selectedPokemonId)",
133133
queryFn: { _ in try await PokemonAPI.fetchPokemon(id: selectedPokemonId) },
134134
staleTime: 30, // 30 seconds for demo - shows stale vs fresh states
135-
enabled: true,
136-
initialData: selectedPokemonId == 1 ? initialPokemonData : nil
135+
initialData: selectedPokemonId == 1 ? initialPokemonData : nil,
136+
enabled: true
137137
) { result in
138138
ScrollView {
139139
VStack(alignment: .leading, spacing: 20) {

Sources/SwiftUIQuery/InfiniteQuery.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,9 @@ public final class InfiniteQuery<
659659
// Update state with the new data (replacing old data)
660660
dispatch(.success(data: newData, dataUpdatedAt: nil, manual: false))
661661

662+
// Clear revertState on successful fetch (no longer needed)
663+
self.revertState = nil
664+
662665
// Clear the fetch task and direction
663666
self.fetchTask = nil
664667
self.currentFetchDirection = nil

Sources/SwiftUIQuery/Query.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ public final class Query<TData: Sendable, TKey: QueryKey>: AnyQuery {
528528

529529
/// Internal fetch implementation that executes the query function
530530
public func internalFetch() async throws -> TData {
531+
// Store current state to revert if fetch is cancelled
532+
revertState = state
533+
531534
// Cancel any existing fetch
532535
fetchTask?.cancel()
533536

@@ -548,6 +551,9 @@ public final class Query<TData: Sendable, TKey: QueryKey>: AnyQuery {
548551
// Update state with success
549552
dispatch(.success(data: data, dataUpdatedAt: nil, manual: false))
550553

554+
// Clear revertState on successful fetch (no longer needed)
555+
self.revertState = nil
556+
551557
// Clear the fetch task
552558
self.fetchTask = nil
553559

Sources/SwiftUIQuery/UseInfiniteQuery.swift

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,14 @@ public struct UseInfiniteQuery<
183183
/// - getPreviousPageParam: Function to determine the previous page parameter
184184
/// - initialPageParam: Initial page parameter for the first page
185185
/// - maxPages: Maximum number of pages to retain
186+
/// - retryConfig: Configuration for retry behavior (default: RetryConfig())
187+
/// - networkMode: Network behavior configuration (default: .online)
186188
/// - staleTime: Time before data is considered stale (default: 0)
187189
/// - gcTime: Time before unused data is garbage collected (default: 5 minutes)
190+
/// - refetchTriggers: Configuration for automatic refetching triggers (default: .default)
191+
/// - refetchOnAppear: When to refetch data on view appear (default: .ifStale)
192+
/// - structuralSharing: Whether to use structural sharing for performance (default: true)
193+
/// - meta: Arbitrary metadata for this query
188194
/// - enabled: Whether the query should execute automatically (default: true)
189195
/// - queryClient: Optional query client (uses shared instance if nil)
190196
/// - content: View builder that receives the infinite query result
@@ -195,8 +201,14 @@ public struct UseInfiniteQuery<
195201
getPreviousPageParam: GetPreviousPageParamFunction<TData, TPageParam>? = nil,
196202
initialPageParam: TPageParam? = nil,
197203
maxPages: Int? = nil,
204+
retryConfig: RetryConfig = RetryConfig(),
205+
networkMode: NetworkMode = .online,
198206
staleTime: TimeInterval = 0,
199207
gcTime: TimeInterval = defaultGcTime,
208+
refetchTriggers: RefetchTriggers = .default,
209+
refetchOnAppear: RefetchOnAppear = .ifStale,
210+
structuralSharing: Bool = true,
211+
meta: QueryMeta? = nil,
200212
enabled: Bool = true,
201213
queryClient: QueryClient? = nil,
202214
@ViewBuilder content: @escaping (UseInfiniteQueryResult<TData, TPageParam>) -> Content
@@ -208,14 +220,14 @@ public struct UseInfiniteQuery<
208220
getPreviousPageParam: getPreviousPageParam,
209221
initialPageParam: initialPageParam,
210222
maxPages: maxPages,
211-
retryConfig: RetryConfig(),
212-
networkMode: NetworkMode.online,
223+
retryConfig: retryConfig,
224+
networkMode: networkMode,
213225
staleTime: staleTime,
214226
gcTime: gcTime,
215-
refetchTriggers: RefetchTriggers.default,
216-
refetchOnAppear: RefetchOnAppear.ifStale,
217-
structuralSharing: true,
218-
meta: nil as QueryMeta?,
227+
refetchTriggers: refetchTriggers,
228+
refetchOnAppear: refetchOnAppear,
229+
structuralSharing: structuralSharing,
230+
meta: meta,
219231
enabled: enabled
220232
)
221233

@@ -292,26 +304,65 @@ public struct UseInfiniteQuery<
292304

293305
/// Additional convenience methods for SwiftUI integration
294306
extension UseInfiniteQuery {
295-
/// Create a UseInfiniteQuery with simple parameters for common use cases
307+
/// Create a UseInfiniteQuery with string-based query key
296308
/// - Parameters:
297309
/// - queryKey: String-based query key
298310
/// - queryFn: Function that fetches page data
299311
/// - getNextPageParam: Function to get next page parameter from pages
312+
/// - getPreviousPageParam: Function to determine the previous page parameter
313+
/// - initialPageParam: Initial page parameter for the first page
314+
/// - maxPages: Maximum number of pages to retain
315+
/// - retryConfig: Configuration for retry behavior (default: RetryConfig())
316+
/// - networkMode: Network behavior configuration (default: .online)
317+
/// - staleTime: Time before data is considered stale (default: 0)
318+
/// - gcTime: Time before unused data is garbage collected (default: 5 minutes)
319+
/// - refetchTriggers: Configuration for automatic refetching triggers (default: .default)
320+
/// - refetchOnAppear: When to refetch data on view appear (default: .ifStale)
321+
/// - structuralSharing: Whether to use structural sharing for performance (default: true)
322+
/// - meta: Arbitrary metadata for this query
323+
/// - enabled: Whether the query should execute automatically (default: true)
324+
/// - queryClient: Optional query client (uses shared instance if nil)
300325
/// - content: View builder that receives the query result
301326
public init(
302327
queryKey: String,
303328
queryFn: @escaping @Sendable (String, TPageParam?) async throws -> TData,
304329
getNextPageParam: @escaping GetNextPageParamFunction<TData, TPageParam>,
330+
getPreviousPageParam: GetPreviousPageParamFunction<TData, TPageParam>? = nil,
331+
initialPageParam: TPageParam? = nil,
332+
maxPages: Int? = nil,
333+
retryConfig: RetryConfig = RetryConfig(),
334+
networkMode: NetworkMode = .online,
335+
staleTime: TimeInterval = 0,
336+
gcTime: TimeInterval = defaultGcTime,
337+
refetchTriggers: RefetchTriggers = .default,
338+
refetchOnAppear: RefetchOnAppear = .ifStale,
339+
structuralSharing: Bool = true,
340+
meta: QueryMeta? = nil,
341+
enabled: Bool = true,
342+
queryClient: QueryClient? = nil,
305343
@ViewBuilder content: @escaping (UseInfiniteQueryResult<TData, TPageParam>) -> Content
306344
) where TKey == String {
307345
let options = InfiniteQueryOptions<TData, QueryError, String, TPageParam>(
308346
queryKey: queryKey,
309347
queryFn: queryFn,
310-
getNextPageParam: getNextPageParam
348+
getNextPageParam: getNextPageParam,
349+
getPreviousPageParam: getPreviousPageParam,
350+
initialPageParam: initialPageParam,
351+
maxPages: maxPages,
352+
retryConfig: retryConfig,
353+
networkMode: networkMode,
354+
staleTime: staleTime,
355+
gcTime: gcTime,
356+
refetchTriggers: refetchTriggers,
357+
refetchOnAppear: refetchOnAppear,
358+
structuralSharing: structuralSharing,
359+
meta: meta,
360+
enabled: enabled
311361
)
312362

313363
self.init(
314364
options: options,
365+
queryClient: queryClient,
315366
content: content
316367
)
317368
}

Sources/SwiftUIQuery/UseQuery.swift

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -97,35 +97,49 @@ public struct UseQuery<TData: Sendable, TKey: QueryKey, Content: View>: View {
9797
/// - Parameters:
9898
/// - queryKey: Unique identifier for the query
9999
/// - queryFn: Function that fetches the data
100+
/// - retryConfig: Configuration for retry behavior (default: RetryConfig())
101+
/// - networkMode: Network behavior configuration (default: .online)
100102
/// - staleTime: Time before data is considered stale (default: 0)
101103
/// - gcTime: Time before unused data is garbage collected (default: 5 minutes)
102-
/// - enabled: Whether the query should execute automatically (default: true)
104+
/// - refetchTriggers: Configuration for automatic refetching triggers (default: .default)
105+
/// - refetchOnAppear: When to refetch data on view appear (default: .ifStale)
103106
/// - initialData: Initial data to show while the query loads
107+
/// - initialDataFunction: Function to provide initial data
108+
/// - structuralSharing: Whether to use structural sharing for performance (default: true)
109+
/// - meta: Arbitrary metadata for this query
110+
/// - enabled: Whether the query should execute automatically (default: true)
104111
/// - queryClient: Optional query client (uses shared instance if nil)
105112
/// - content: View builder that receives the query result
106113
public init(
107114
queryKey: TKey,
108115
queryFn: @escaping @Sendable (TKey) async throws -> TData,
116+
retryConfig: RetryConfig = RetryConfig(),
117+
networkMode: NetworkMode = .online,
109118
staleTime: TimeInterval = 0,
110119
gcTime: TimeInterval = defaultGcTime,
111-
enabled: Bool = true,
120+
refetchTriggers: RefetchTriggers = .default,
121+
refetchOnAppear: RefetchOnAppear = .ifStale,
112122
initialData: TData? = nil,
123+
initialDataFunction: InitialDataFunction<TData>? = nil,
124+
structuralSharing: Bool = true,
125+
meta: QueryMeta? = nil,
126+
enabled: Bool = true,
113127
queryClient: QueryClient? = nil,
114128
@ViewBuilder content: @escaping (UseQueryResult<TData>) -> Content
115129
) {
116130
let options = QueryOptions<TData, TKey>(
117131
queryKey: queryKey,
118132
queryFn: queryFn,
119-
retryConfig: RetryConfig(),
120-
networkMode: NetworkMode.online,
133+
retryConfig: retryConfig,
134+
networkMode: networkMode,
121135
staleTime: staleTime,
122136
gcTime: gcTime,
123-
refetchTriggers: RefetchTriggers.default,
124-
refetchOnAppear: RefetchOnAppear.ifStale,
137+
refetchTriggers: refetchTriggers,
138+
refetchOnAppear: refetchOnAppear,
125139
initialData: initialData,
126-
initialDataFunction: nil as InitialDataFunction<TData>?,
127-
structuralSharing: true,
128-
meta: nil as QueryMeta?,
140+
initialDataFunction: initialDataFunction,
141+
structuralSharing: structuralSharing,
142+
meta: meta,
129143
enabled: enabled
130144
)
131145

@@ -181,35 +195,49 @@ extension UseQuery {
181195
/// - Parameters:
182196
/// - queryKey: String identifier for the query
183197
/// - queryFn: Function that fetches the data
198+
/// - retryConfig: Configuration for retry behavior (default: RetryConfig())
199+
/// - networkMode: Network behavior configuration (default: .online)
184200
/// - staleTime: Time before data is considered stale (default: 0)
185201
/// - gcTime: Time before unused data is garbage collected (default: 5 minutes)
186-
/// - enabled: Whether the query should execute automatically (default: true)
202+
/// - refetchTriggers: Configuration for automatic refetching triggers (default: .default)
203+
/// - refetchOnAppear: When to refetch data on view appear (default: .ifStale)
187204
/// - initialData: Initial data to show while the query loads
205+
/// - initialDataFunction: Function to provide initial data
206+
/// - structuralSharing: Whether to use structural sharing for performance (default: true)
207+
/// - meta: Arbitrary metadata for this query
208+
/// - enabled: Whether the query should execute automatically (default: true)
188209
/// - queryClient: Optional query client (uses shared instance if nil)
189210
/// - content: View builder that receives the query result
190211
public init(
191212
queryKey: String,
192213
queryFn: @escaping @Sendable (String) async throws -> TData,
214+
retryConfig: RetryConfig = RetryConfig(),
215+
networkMode: NetworkMode = .online,
193216
staleTime: TimeInterval = 0,
194217
gcTime: TimeInterval = defaultGcTime,
195-
enabled: Bool = true,
218+
refetchTriggers: RefetchTriggers = .default,
219+
refetchOnAppear: RefetchOnAppear = .ifStale,
196220
initialData: TData? = nil,
221+
initialDataFunction: InitialDataFunction<TData>? = nil,
222+
structuralSharing: Bool = true,
223+
meta: QueryMeta? = nil,
224+
enabled: Bool = true,
197225
queryClient: QueryClient? = nil,
198226
@ViewBuilder content: @escaping (UseQueryResult<TData>) -> Content
199227
) where TKey == String {
200228
let options = QueryOptions<TData, String>(
201229
queryKey: queryKey,
202230
queryFn: queryFn,
203-
retryConfig: RetryConfig(),
204-
networkMode: NetworkMode.online,
231+
retryConfig: retryConfig,
232+
networkMode: networkMode,
205233
staleTime: staleTime,
206234
gcTime: gcTime,
207-
refetchTriggers: RefetchTriggers.default,
208-
refetchOnAppear: RefetchOnAppear.ifStale,
235+
refetchTriggers: refetchTriggers,
236+
refetchOnAppear: refetchOnAppear,
209237
initialData: initialData,
210-
initialDataFunction: nil as InitialDataFunction<TData>?,
211-
structuralSharing: true,
212-
meta: nil as QueryMeta?,
238+
initialDataFunction: initialDataFunction,
239+
structuralSharing: structuralSharing,
240+
meta: meta,
213241
enabled: enabled
214242
)
215243

@@ -225,35 +253,49 @@ extension UseQuery {
225253
/// - Parameters:
226254
/// - queryKey: Array identifier for the query
227255
/// - queryFn: Function that fetches the data
256+
/// - retryConfig: Configuration for retry behavior (default: RetryConfig())
257+
/// - networkMode: Network behavior configuration (default: .online)
228258
/// - staleTime: Time before data is considered stale (default: 0)
229259
/// - gcTime: Time before unused data is garbage collected (default: 5 minutes)
230-
/// - enabled: Whether the query should execute automatically (default: true)
260+
/// - refetchTriggers: Configuration for automatic refetching triggers (default: .default)
261+
/// - refetchOnAppear: When to refetch data on view appear (default: .ifStale)
231262
/// - initialData: Initial data to show while the query loads
263+
/// - initialDataFunction: Function to provide initial data
264+
/// - structuralSharing: Whether to use structural sharing for performance (default: true)
265+
/// - meta: Arbitrary metadata for this query
266+
/// - enabled: Whether the query should execute automatically (default: true)
232267
/// - queryClient: Optional query client (uses shared instance if nil)
233268
/// - content: View builder that receives the query result
234269
public init(
235270
queryKey: [String],
236271
queryFn: @escaping @Sendable ([String]) async throws -> TData,
272+
retryConfig: RetryConfig = RetryConfig(),
273+
networkMode: NetworkMode = .online,
237274
staleTime: TimeInterval = 0,
238275
gcTime: TimeInterval = defaultGcTime,
239-
enabled: Bool = true,
276+
refetchTriggers: RefetchTriggers = .default,
277+
refetchOnAppear: RefetchOnAppear = .ifStale,
240278
initialData: TData? = nil,
279+
initialDataFunction: InitialDataFunction<TData>? = nil,
280+
structuralSharing: Bool = true,
281+
meta: QueryMeta? = nil,
282+
enabled: Bool = true,
241283
queryClient: QueryClient? = nil,
242284
@ViewBuilder content: @escaping (UseQueryResult<TData>) -> Content
243285
) where TKey == [String] {
244286
let options = QueryOptions<TData, [String]>(
245287
queryKey: queryKey,
246288
queryFn: queryFn,
247-
retryConfig: RetryConfig(),
248-
networkMode: NetworkMode.online,
289+
retryConfig: retryConfig,
290+
networkMode: networkMode,
249291
staleTime: staleTime,
250292
gcTime: gcTime,
251-
refetchTriggers: RefetchTriggers.default,
252-
refetchOnAppear: RefetchOnAppear.ifStale,
293+
refetchTriggers: refetchTriggers,
294+
refetchOnAppear: refetchOnAppear,
253295
initialData: initialData,
254-
initialDataFunction: nil as InitialDataFunction<TData>?,
255-
structuralSharing: true,
256-
meta: nil as QueryMeta?,
296+
initialDataFunction: initialDataFunction,
297+
structuralSharing: structuralSharing,
298+
meta: meta,
257299
enabled: enabled
258300
)
259301

0 commit comments

Comments
 (0)