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.
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.
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.
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.
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
Disable connectivity and submit one queued comment: expect
falseand one pending entry.Reconnect, flush, and confirm one server comment and zero pending entries for this test.
Switch accounts before flushing and confirm the old entry is not sent as the new user.
Test cache reads before and after TTL expiry, and verify logout removes any data your app promises to forget.