Skip to content

Commit 996e616

Browse files
author
Hoang Pham
committed
UseInfiniteQuery tuple key inits
1 parent 7857cdd commit 996e616

3 files changed

Lines changed: 162 additions & 8 deletions

File tree

Sources/SwiftUIQuery/UseInfiniteQuery.swift

Lines changed: 133 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,9 @@ public struct UseInfiniteQuery<
304304

305305
/// Additional convenience methods for SwiftUI integration
306306
extension UseInfiniteQuery {
307-
/// Create a UseInfiniteQuery with string-based query key
307+
/// Create UseInfiniteQuery with KeyTuple2-based query key
308308
/// - Parameters:
309-
/// - queryKey: String-based query key
309+
/// - queryKey: KeyTuple2 identifier for the query
310310
/// - queryFn: Function that fetches page data
311311
/// - getNextPageParam: Function to get next page parameter from pages
312312
/// - getPreviousPageParam: Function to determine the previous page parameter
@@ -323,9 +323,135 @@ extension UseInfiniteQuery {
323323
/// - enabled: Whether the query should execute automatically (default: true)
324324
/// - queryClient: Optional query client (uses shared instance if nil)
325325
/// - content: View builder that receives the query result
326-
public init(
327-
queryKey: String,
328-
queryFn: @escaping @Sendable (String, TPageParam?) async throws -> TData,
326+
public init<K1: QueryKeyCodable, K2: QueryKeyCodable>(
327+
queryKey: KeyTuple2<K1, K2>,
328+
queryFn: @escaping @Sendable (KeyTuple2<K1, K2>, TPageParam?) async throws -> TData,
329+
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,
343+
@ViewBuilder content: @escaping (UseInfiniteQueryResult<TData, TPageParam>) -> Content
344+
) where TKey == KeyTuple2<K1, K2> {
345+
let options = InfiniteQueryOptions<TData, QueryError, KeyTuple2<K1, K2>, TPageParam>(
346+
queryKey: queryKey,
347+
queryFn: queryFn,
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
361+
)
362+
363+
self.init(
364+
options: options,
365+
queryClient: queryClient,
366+
content: content
367+
)
368+
}
369+
370+
/// Create UseInfiniteQuery with KeyTuple3-based query key
371+
/// - Parameters:
372+
/// - queryKey: KeyTuple3 identifier for the query
373+
/// - queryFn: Function that fetches page data
374+
/// - getNextPageParam: Function to get next page parameter from pages
375+
/// - getPreviousPageParam: Function to determine the previous page parameter
376+
/// - initialPageParam: Initial page parameter for the first page
377+
/// - maxPages: Maximum number of pages to retain
378+
/// - retryConfig: Configuration for retry behavior (default: RetryConfig())
379+
/// - networkMode: Network behavior configuration (default: .online)
380+
/// - staleTime: Time before data is considered stale (default: 0)
381+
/// - gcTime: Time before unused data is garbage collected (default: 5 minutes)
382+
/// - refetchTriggers: Configuration for automatic refetching triggers (default: .default)
383+
/// - refetchOnAppear: When to refetch data on view appear (default: .ifStale)
384+
/// - structuralSharing: Whether to use structural sharing for performance (default: true)
385+
/// - meta: Arbitrary metadata for this query
386+
/// - enabled: Whether the query should execute automatically (default: true)
387+
/// - queryClient: Optional query client (uses shared instance if nil)
388+
/// - content: View builder that receives the query result
389+
public init<K1: QueryKeyCodable, K2: QueryKeyCodable, K3: QueryKeyCodable>(
390+
queryKey: KeyTuple3<K1, K2, K3>,
391+
queryFn: @escaping @Sendable (KeyTuple3<K1, K2, K3>, TPageParam?) async throws -> TData,
392+
getNextPageParam: @escaping GetNextPageParamFunction<TData, TPageParam>,
393+
getPreviousPageParam: GetPreviousPageParamFunction<TData, TPageParam>? = nil,
394+
initialPageParam: TPageParam? = nil,
395+
maxPages: Int? = nil,
396+
retryConfig: RetryConfig = RetryConfig(),
397+
networkMode: NetworkMode = .online,
398+
staleTime: TimeInterval = 0,
399+
gcTime: TimeInterval = defaultGcTime,
400+
refetchTriggers: RefetchTriggers = .default,
401+
refetchOnAppear: RefetchOnAppear = .ifStale,
402+
structuralSharing: Bool = true,
403+
meta: QueryMeta? = nil,
404+
enabled: Bool = true,
405+
queryClient: QueryClient? = nil,
406+
@ViewBuilder content: @escaping (UseInfiniteQueryResult<TData, TPageParam>) -> Content
407+
) where TKey == KeyTuple3<K1, K2, K3> {
408+
let options = InfiniteQueryOptions<TData, QueryError, KeyTuple3<K1, K2, K3>, TPageParam>(
409+
queryKey: queryKey,
410+
queryFn: queryFn,
411+
getNextPageParam: getNextPageParam,
412+
getPreviousPageParam: getPreviousPageParam,
413+
initialPageParam: initialPageParam,
414+
maxPages: maxPages,
415+
retryConfig: retryConfig,
416+
networkMode: networkMode,
417+
staleTime: staleTime,
418+
gcTime: gcTime,
419+
refetchTriggers: refetchTriggers,
420+
refetchOnAppear: refetchOnAppear,
421+
structuralSharing: structuralSharing,
422+
meta: meta,
423+
enabled: enabled
424+
)
425+
426+
self.init(
427+
options: options,
428+
queryClient: queryClient,
429+
content: content
430+
)
431+
}
432+
433+
/// Create UseInfiniteQuery with KeyTuple4-based query key
434+
/// - Parameters:
435+
/// - queryKey: KeyTuple4 identifier for the query
436+
/// - queryFn: Function that fetches page data
437+
/// - getNextPageParam: Function to get next page parameter from pages
438+
/// - getPreviousPageParam: Function to determine the previous page parameter
439+
/// - initialPageParam: Initial page parameter for the first page
440+
/// - maxPages: Maximum number of pages to retain
441+
/// - retryConfig: Configuration for retry behavior (default: RetryConfig())
442+
/// - networkMode: Network behavior configuration (default: .online)
443+
/// - staleTime: Time before data is considered stale (default: 0)
444+
/// - gcTime: Time before unused data is garbage collected (default: 5 minutes)
445+
/// - refetchTriggers: Configuration for automatic refetching triggers (default: .default)
446+
/// - refetchOnAppear: When to refetch data on view appear (default: .ifStale)
447+
/// - structuralSharing: Whether to use structural sharing for performance (default: true)
448+
/// - meta: Arbitrary metadata for this query
449+
/// - enabled: Whether the query should execute automatically (default: true)
450+
/// - queryClient: Optional query client (uses shared instance if nil)
451+
/// - content: View builder that receives the query result
452+
public init<K1: QueryKeyCodable, K2: QueryKeyCodable, K3: QueryKeyCodable, K4: QueryKeyCodable>(
453+
queryKey: KeyTuple4<K1, K2, K3, K4>,
454+
queryFn: @escaping @Sendable (KeyTuple4<K1, K2, K3, K4>, TPageParam?) async throws -> TData,
329455
getNextPageParam: @escaping GetNextPageParamFunction<TData, TPageParam>,
330456
getPreviousPageParam: GetPreviousPageParamFunction<TData, TPageParam>? = nil,
331457
initialPageParam: TPageParam? = nil,
@@ -341,8 +467,8 @@ extension UseInfiniteQuery {
341467
enabled: Bool = true,
342468
queryClient: QueryClient? = nil,
343469
@ViewBuilder content: @escaping (UseInfiniteQueryResult<TData, TPageParam>) -> Content
344-
) where TKey == String {
345-
let options = InfiniteQueryOptions<TData, QueryError, String, TPageParam>(
470+
) where TKey == KeyTuple4<K1, K2, K3, K4> {
471+
let options = InfiniteQueryOptions<TData, QueryError, KeyTuple4<K1, K2, K3, K4>, TPageParam>(
346472
queryKey: queryKey,
347473
queryFn: queryFn,
348474
getNextPageParam: getNextPageParam,

Tests/SwiftUIQueryTests/KeyTupleTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,32 @@ struct KeyTupleTests {
168168
#expect(key7 == key8)
169169
#expect(key7 != key9)
170170
}
171+
172+
// MARK: - InfiniteQueryOptions KeyTuple Tests
173+
174+
@Test("InfiniteQueryOptions with KeyTuple2")
175+
func infiniteQueryOptionsKeyTuple2() async {
176+
struct Post: Sendable, Codable {
177+
let id: Int
178+
let title: String
179+
}
180+
181+
let key = KeyTuple2("posts", 10)
182+
183+
let options = InfiniteQueryOptions<[Post], QueryError, KeyTuple2<String, Int>, Int>(
184+
queryKey: key,
185+
queryFn: { (_: KeyTuple2<String, Int>, pageParam: Int?) async throws -> [Post] in
186+
let page = pageParam ?? 0
187+
return [Post(id: page, title: "Post \(page)")]
188+
},
189+
getNextPageParam: { pages in
190+
return pages.count < 3 ? pages.count : nil
191+
},
192+
initialPageParam: 0
193+
)
194+
195+
#expect(options.queryKey == key)
196+
#expect(options.queryKey.key1 == "posts")
197+
#expect(options.queryKey.key2 == 10)
198+
}
171199
}

Tests/SwiftUIQueryTests/UseInfiniteQueryTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ struct InfiniteQueryTests {
697697
// After refetch, new data should be present
698698
#expect(refetchedData.pages.count == 1)
699699
#expect(infiniteQuery.state.data?.pages.count == 1)
700-
#expect(await fetchCounter.count == 2) // Verify fetch was called twice
700+
#expect(fetchCounter.count == 2) // Verify fetch was called twice
701701
}
702702
}
703703

0 commit comments

Comments
 (0)