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.

149 stars

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

$curl -o ~/.claude/skills/offline-queue/SKILL.md --create-dirs "https://raw.githubusercontent.com/rshankras/claude-code-apple-skills/main/skills/generators/offline-queue/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/offline-queue/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How offline-queue Compares

Feature / Agentoffline-queueStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/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 reading

Related Skills

watchOS

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

Pre-refactor safety checklist. Verifies test coverage exists before AI modifies existing code. Use before asking AI to refactor anything.

tdd-feature

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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

149
from rshankras/claude-code-apple-skills

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.