offline-queue
Generates an offline operation queue with persistence, automatic retry on connectivity, and conflict resolution. Use when user needs offline-first behavior, queued mutations, or pending operations that sync when back online.
Best use case
offline-queue is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Generates an offline operation queue with persistence, automatic retry on connectivity, and conflict resolution. Use when user needs offline-first behavior, queued mutations, or pending operations that sync when back online.
Teams using offline-queue should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/offline-queue/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How offline-queue Compares
| Feature / Agent | offline-queue | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Generates an offline operation queue with persistence, automatic retry on connectivity, and conflict resolution. Use when user needs offline-first behavior, queued mutations, or pending operations that sync when back online.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Offline Queue Generator
Generate a production offline operation queue that persists API requests/mutations when offline, stores them to disk, and retries with exponential backoff when connectivity returns. Essential for apps that need offline-first behavior.
## When This Skill Activates
Use this skill when the user:
- Asks to "add offline queue" or "offline support"
- Wants to "queue requests" when there is no network
- Mentions "offline first" architecture or design
- Asks about "retry when online" or "retry on reconnect"
- Wants "pending operations" that sync later
- Mentions "offline mutations" or "queue API calls"
## Pre-Generation Checks
### 1. Project Context Detection
- [ ] Check Swift version (requires Swift 5.9+)
- [ ] Check deployment target (iOS 16+ / macOS 13+)
- [ ] Check for @Observable support (iOS 17+ / macOS 14+)
- [ ] Identify source file locations
### 2. Conflict Detection
Search for existing networking/offline code:
```
Glob: **/*OfflineQueue*.swift, **/*OfflineOperation*.swift, **/*NetworkMonitor*.swift, **/*RetryPolicy*.swift
Grep: "NWPathMonitor" or "OfflineQueue" or "pendingOperations" or "offlineQueue"
```
If existing offline handling found:
- Ask if user wants to replace or extend it
- If extending, adapt generated code to existing patterns
### 3. Framework Availability
Check for Network framework availability (required for NWPathMonitor). Available on iOS 12+ / macOS 10.14+, so effectively always available for our iOS 16+ / macOS 13+ targets.
## Configuration Questions
Ask user via AskUserQuestion:
1. **Operation types?**
- API calls only (JSON requests/responses)
- File uploads only (multipart data)
- Both API calls and file uploads
2. **Persistence strategy?**
- SwiftData (iOS 17+ / macOS 14+) — structured queries, migration support
- File-based (JSON files in app support) — simpler, wider compatibility — recommended
3. **Retry strategy?**
- Exponential backoff with jitter — recommended (prevents thundering herd)
- Linear backoff (fixed interval between retries)
- Immediate (retry as soon as connectivity returns, no delay)
4. **Conflict resolution?**
- Server wins (discard client changes on conflict)
- Client wins (overwrite server data on conflict)
- Manual merge (surface conflicts to the user for resolution)
## Generation Process
### Step 1: Read Templates
Read `patterns.md` for architecture guidance and conflict resolution strategies.
Read `templates.md` for production Swift code.
### Step 2: Create Core Files
Generate these files:
1. `OfflineOperation.swift` — Codable model for queued operations
2. `OfflineQueueManager.swift` — Actor managing enqueue, dequeue, process, retry
3. `QueuePersistence.swift` — Protocol + file-based implementation for saving operations
4. `NetworkMonitor.swift` — @Observable wrapper around NWPathMonitor
### Step 3: Create Policy Files
5. `RetryPolicy.swift` — Configurable backoff strategy with jitter
### Step 4: Create UI Files
6. `OfflineQueueDashboardView.swift` — Debug view showing queue state and manual controls
7. `OfflineQueueModifier.swift` — ViewModifier showing "Offline" banner when disconnected
### Step 5: Determine File Location
Check project structure:
- If `Sources/` exists → `Sources/OfflineQueue/`
- If `App/` exists → `App/OfflineQueue/`
- Otherwise → `OfflineQueue/`
## Output Format
After generation, provide:
### Files Created
```
OfflineQueue/
├── OfflineOperation.swift # Codable operation model
├── OfflineQueueManager.swift # Actor-based queue manager
├── QueuePersistence.swift # Protocol + file-based persistence
├── NetworkMonitor.swift # NWPathMonitor wrapper
├── RetryPolicy.swift # Exponential backoff with jitter
├── OfflineQueueDashboardView.swift # Debug dashboard view
└── OfflineQueueModifier.swift # Offline banner modifier
```
### Integration with Networking Layer
**Enqueue an operation when offline:**
```swift
// In your networking layer or repository
func createPost(_ post: Post) async throws {
guard networkMonitor.isConnected else {
let operation = OfflineOperation(
endpoint: "/api/posts",
httpMethod: .post,
body: try JSONEncoder().encode(post),
headers: ["Content-Type": "application/json"]
)
await queueManager.enqueue(operation)
return
}
// Normal online request
try await apiClient.post("/api/posts", body: post)
}
```
**Transparent offline support with a wrapper:**
```swift
func performOrQueue<T: Codable>(
endpoint: String,
method: HTTPMethod,
body: T
) async throws {
let data = try JSONEncoder().encode(body)
if networkMonitor.isConnected {
try await apiClient.request(endpoint: endpoint, method: method, body: data)
} else {
let operation = OfflineOperation(
endpoint: endpoint,
httpMethod: method,
body: data
)
await queueManager.enqueue(operation)
}
}
```
**Show offline banner in your app:**
```swift
struct ContentView: View {
var body: some View {
NavigationStack {
FeedView()
}
.offlineQueueBanner() // Shows "Offline — changes will sync" when disconnected
}
}
```
**Add dashboard for debugging:**
```swift
#if DEBUG
NavigationLink("Offline Queue") {
OfflineQueueDashboardView()
}
#endif
```
### Testing
```swift
@Test
func operationEnqueuedWhenOffline() async throws {
let persistence = MockQueuePersistence()
let monitor = MockNetworkMonitor(isConnected: false)
let manager = OfflineQueueManager(persistence: persistence, monitor: monitor)
let operation = OfflineOperation(
endpoint: "/api/posts",
httpMethod: .post,
body: Data("{\"title\":\"Hello\"}".utf8)
)
await manager.enqueue(operation)
let pending = await persistence.loadAll()
#expect(pending.count == 1)
#expect(pending.first?.endpoint == "/api/posts")
}
@Test
func operationsProcessedOnReconnect() async throws {
let persistence = MockQueuePersistence()
let monitor = MockNetworkMonitor(isConnected: false)
let executor = MockOperationExecutor()
let manager = OfflineQueueManager(
persistence: persistence,
monitor: monitor,
executor: executor
)
let operation = OfflineOperation(
endpoint: "/api/posts",
httpMethod: .post,
body: Data("{\"title\":\"Hello\"}".utf8)
)
await manager.enqueue(operation)
// Simulate coming back online
monitor.simulateConnectivityChange(isConnected: true)
// Wait for processing
try await Task.sleep(for: .milliseconds(200))
#expect(executor.executedOperations.count == 1)
let remaining = await persistence.loadAll()
#expect(remaining.isEmpty)
}
@Test
func exponentialBackoffCalculation() {
let policy = RetryPolicy(
maxRetries: 5,
baseDelay: 1.0,
maxDelay: 60.0,
multiplier: 2.0
)
#expect(policy.delay(forAttempt: 0) >= 1.0)
#expect(policy.delay(forAttempt: 1) >= 2.0)
#expect(policy.delay(forAttempt: 2) >= 4.0)
#expect(policy.delay(forAttempt: 5) <= 60.0) // Capped at maxDelay
}
```
## Common Patterns
### Enqueue Operation
```swift
let operation = OfflineOperation(
endpoint: "/api/comments",
httpMethod: .post,
body: try JSONEncoder().encode(comment),
headers: ["Authorization": "Bearer \(token)"]
)
await queueManager.enqueue(operation)
```
### Process Queue on Connectivity
```swift
// Automatic — OfflineQueueManager observes NetworkMonitor
// and calls processQueue() when connectivity returns.
// No manual intervention needed.
```
### Handle Conflict Resolution
```swift
// Server returns 409 Conflict
switch conflictStrategy {
case .serverWins:
// Discard local operation, fetch server state
await queueManager.markCompleted(operation)
case .clientWins:
// Retry with force flag
operation.headers["X-Force-Overwrite"] = "true"
await queueManager.retry(operation)
case .manualMerge:
// Surface to user
await queueManager.markConflict(operation, serverData: responseData)
}
```
## Gotchas
### Operation Ordering and Dependencies
Operations may have dependencies (e.g., "create parent" must succeed before "create child"). The queue processes in FIFO order by default. For explicit dependencies, use the `dependsOn` field to chain operations, and the queue manager will skip dependent operations until their prerequisites complete.
### Idempotency Keys
Every queued operation gets a UUID-based idempotency key. The server must check this key to avoid duplicate processing if the client retries an operation that actually succeeded but the response was lost. Without idempotency keys, a retry could create duplicate records.
### Stale Data After Long Offline Periods
If the device is offline for hours or days, queued operations may reference data that has changed server-side. Consider adding a TTL to operations (e.g., 24 hours) and discarding expired operations with a user notification rather than blindly replaying stale mutations.
### Background URLSession for Large Uploads
For file uploads, use a background `URLSession` configuration so uploads continue even when the app is suspended. The standard queue manager handles JSON API calls; for large uploads, delegate to a background transfer service.
### Queue Size Limits
Set a maximum queue size (e.g., 500 operations or 50 MB) to prevent unbounded growth. When the limit is reached, notify the user that offline storage is full and suggest connecting to sync pending changes.
## References
- **templates.md** — All production Swift templates
- **patterns.md** — Offline-first architecture, conflict resolution, idempotency
- Related: `generators/networking-layer` — Base networking layer to wrap with offline support
- Related: `generators/http-cache` — Cache GET responses for offline readingRelated Skills
watchOS
watchOS development guidance including SwiftUI for Watch, Watch Connectivity, complications, and watch-specific UI patterns. Use for watchOS code review, best practices, or Watch app development.
visionos-widgets
visionOS widget patterns including mounting styles, glass/paper textures, proximity-aware layouts, and spatial widget families. Use when creating or adapting widgets for visionOS.
test-data-factory
Generate test fixture factories for your models. Builder pattern and static factories for zero-boilerplate test data. Use when tests need sample data setup.
test-contract
Generate protocol/interface test suites that any implementation must pass. Define the contract once, test every implementation. Use when designing protocols or swapping implementations.
tdd-refactor-guard
Pre-refactor safety checklist. Verifies test coverage exists before AI modifies existing code. Use before asking AI to refactor anything.
tdd-feature
Red-green-refactor scaffold for building new features with TDD. Write failing tests first, then implement to pass. Use when building new features test-first.
tdd-bug-fix
Fix bugs using red-green-refactor — reproduce the bug as a failing test first, then fix it. Use when fixing bugs to ensure they never regress.
snapshot-test-setup
Set up SwiftUI visual regression testing with swift-snapshot-testing. Generates snapshot test boilerplate and CI configuration. Use for UI regression prevention.
integration-test-scaffold
Generate cross-module test harness with mock servers, in-memory stores, and test configuration. Use when testing networking + persistence + business logic together.
characterization-test-generator
Generates tests that capture current behavior of existing code before refactoring. Use when you need a safety net before AI-assisted refactoring or modifying legacy code.
testing
TDD and testing skills for iOS/macOS apps. Covers characterization tests, TDD workflows, test contracts, snapshot tests, and test infrastructure. Use for test-driven development, adding tests to existing code, or building test infrastructure.
webkit-integration
WebKit integration in SwiftUI using WebView and WebPage for embedding web content, navigation, JavaScript interop, and customization. Use when embedding web content in SwiftUI apps.