Skill v1.0.1
currentAutomated scan100/1002 files
version: "1.0.1" name: pagination description: Generates pagination infrastructure with offset or cursor-based patterns, infinite scroll, and search support. Use when user wants to add paginated lists, infinite scrolling, or load-more functionality. allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion]
Pagination Generator
Generate production pagination infrastructure supporting offset-based and cursor-based APIs, with infinite scroll SwiftUI views, state machine management, and optional search integration.
When This Skill Activates
Use this skill when the user:
- Asks to "add pagination" or "paginate a list"
- Wants "infinite scroll" or "load more" functionality
- Mentions "cursor-based pagination" or "offset pagination"
- Asks about "paginated API" or "loading pages of data"
- Wants "search with pagination"
Pre-Generation Checks
1. Project Context Detection
- [ ] Check Swift version (requires Swift 5.9+)
- [ ] Check deployment target (iOS 17+ / macOS 14+ for @Observable)
- [ ] Search for existing pagination implementations
- [ ] Identify source file locations
2. Networking Layer Detection
Search for existing networking code:
Glob: **/*API*.swift, **/*Client*.swift, **/*Endpoint*.swiftGrep: "APIClient" or "APIEndpoint"
If networking-layer generator was used, detect the APIEndpoint protocol and generate data sources that conform to it.
3. Conflict Detection
Search for existing pagination:
Glob: **/*Pagina*.swift, **/*LoadMore*.swiftGrep: "PaginationState" or "loadNextPage" or "hasMorePages"
If found, ask user whether to replace or extend.
Configuration Questions
Ask user via AskUserQuestion:
- Pagination style?
- Offset-based (page number + page size) — most common for REST APIs
- Cursor-based (opaque cursor token) — better for real-time data, social feeds
- Loading trigger?
- Infinite scroll (auto-load when near bottom) — recommended
- Manual "Load More" button
- Both (infinite scroll with manual fallback on error)
- Additional features? (multi-select)
- Search with pagination (debounced, resets on query change)
- Pull-to-refresh
- Empty/error/loading state views
- Data source pattern?
- Generic (works with any Codable model)
- Protocol-based (define per-endpoint data sources)
Generation Process
Step 1: Read Templates
Read pagination-patterns.md for architecture guidance. Read templates.md for production Swift code.
Step 2: Create Core Files
Generate these files:
PaginatedResponse.swift— Generic response models for offset and cursorPaginationState.swift— State machine (idle, loading, loaded, error, exhausted)PaginatedDataSource.swift— Protocol endpoints conform toPaginationManager.swift— @Observable manager with state transitions
Step 3: Create Optional Files
Based on configuration:
SearchablePaginationManager.swift— If search selectedViews/PaginatedList.swift— Infinite scroll SwiftUI wrapperViews/LoadMoreButton.swift— Manual load-more buttonViews/PaginationStateView.swift— Empty/loading/error state views
Step 4: Determine File Location
Check project structure:
- If
Sources/exists →Sources/Pagination/ - If
App/exists →App/Pagination/ - Otherwise →
Pagination/
Output Format
After generation, provide:
Files Created
Pagination/├── PaginatedResponse.swift # Generic response models├── PaginationState.swift # State machine enum├── PaginatedDataSource.swift # Data source protocol├── PaginationManager.swift # @Observable manager├── SearchablePaginationManager.swift # Optional: search + pagination└── Views/├── PaginatedList.swift # Infinite scroll wrapper├── LoadMoreButton.swift # Manual load-more└── PaginationStateView.swift # Empty/loading/error states
Integration Steps
Define a data source:
struct UsersDataSource: PaginatedDataSource {typealias Item = Userlet apiClient: APIClientfunc fetch(page: PageRequest) async throws -> PaginatedResponse<User> {try await apiClient.request(UsersEndpoint(page: page.page, size: page.size))}}
Use PaginationManager in a view model:
@Observablefinal class UsersViewModel {let pagination: PaginationManager<UsersDataSource>init(apiClient: APIClient) {pagination = PaginationManager(dataSource: UsersDataSource(apiClient: apiClient))}}
With SwiftUI (infinite scroll):
struct UsersListView: View {@State private var viewModel = UsersViewModel()var body: some View {PaginatedList(manager: viewModel.pagination) { user inUserRow(user: user)}.task {await viewModel.pagination.loadFirstPage()}}}
With search:
struct SearchableUsersView: View {@State private var searchManager = SearchablePaginationManager(dataSource: UsersDataSource())var body: some View {PaginatedList(manager: searchManager.pagination) { user inUserRow(user: user)}.searchable(text: $searchManager.query)}}
Testing
@Testfunc loadFirstPagePopulatesItems() async throws {let mockSource = MockDataSource(items: User.mockList(count: 20))let manager = PaginationManager(dataSource: mockSource, pageSize: 10)await manager.loadFirstPage()#expect(manager.items.count == 10)#expect(manager.state == .loaded)#expect(manager.hasMore == true)}@Testfunc loadAllPagesReachesExhausted() async throws {let mockSource = MockDataSource(items: User.mockList(count: 15))let manager = PaginationManager(dataSource: mockSource, pageSize: 10)await manager.loadFirstPage()await manager.loadNextPage()#expect(manager.items.count == 15)#expect(manager.state == .exhausted)}
References
- pagination-patterns.md — Offset vs cursor comparison, state machine, threshold prefetching
- templates.md — All production Swift templates
- Related:
generators/networking-layer— Base networking layer for data sources - Related:
generators/http-cache— Cache paginated responses