Skip to content
AppNorth
Log in

iOS SDK

Offline writes and cache

The client does not silently persist failed requests. Add AppNorthOfflineQueue for selected writes and AppNorthReadCache for boards and idea lists. You provide storage and decide its retention and protection.

View Markdown

Provide storage

Both storage protocols expose load() async throws -> Data? and save(_:) async throws. The following actor is a minimal memory-only implementation. Use separate instances for the queue and cache; their serialized formats are different.

MemorySDKStorage.swift
import Foundation
import AppNorth

actor MemorySDKStorage: AppNorthOfflineQueueStorage, AppNorthReadCacheStorage {
    private var data: Data?

    func load() async throws -> Data? { data }
    func save(_ data: Data?) async throws { self.data = data }
}

For persistence across launches, implement these protocols with your app's protected storage. Queue contents can include report text, diagnostics, screenshots, and an identity scope. Do not store the signing secret or credential alongside them.

Queue a write

Retain a queue for the account session. submitIdea, submitComment, and submitFeedback attempt the request immediately. They return true when submitted or false when a network failure was queued.

QueuedComment.swift
import AppNorth

func sendComment(
    queue: AppNorthOfflineQueue,
    client: AppNorthClient,
    ideaID: String,
    body: String,
    retryKey: String
) async throws -> Bool {
    try await queue.submitComment(
        using: client,
        handle: ideaID,
        body: body,
        idempotencyKey: retryKey
    )
}

func retryPending(queue: AppNorthOfflineQueue, client: AppNorthClient) async throws -> Int {
    try await queue.flush(using: client)
    return try await queue.pendingCount()
}

Only AppNorthError.network queues a write. Validation, authentication, and HTTP server errors propagate. The default queue limit is 50 entries; a full queue throws AppNorthOfflineQueueError.full. Votes and chat messages are not supported queue operations.

Call flush(using:) after reconnecting or returning to the foreground. The queue does not monitor connectivity itself. A network failure stops the flush and retains the remaining entries; another error throws and leaves the failing entry pending. A completed flush call does not prove the queue is empty: inspect pendingCount(), which counts all stored scopes.

Cache community reads

The cache defaults to a 300-second TTL. Pass it to AppNorthView to show fresh cached boards and ideas before the initial network load. It caches arrays, not pagination cursors, chat messages, or private report threads.

CachedCommunity.swift
import SwiftUI
import AppNorth

struct CachedCommunity: View {
    let client: AppNorthClient
    let cache: AppNorthReadCache

    var body: some View {
        AppNorthView(client: client, cache: cache)
    }
}

Construct the cache once with AppNorthReadCache(storage: MemorySDKStorage()), using the storage example above. Expired entries return nil; there is no stale-forever fallback. For your own reads, use storeBoards, storeIdeas, boards(scope:), and ideas(scope:query:). Include server, project, account, and all relevant filters in your cache keys. clear(scope:) removes that scope's boards and ideas.

Separate accounts

Queue replay matches the API URL, App ID, and lowercased credential email (or anonymous). This prevents replaying an old account's queued write with a different account. It does not erase old entries automatically.

On logout, stop submitting/flushing, discard the queue and view models, and erase account-owned storage if your retention policy requires it. Create new instances after changing identity. Clearing storage alone does not clear an already-loaded queue's in-memory entries. The built-in community model can retain its loaded arrays; recreate its screen on account changes as well.

Verify

  1. Disable connectivity and submit one queued comment: expect false and one pending entry.

  2. Reconnect, flush, and confirm one server comment and zero pending entries for this test.

  3. Switch accounts before flushing and confirm the old entry is not sent as the new user.

  4. Test cache reads before and after TTL expiry, and verify logout removes any data your app promises to forget.