Skip to content
AppNorth
Log in

Reference

Swift API

Use this reference to look up a signature, then follow the linked task guide for integration. It covers the public SDK declarations, protocol requirements, enum cases, and public decoding/raw-value initializers. Each entry has an explicit anchor. Swift-generated protocol witnesses such as equality and hashing use their standard Swift behavior.

View Markdown

Choose an API

Response structs without an explicit public value initializer are read from the client or a Decoder; do not rely on an internal memberwise initializer. Observable models and the paywall controller run on the main actor. Published properties documented with { get } are externally read-only; their $property projections expose observation, not permission to mutate.

ReadCommunity.swift
import AppNorth

func firstCommunityPage(client: AppNorthClient) async throws -> AppNorthPage<AppNorthIdea> {
    let boards = try await client.boards()
    return try await client.ideas(board: boards.first?.slug, sort: .top)
}

AppNorthTokenProviding

Supplies a stable anonymous token. A custom provider must be safe for concurrent access and return a token accepted by the server.

protocol AppNorthTokenProviding : Sendable

token()

func token() -> String

AppNorthAnonymousToken

Thread-safe anonymous voter token backed by UserDefaults. The default key is appnorth.voter; a valid legacy token is migrated. Removing stored data or using another installation can create a different token. This is not verified identity.

final class AppNorthAnonymousToken

init(defaults:key:)

init(defaults: UserDefaults = .standard, key: String = "appnorth.voter")

token()

func token() -> String

AppNorthViewModel

Main-actor observable community state. Optional read caching applies to initial load; recreate the model after account changes to discard previously loaded arrays.

@MainActor final class AppNorthViewModel

boards

@Published @MainActor var boards: [AppNorthBoard] { get }

ideas

@Published @MainActor var ideas: [AppNorthIdea] { get }

isLoading

@Published @MainActor var isLoading: Bool { get }

error

@Published @MainActor var error: AppNorthError? { get }

showWatermark

@Published @MainActor var showWatermark: Bool { get }

selectedBoard

@Published @MainActor var selectedBoard: String? { get set }

sort

@Published @MainActor var sort: AppNorthIdeaSort { get set }

client

@MainActor let client: AppNorthClient

init(client:cache:)

@MainActor init(client: AppNorthClient, cache: AppNorthReadCache? = nil)

load()

@MainActor func load() async

Loads fresh cached values if available, then boards and the first idea page; loads branding independently.

reloadIdeas()

@MainActor func reloadIdeas() async

Reloads the first idea page for the selected board and sort and stores successful results in the optional cache.

AppNorthView

Complete native community screen with its own navigation stack, board/sort controls, idea details, creation, and optional floating chat entry. Loads the first idea page.

@MainActor struct AppNorthView

init(client:copy:theme:uiConfiguration:cache:paywalls:)

@MainActor init(client: AppNorthClient, copy: AppNorthCopy = .init(), theme: AppNorthTheme = .init(), uiConfiguration: AppNorthUIConfiguration = .init(), cache: AppNorthReadCache? = nil, paywalls: AppNorthPaywalls? = nil)

body

@MainActor var body: some View { get }

The content and behavior of the view. When you implement a custom view, you must implement a computed body property to provide the content for your view. Return a view that's composed of built-in views that SwiftUI provides, plus other composite views that you've already defined: struct MyView: View { var body: some View { Text("Hello, World!") } } For more information about composing views and a view hierarchy, see doc:Declaring-a-Custom-View.

AppNorthScreenshotCapturing

Host-supplied capture adapter. Return a supported base64 image data URL; the SDK provides no automatic screen capture.

protocol AppNorthScreenshotCapturing : Sendable

capture()

func capture() async throws -> String

AppNorthScreenshotRedacting

Host-supplied redaction adapter. Return a changed, valid image data URL; unchanged output is rejected by the capture helper.

protocol AppNorthScreenshotRedacting : Sendable

redact(_:)

func redact(_ screenshot: String) async throws -> String

AppNorthChatEntry

A displayed chat row, including local delivery state. Obtain entries from AppNorthChatModel; there is no public memberwise initializer.

struct AppNorthChatEntry

id

var id: String

The stable identity of the entity associated with this instance.

sender

let sender: AppNorthChatMessage.Sender

body

let body: String

SwiftUI view content. Present the view itself rather than invoking this property directly.

actionCard

var actionCard: AppNorthActionCard?

openedAt

var openedAt: String?

tappedAt

var tappedAt: String?

First review-card tap recorded by the server; this does not confirm a submitted review.

state

var state: AppNorthChatEntry.State

images

var images: [AppNorthChatImage]

Photos shown with the optimistic or delivered bubble. Failed sends retain their photos for retry.

AppNorthChatEntry.State

Local delivery state: sending, sent, or failed. Failed entries can be retried with their original key.

enum State

sent

case sent

sending

case sending

failed

case failed

AppNorthChatModel

Main-actor conversation model with optimistic entries and retry handling. A custom owner must stop polling while hidden or inactive and discard the model on account changes. See support chat.

@MainActor final class AppNorthChatModel

maximumLength

@MainActor static let maximumLength: Int

entries

@Published @MainActor var entries: [AppNorthChatEntry] { get }

isAvailable

@Published @MainActor var isAvailable: Bool { get }

hasLoaded

@Published @MainActor var hasLoaded: Bool { get }

lastLoadFailed

@Published @MainActor var lastLoadFailed: Bool { get }

init(client:paywalls:)

@MainActor init(client: AppNorthClient, paywalls: AppNorthPaywalls? = nil)

startPolling()

@MainActor func startPolling()

Starts a refresh loop at five-second intervals; cancels any earlier polling task.

stopPolling()

@MainActor func stopPolling()

Cancels polling. Call when your custom chat screen hides or becomes inactive.

refresh()

@MainActor func refresh() async

Fetches and merges server messages, updating availability and failure state. Discards results for a changed identity scope.

send(_:images:)

@discardableResult @MainActor func send(_ text: String, images: [AppNorthChatImageUpload] = []) -> Bool

Trims text and caps it at 2,000 Swift characters. Returns true when a local entry was queued, not when delivery succeeds.

retry(_:)

@MainActor func retry(_ entry: AppNorthChatEntry)

Retries a failed entry with its original idempotency key.

AppNorthChatView

Native conversation content. Owns a model and visible/foreground polling; your app supplies presentation and dismissal. Accepts copy, theme, and an optional paywall controller.

@MainActor struct AppNorthChatView

init(client:copy:theme:paywalls:)

@MainActor init(client: AppNorthClient, copy: AppNorthCopy = .init(), theme: AppNorthTheme = .init(), paywalls: AppNorthPaywalls? = nil)

body

@MainActor var body: some View { get }

The content and behavior of the view. When you implement a custom view, you must implement a computed body property to provide the content for your view. Return a view that's composed of built-in views that SwiftUI provides, plus other composite views that you've already defined: struct MyView: View { var body: some View { Text("Hello, World!") } } For more information about composing views and a view hierarchy, see doc:Declaring-a-Custom-View.

AppNorthHTTPTransport

Implement this protocol for a custom transport or deterministic tests. URLSession already conforms. Return both response bytes and the HTTP response; transport errors become AppNorthError.network.

protocol AppNorthHTTPTransport : Sendable

data(for:)

func data(for request: URLRequest) async throws -> (Data, URLResponse)

AppNorthClient

A Sendable HTTP client. Copies share the same in-memory identity store. Both initializers support an anonymous token provider, an optional credential, and an injectable clock. See custom UI.

struct AppNorthClient

configuration

let configuration: AppNorthConfiguration

init(configuration:session:tokenProvider:identityCredential:clock:)

init(configuration: AppNorthConfiguration, session: URLSession = .shared, tokenProvider: any AppNorthTokenProviding = AppNorthAnonymousToken(), identityCredential: AppNorthIdentityCredential? = nil, clock: @escaping @Sendable () -> Date = { Date() })

init(configuration:transport:tokenProvider:identityCredential:clock:)

init(configuration: AppNorthConfiguration, transport: any AppNorthHTTPTransport, tokenProvider: any AppNorthTokenProviding = AppNorthAnonymousToken(), identityCredential: AppNorthIdentityCredential? = nil, clock: @escaping @Sendable () -> Date = { Date() })

setIdentityCredential(_:)

func setIdentityCredential(_ credential: AppNorthIdentityCredential)

Replaces the in-memory credential for subsequent requests. Does not recreate screens or clear host storage.

clearIdentityCredential()

func clearIdentityCredential()

Removes the credential only. Dismiss and discard account-specific views and storage separately.

hasValidIdentity(at:)

func hasValidIdentity(at date: Date = Date()) -> Bool

Checks that a credential exists and has not expired at the supplied date; does not verify its signature.

canParticipate(_:at:)

func canParticipate(_ policy: AppNorthParticipation, at date: Date = Date()) -> Bool

Evaluates the policy against local credential expiry. The server makes the authorization decision.

boards()

func boards() async throws -> [AppNorthBoard]

Returns the project’s boards, statuses, and policies.

widgetConfig()

func widgetConfig() async throws -> AppNorthWidgetConfig

Loads public presentation configuration independently of community data.

ideas(board:status:query:sort:cursor:limit:)

func ideas(board: String? = nil, status: String? = nil, query: String? = nil, sort: AppNorthIdeaSort = .top, cursor: String? = nil, limit: Int = 20) async throws -> AppNorthPage<AppNorthIdea>

Returns one page. Limit defaults to 20 and is clamped to 1–50. Preserve filters when reusing the opaque cursor.

idea(_:)

func idea(_ handle: String) async throws -> AppNorthIdea

Loads one idea by ID or slug.

comments(for:cursor:limit:)

func comments(for handle: String, cursor: String? = nil, limit: Int = 20) async throws -> AppNorthPage<AppNorthComment>

Returns a paginated comment list for an idea ID or slug; limit is clamped to 1–50.

createIdea(board:title:description:authorName:idempotencyKey:)

func createIdea(board: String, title: String, description: String = "", authorName: String? = nil, idempotencyKey: String = UUID().uuidString) async throws -> AppNorthCreation<AppNorthIdea>

Creates an idea. Retain the same idempotency key and payload on retries; inspect the moderation flag.

setVote(on:voted:)

func setVote(on handle: String, voted: Bool) async throws -> AppNorthVote

Sets the desired vote state, returning the resulting state, count, and whether it changed.

createComment(on:body:authorName:idempotencyKey:)

func createComment(on handle: String, body: String, authorName: String? = nil, idempotencyKey: String = UUID().uuidString) async throws -> AppNorthCreation<AppNorthComment>

Creates a comment. Preserve key and payload on retries; inspect the moderation flag.

createFeedback(_:idempotencyKey:)

func createFeedback(_ input: AppNorthCreateFeedback, idempotencyKey: String = UUID().uuidString) async throws -> AppNorthFeedbackReceipt

Submits private feedback and returns its receipt. Reuse the same key only for the same user intention.

feedbackThread(_:)

func feedbackThread(_ id: String) async throws -> AppNorthFeedbackThread

Requires a signed credential and server-verified ownership. Returns only the report and public staff replies.

chatStatus()

func chatStatus() async throws -> AppNorthChatStatus

Reads availability and unread state without marking messages read.

chatMessages(after:)

func chatMessages(after: String? = nil) async throws -> AppNorthChatThread

Reads messages and marks team replies read. Pass a server message ID as after for newer messages.

sendChatMessage(_:imageTokens:idempotencyKey:)

func sendChatMessage(_ body: String, imageTokens: [String] = [], idempotencyKey: String = UUID().uuidString) async throws -> AppNorthChatMessage

Sends chat text and up to three uploaded image tokens. Text may be empty when images are attached. Reuse the key and tokens when retrying the same message.

registerPushToken(_:environment:)

func registerPushToken(_ token: Data, environment: AppNorthPushEnvironment) async throws

Registers the raw APNs device token for the current chat identity. Call after APNs supplies a token and again after an identity change. Pass the APNs environment matching your app's signing configuration. Empty tokens or tokens larger than 256 bytes throw AppNorthError.invalidConfiguration before a request is made.

unregisterPushToken(_:environment:)

func unregisterPushToken(_ token: Data, environment: AppNorthPushEnvironment) async throws

Removes the token registration using the current identity and the same environment used to register it. Await this before clearing the signed identity on logout. Token validation matches registration; request failures propagate so your app can retry.

reportPaywalls(_:idempotencyKey:)

func reportPaywalls(_ snapshot: AppNorthPaywallSnapshot, idempotencyKey: String = UUID().uuidString) async throws

Replaces the reporter’s device snapshot. Reuse the same key for a retry.

markActionCardOpened(_:)

func markActionCardOpened(_ messageId: String) async throws

Reports first display for review cards and first tap for offer cards with a deterministic card-open-<messageId> key. Does not report a purchase or a submitted review.

markActionCardTapped(_:)

func markActionCardTapped(_ messageId: String) async throws

Reports a review-card tap using the deterministic card-tap-<messageId> key. It does not invoke StoreKit or confirm that its prompt appeared.

uploadChatImage(_:contentType:)

func uploadChatImage(_ data: Data, contentType: String = "image/jpeg") async throws -> AppNorthChatImageUpload

Uploads up to 5 MB of JPEG, PNG or WebP through a signed storage PUT and completes server-side pixel validation and metadata removal. Custom UIs prepare their own resized pixels; the built-in iOS picker handles this automatically. Pass the returned imageToken in sendChatMessage(_:imageTokens:idempotencyKey:). At most three image tokens may accompany a message; its text may be empty. Keep the same tokens and idempotency key on retry. See Photos in chat.

AppNorthPushEnvironment

enum AppNorthPushEnvironment: String, Codable, Sendable

Choose the APNs environment matching your app's signing configuration; the SDK does not infer it.

sandbox

case sandbox

production

case production

AppNorth

@MainActor enum AppNorth

Optional push facade for one configured chat client at a time. Your app owns notification permission prompts, APNs registration, its notification-center delegate, and navigation. The SDK does not swizzle callbacks. See the push chat example.

configurePush(client:environment:badgeHandler:routeHandler:)

swift
@MainActor static func configurePush(
    client: AppNorthClient,
    environment: AppNorthPushEnvironment,
    badgeHandler: @escaping @MainActor (Int) -> Void,
    routeHandler: @escaping @MainActor (AppNorthLink) -> Void
)

Stores the same client used by chat, the APNs environment, and your callbacks. badgeHandler receives 0 or 1 to indicate unread chat replies, not a message count; combine it with any other badge sources in your app. routeHandler receives accepted chat destinations and must present your chat UI.

registerPushToken(_:)

@MainActor static func registerPushToken(_ token: Data) async throws

Forwards the raw token to the configured client's registerPushToken(_:environment:). Throws AppNorthError.invalidConfiguration if configurePush has not been called. Call again after changing the chat identity.

unregisterPushToken(_:)

@MainActor static func unregisterPushToken(_ token: Data) async throws

Forwards removal to the configured client and environment. Await before logout or disabling push; failures propagate for retry. Throws AppNorthError.invalidConfiguration without a configured client.

handleNotificationTap(_:)

@discardableResult @MainActor static func handleNotificationTap(_ userInfo: [AnyHashable: Any]) -> Bool

Pass the notification response's userInfo. Returns true and invokes routeHandler only when the appnorth payload has type: "chat", an appId equal to the configured client's configuration.appId, and a parseable chat URL using the appnorth scheme. Unconfigured, foreign-app, and malformed payloads return false without routing. It does not open a screen or authenticate the user.

syncUnreadBadge()

@discardableResult @MainActor static func syncUnreadBadge() async throws -> Int

Fetches chatStatus() and returns 1 only when chat is available and has unread replies; otherwise returns 0. Calls badgeHandler only if the identity scope and push configuration are still current. Request failures preserve the existing badge and propagate. Throws AppNorthError.invalidConfiguration without a configured client. Call on foreground and after reading with a custom UI; built-in chat also calls it after a successful read.

resetPush()

@MainActor static func resetPush()

Calls the current badge handler with 0 and releases the client and callbacks. This only resets local state: await unregisterPushToken(_:) before resetting and clearing identity to remove the server registration.

AppNorthColorPair

A light/dark SwiftUI color pair, or one color shared by both appearances.

struct AppNorthColorPair

light

var light: Color

dark

var dark: Color

init(light:dark:)

init(light: Color, dark: Color)

init(_:)

init(_ color: Color)

Uses one color in both appearances.

color(in:)

func color(in appearance: ColorScheme) -> Color

Selects the dark color for dark appearance and the light color otherwise.

AppNorthConfiguration

Validated server URL, public App ID, and allowed write origin. Use HTTPS in production and pass the server base URL without /api/v1. See installation.

struct AppNorthConfiguration

apiURL

let apiURL: URL

appId

let appId: String

origin

let origin: URL

init(apiURL:appId:origin:)

init(apiURL: URL, appId: String, origin: URL) throws

AppNorthError

Typed client failures. Server errors preserve the HTTP status, machine code, and optional field errors; see error handling.

enum AppNorthError

invalidConfiguration(_:)

case invalidConfiguration(String)

invalidIdentityCredential(_:)

case invalidIdentityCredential(String)

invalidResponse

case invalidResponse

network(_:)

case network(String)

server(status:code:fields:)

case server(status: Int, code: String, fields: [String : [String]]?)

decoding(_:)

case decoding(String)

errorDescription

var errorDescription: String? { get }

A localized message describing what error occurred.

AppNorthCopy

Mutable SDK labels. The default follows the host app language; unsupported languages fall back to English. Each string property below overrides the corresponding label. See language.

struct AppNorthCopy

feedback

var feedback: String

Override this label in the copy value passed to your screen.

board

var board: String

Override this label in the copy value passed to your screen.

allBoards

var allBoards: String

Override this label in the copy value passed to your screen.

noDescription

var noDescription: String

Override this label in the copy value passed to your screen.

comments

var comments: String

Override this label in the copy value passed to your screen.

writeComment

var writeComment: String

Override this label in the copy value passed to your screen.

send

var send: String

Override this label in the copy value passed to your screen.

signInToComment

var signInToComment: String

Override this label in the copy value passed to your screen.

commentsDisabled

var commentsDisabled: String

Override this label in the copy value passed to your screen.

newIdea

var newIdea: String

Override this label in the copy value passed to your screen.

title

var title: String

Override this label in the copy value passed to your screen.

description

var description: String

Override this label in the copy value passed to your screen.

yourName

var yourName: String

Override this label in the copy value passed to your screen.

cancel

var cancel: String

Override this label in the copy value passed to your screen.

signInToSubmit

var signInToSubmit: String

Override this label in the copy value passed to your screen.

submissionDisabled

var submissionDisabled: String

Override this label in the copy value passed to your screen.

anonymous

var anonymous: String

Override this label in the copy value passed to your screen.

team

var team: String

Override this label in the copy value passed to your screen.

vote

var vote: String

Override this label in the copy value passed to your screen.

removeVote

var removeVote: String

Override this label in the copy value passed to your screen.

error

var error: String

Override this label in the copy value passed to your screen.

open

var open: String

Override this label in the copy value passed to your screen.

closed

var closed: String

Override this label in the copy value passed to your screen.

pending

var pending: String

Override this label in the copy value passed to your screen.

poweredBy

var poweredBy: String

Override this label in the copy value passed to your screen.

somethingWentWrong

var somethingWentWrong: String

Override this label in the copy value passed to your screen.

seeTranslation

var seeTranslation: String

Override this label in the copy value passed to your screen.

seeOriginal

var seeOriginal: String

Override this label in the copy value passed to your screen.

noIdeas

var noIdeas: String

Override this label in the copy value passed to your screen.

filter

var filter: String

Override this label in the copy value passed to your screen.

activateToSwitchFilter

var activateToSwitchFilter: String

Override this label in the copy value passed to your screen.

charactersRemaining

var charactersRemaining: String

Override this label in the copy value passed to your screen.

chat

var chat: String

Override this label in the copy value passed to your screen.

writeMessage

var writeMessage: String

Override this label in the copy value passed to your screen.

chatEmpty

var chatEmpty: String

Override this label in the copy value passed to your screen.

sendFailed

var sendFailed: String

Override this label in the copy value passed to your screen.

done

var done: String

Override this label in the copy value passed to your screen.

init()

init()

Texts in the language the host app is currently displayed in.

init(language:)

init(language: String)

Texts in a specific language, e.g. "de" or "pt-BR". Unsupported languages fall back to English.

spanish

static var spanish: AppNorthCopy { get }

Photo control copy

var attachPhotos: String — photo picker button.

var removePhoto: String — remove a selected photo.

var attachedPhoto: String — accessible image description.

var photoLimits: String — count and size guidance.

All four properties have bundled translations and can be overridden.

AppNorthCreateIdeaView

Native creation form with board selection and policy-aware submission. The completion callback lets your host reload after creation.

@MainActor struct AppNorthCreateIdeaView

init(client:boards:copy:theme:onCreated:)

@MainActor init(client: AppNorthClient, boards: [AppNorthBoard], copy: AppNorthCopy = .init(), theme: AppNorthTheme = .init(), onCreated: @escaping () -> Void = {})

body

@MainActor var body: some View { get }

The content and behavior of the view. When you implement a custom view, you must implement a computed body property to provide the content for your view. Return a view that's composed of built-in views that SwiftUI provides, plus other composite views that you've already defined: struct MyView: View { var body: some View { Text("Hello, World!") } } For more information about composing views and a view hierarchy, see doc:Declaring-a-Custom-View.

AppNorthLinkKind

Supported custom-link destinations: idea, private feedback, or the current chat.

enum AppNorthLinkKind

idea

case idea

feedback

case feedback

chat

case chat

The current identity's chat; the parsed link's value is "current".

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

Parsed destination kind and decoded identifier. The link carries no authorization.

struct AppNorthLink

let kind: AppNorthLinkKind

let value: String

Custom-scheme URL builders and parser. Parsing does not validate the application scheme; the host must check it. See deep links.

enum AppNorthDeepLinks

static func idea(scheme: String, handle: String) throws -> URL

Creates a custom-scheme idea URL and percent-encodes its handle as a single path component.

static func feedback(scheme: String, id: String) throws -> URL

Creates a custom-scheme private-report URL; it conveys no identity or access rights.

static func chat(scheme: String) throws -> URL

Creates <scheme>://appnorth/chat/current. Invalid schemes throw AppNorthError.invalidConfiguration. The destination contains no user or conversation ID; your app presents chat with the intended client's current identity.

static func parse(_ url: URL) -> AppNorthLink?

Parses the host/path only. Reject unrecognized schemes in your app before calling this method.

AppNorthIdeaDetailView

Native idea detail and comments. Pass the actual board comment policy; the initializer defaults to anyone, while the server still enforces its policy.

@MainActor struct AppNorthIdeaDetailView

init(client:idea:copy:theme:uiConfiguration:commentPolicy:)

@MainActor init(client: AppNorthClient, idea: AppNorthIdea, copy: AppNorthCopy = .init(), theme: AppNorthTheme = .init(), uiConfiguration: AppNorthUIConfiguration = .init(), commentPolicy: AppNorthParticipation = .anyone)

body

@MainActor var body: some View { get }

The content and behavior of the view. When you implement a custom view, you must implement a computed body property to provide the content for your view. Return a view that's composed of built-in views that SwiftUI provides, plus other composite views that you've already defined: struct MyView: View { var body: some View { Text("Hello, World!") } } For more information about composing views and a view hierarchy, see doc:Declaring-a-Custom-View.

AppNorthInputLimits

Client-side limits for idea titles (100), descriptions (5,000), and comments (2,000). Server validation remains authoritative.

enum AppNorthInputLimits

ideaTitle

static let ideaTitle: Int

ideaDescription

static let ideaDescription: Int

comment

static let comment: Int

AppNorthParticipation

Per-action board policy. Local permission checks guide UI; server enforcement remains authoritative.

enum AppNorthParticipation

anyone

case anyone

identified

case identified

disabled

case disabled

allowsParticipation(hasValidIdentity:)

func allowsParticipation(hasValidIdentity: Bool) -> Bool

Anyone returns true; identified returns the supplied identity flag; disabled returns false.

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthBoardStatus

A board status definition. isFinal drives open/closed filtering; color is a server-provided hexadecimal color.

struct AppNorthBoardStatus

id

let id: String

The stable identity of the entity associated with this instance.

slug

let slug: String

label

let label: String

color

let color: String

position

let position: Int

isDefault

let isDefault: Bool

isFinal

let isFinal: Bool

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthBoardPolicies

Independent policies for creating ideas, voting, and commenting.

struct AppNorthBoardPolicies

submission

let submission: AppNorthParticipation

vote

let vote: AppNorthParticipation

comment

let comment: AppNorthParticipation

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthBoard

A server-provided community board, including independent submission/vote/comment policies and status definitions.

struct AppNorthBoard

id

let id: String

The stable identity of the entity associated with this instance.

slug

let slug: String

name

let name: String

description

let description: String?

position

let position: Int

moderation

let moderation: String

policies

let policies: AppNorthBoardPolicies

statuses

let statuses: [AppNorthBoardStatus]

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthIdeaStatus

The status attached to an idea: slug, display label, and hexadecimal color.

struct AppNorthIdeaStatus

slug

let slug: String

label

let label: String

color

let color: String

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthIdea

A community idea returned by the server. Use its ID or slug for detail and write operations. Counts and vote state are server-derived.

struct AppNorthIdea

id

let id: String

The stable identity of the entity associated with this instance.

slug

let slug: String

boardSlug

let boardSlug: String

title

let title: String

description

let description: String

authorName

let authorName: String?

status

let status: AppNorthIdeaStatus?

voteCount

let voteCount: Int

commentCount

let commentCount: Int

hasVoted

let hasVoted: Bool

pinned

let pinned: Bool

moderation

let moderation: String?

createdAt

let createdAt: String

lastActivityAt

let lastActivityAt: String

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthWidgetConfig

Public presentation configuration. Failure to load this resource need not block community reads.

struct AppNorthWidgetConfig

branding

let branding: AppNorthWidgetConfig.Branding

features

let features: AppNorthWidgetConfig.Features?

languages

let languages: [String]?

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthWidgetConfig.Branding

Server-controlled branding flags. watermark controls the built-in attribution.

struct Branding

watermark

let watermark: Bool

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthWidgetConfig.Features

Optional feature configuration. Chat availability also depends on the current identity.

struct Features

chat

let chat: AppNorthChatMode?

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthChatMode

Chat participation mode. Unknown decoded values fall back to disabled.

enum AppNorthChatMode

disabled

case disabled

identified

case identified

anyone

case anyone

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthChatStatus

Chat availability and unread state without reading the conversation.

struct AppNorthChatStatus

available

let available: Bool

hasUnread

let hasUnread: Bool

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthChatMessage

A server message, optionally carrying an action card and its first-open timestamp.

struct AppNorthChatMessage

kind

var kind: String?

actionCard

var actionCard: AppNorthActionCard?

openedAt

var openedAt: String?

tappedAt

var tappedAt: String?

First review-card tap recorded by the server; this does not confirm a submitted review.

id

let id: String

The stable identity of the entity associated with this instance.

sender

let sender: AppNorthChatMessage.Sender

body

let body: String

SwiftUI view content. Present the view itself rather than invoking this property directly.

createdAt

let createdAt: String

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

images

var images: [AppNorthChatImage]?

Optional for backward compatibility with messages from older servers. URLs are private and expire; reload the thread to refresh them.

AppNorthChatMessage.Sender

Conversation side. Unknown sender values decode as team for forward compatibility.

enum Sender

user

case user

team

case team

init(from:)

init(from decoder: any Decoder) throws

Future sender kinds render as the team side instead of failing.

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthChatThread

Conversation availability and the returned messages. A request with an after cursor can contain only newer messages.

struct AppNorthChatThread

available

let available: Bool

messages

let messages: [AppNorthChatMessage]

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthComment

A community comment. official indicates an official reply when provided by the server.

struct AppNorthComment

id

let id: String

The stable identity of the entity associated with this instance.

authorName

let authorName: String?

body

let body: String

SwiftUI view content. Present the view itself rather than invoking this property directly.

createdAt

let createdAt: String

official

let official: Bool?

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthIdeaSort

Server sorting choices: most voted (top), newest (recent), or most discussed (discussed).

enum AppNorthIdeaSort

top

case top

recent

case recent

discussed

case discussed

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthPage

One page of typed items and an opaque optional next cursor. A nil cursor ends pagination.

struct AppNorthPage<Element> where Element : Sendable

items

let items: [Element]

nextCursor

let nextCursor: String?

AppNorthVote

The resulting vote state and count. applied distinguishes a state change from a repeated request.

struct AppNorthVote

voted

let voted: Bool

voteCount

let voteCount: Int

applied

let applied: Bool

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthCreation

A created value plus moderated, which indicates that publication may await approval.

struct AppNorthCreation<Element> where Element : Sendable

value

let value: Element

moderated

let moderated: Bool

AppNorthFeedbackType

Private report category: problem, suggestion, or comment.

enum AppNorthFeedbackType

problem

case problem

suggestion

case suggestion

comment

case comment

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthViewport

Positive width and height for the reported screen. The server accepts dimensions up to 20,000.

struct AppNorthViewport

width

let width: Int

height

let height: Int

init(width:height:)

init(width: Int, height: Int)

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthJSONValue

Codable JSON values for diagnostic metadata. Sanitize content before sending it.

indirect enum AppNorthJSONValue

string(_:)

case string(String)

number(_:)

case number(Double)

bool(_:)

case bool(Bool)

object(_:)

case object([String : AppNorthJSONValue])

array(_:)

case array([AppNorthJSONValue])

null

case null

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

encode(to:)

func encode(to encoder: any Encoder) throws

Encodes the value using its wire representation.

AppNorthDiagnosticCategory

Diagnostic kind: console, error, network, or breadcrumb.

enum AppNorthDiagnosticCategory

console

case console

error

case error

network

case network

breadcrumb

case breadcrumb

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthDiagnosticLevel

Diagnostic severity: debug, info, warn, or error.

enum AppNorthDiagnosticLevel

debug

case debug

info

case info

warn

case warn

error

case error

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthFeedbackDiagnostic

One host-sanitized diagnostic with optional severity, timestamp, and JSON metadata. Requires the host’s collection consent.

struct AppNorthFeedbackDiagnostic

category

let category: AppNorthDiagnosticCategory

level

let level: AppNorthDiagnosticLevel?

message

let message: String

timestamp

let timestamp: String?

metadata

let metadata: [String : AppNorthJSONValue]?

init(category:level:message:timestamp:metadata:)

init(category: AppNorthDiagnosticCategory, level: AppNorthDiagnosticLevel? = nil, message: String, timestamp: String? = nil, metadata: [String : AppNorthJSONValue]? = nil)

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthDeviceType

Device context values accepted by the shared wire contract. Their presence is not a promise of SDK support for other platforms; this guide covers iOS.

enum AppNorthDeviceType

phone

case phone

tablet

case tablet

desktop

case desktop

tv

case tv

unknown

case unknown

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthDeviceContext

Optional bounded device/app context. Strings are flattened and truncated to 120 Swift characters. No automatic device collection or secret redaction is performed.

struct AppNorthDeviceContext

appName

let appName: String?

appVersion

let appVersion: String?

buildVersion

let buildVersion: String?

deviceModel

let deviceModel: String?

deviceType

let deviceType: AppNorthDeviceType?

operatingSystem

let operatingSystem: String?

systemVersion

let systemVersion: String?

init(appName:appVersion:buildVersion:deviceModel:deviceType:operatingSystem:systemVersion:)

init(appName: String? = nil, appVersion: String? = nil, buildVersion: String? = nil, deviceModel: String? = nil, deviceType: AppNorthDeviceType? = nil, operatingSystem: String? = nil, systemVersion: String? = nil)

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthCreateFeedback

Private report input. Required fields are type, message, HTTP(S) page URL, and viewport; optional diagnostics and screenshot must already be consented and sanitized. See private feedback.

struct AppNorthCreateFeedback

type

let type: AppNorthFeedbackType

message

let message: String

email

let email: String?

pageURL

let pageURL: URL

pageTitle

let pageTitle: String?

locale

let locale: String?

viewport

let viewport: AppNorthViewport

userAgent

let userAgent: String?

deviceContext

let deviceContext: AppNorthDeviceContext?

diagnostics

let diagnostics: [AppNorthFeedbackDiagnostic]

screenshot

let screenshot: String?

init(type:message:email:pageURL:pageTitle:locale:viewport:userAgent:deviceContext:diagnostics:screenshot:)

init(type: AppNorthFeedbackType, message: String, email: String? = nil, pageURL: URL, pageTitle: String? = nil, locale: String? = nil, viewport: AppNorthViewport, userAgent: String? = nil, deviceContext: AppNorthDeviceContext? = nil, diagnostics: [AppNorthFeedbackDiagnostic] = [], screenshot: String? = nil)

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthFeedbackReceipt

Submission receipt. screenshotStored reports actual attachment retention and deduplicated identifies a replayed creation.

struct AppNorthFeedbackReceipt

id

let id: String

correlationId

let correlationId: String

screenshotStored

let screenshotStored: Bool

deduplicated

let deduplicated: Bool

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthFeedbackReply

A public staff reply within the author’s private report thread.

struct AppNorthFeedbackReply

id

let id: String

The stable identity of the entity associated with this instance.

message

let message: String

createdAt

let createdAt: String

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthFeedbackThread

The verified author’s report and public staff replies. Excludes contact data, screenshots, diagnostics, and internal notes.

struct AppNorthFeedbackThread

id

let id: String

The stable identity of the entity associated with this instance.

type

let type: AppNorthFeedbackType

status

let status: String

message

let message: String

createdAt

let createdAt: String

updatedAt

let updatedAt: String

replies

let replies: [AppNorthFeedbackReply]

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthOfflineQueueStorage

Host-provided queue persistence. Keep it separate from cache storage and scope retention to your account lifecycle.

protocol AppNorthOfflineQueueStorage : Sendable

load()

func load() async throws -> Data?

Returns stored bytes, or nil when no snapshot exists. Storage failures may throw.

save(_:)

func save(_ data: Data?) async throws

Replaces stored bytes; nil means remove the persisted snapshot.

AppNorthOfflineQueueError

Queue errors. full means another write could not be queued without exceeding capacity.

enum AppNorthOfflineQueueError

full

case full

AppNorthOfflineQueue

Opt-in actor for network-failed idea, comment, and feedback writes. Default capacity is 50. Storage and flush scheduling belong to the host; votes and chat are not queue operations.

actor AppNorthOfflineQueue

init(storage:limit:)

init(storage: any AppNorthOfflineQueueStorage, limit: Int = 50)

submitIdea(using:board:title:description:authorName:idempotencyKey:)

@discardableResult func submitIdea(using client: AppNorthClient, board: String, title: String, description: String = "", authorName: String? = nil, idempotencyKey: String = UUID().uuidString) async throws -> Bool

Attempts the write immediately; returns true on success, false when a network failure is queued. Other errors propagate. Preserve the idempotency key on replay.

submitComment(using:handle:body:authorName:idempotencyKey:)

@discardableResult func submitComment(using client: AppNorthClient, handle: String, body: String, authorName: String? = nil, idempotencyKey: String = UUID().uuidString) async throws -> Bool

Attempts the write immediately; returns true on success, false when a network failure is queued. Other errors propagate. Preserve the idempotency key on replay.

submitFeedback(using:input:idempotencyKey:)

@discardableResult func submitFeedback(using client: AppNorthClient, input: AppNorthCreateFeedback, idempotencyKey: String = UUID().uuidString) async throws -> Bool

Attempts the write immediately; returns true on success, false when a network failure is queued. Other errors propagate. Preserve the idempotency key on replay.

pendingCount()

func pendingCount() async throws -> Int

Loads storage if needed and returns the total number of queued entries across scopes.

flush(using:)

func flush(using client: AppNorthClient) async throws

Replays only matching identity scopes. Stops on network failure; other errors throw and leave the entry pending.

AppNorthPaywallProduct

Product metadata: localized display price, ISO-8601 billing period, and optional trial duration. Use P0D for one-time products.

struct AppNorthPaywallProduct

id

let id: String

price

let price: String

Localized display price, including currency.

period

let period: String

ISO-8601 duration. Use P0D for a one-time product.

trial

let trial: String?

init(id:price:period:trial:)

init(id: String, price: String, period: String, trial: String? = nil)

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthPaywall

One offering in the device catalog. Title is team-only; the initializer sets source to revenuecat. presentable and available both gate opening.

struct AppNorthPaywall

id

let id: String

The stable identity of the entity associated with this instance.

title

let title: String

Team-only label; never rendered on the customer's card.

source

let source: String

presentable

let presentable: Bool

available

var available: Bool

products

let products: [AppNorthPaywallProduct]

init(id:title:presentable:available:products:)

init(id: String, title: String, presentable: Bool, available: Bool = true, products: [AppNorthPaywallProduct] = [])

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthSubscriptionMetadata

Device-reported subscription state, separate from signed identity. Dates use YYYY-MM-DD; renews and expires dates are mutually exclusive. Sandbox sets environment to sandbox.

struct AppNorthSubscriptionMetadata

subscription_status

let subscription_status: AppNorthSubscriptionMetadata.Status

plan

let plan: String?

renews_on

let renews_on: String?

expires_on

let expires_on: String?

environment

let environment: String?

init(status:plan:renewsOn:expiresOn:sandbox:)

init(status: AppNorthSubscriptionMetadata.Status, plan: String? = nil, renewsOn: String? = nil, expiresOn: String? = nil, sandbox: Bool = false)

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthSubscriptionMetadata.Status

Whether the reported subscription is active or absent. This device metadata is not a trusted entitlement check.

enum Status

active

case active

none

case none

init(rawValue:)

init?(rawValue: String)

Creates a new instance with the specified raw value. If there is no value of the type that corresponds with the specified raw value, this initializer returns nil. For example: enum PaperSize: String { case A4, A5, Letter, Legal } print(PaperSize(rawValue: "Legal")) // Prints "Optional(PaperSize.Legal)" print(PaperSize(rawValue: "Tabloid")) // Prints "nil" - Parameter rawValue: The raw value to use for the new instance.

AppNorthPaywallSnapshot

Validated catalog and optional plan metadata. Limits: 20 paywalls, 10 products per paywall, 40 products total. Validation also runs during encoding.

struct AppNorthPaywallSnapshot

paywalls

let paywalls: [AppNorthPaywall]

plan

let plan: AppNorthSubscriptionMetadata?

init(paywalls:plan:)

init(paywalls: [AppNorthPaywall], plan: AppNorthSubscriptionMetadata? = nil) throws

validate()

func validate() throws

Throws invalidConfiguration for invalid catalog or metadata fields; never silently trims the catalog.

encode(to:)

func encode(to encoder: any Encoder) throws

Encodes the value using its wire representation.

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthActionCard

Server card envelope. Only offer cards are supported by the paywall controller; unknown kinds remain disabled. titleAtSend is a team label, not customer-facing copy.

struct AppNorthActionCard

kind

let kind: String

paywallId

let paywallId: String?

titleAtSend

let titleAtSend: String

buttonLabel

let buttonLabel: String

body

let body: String?

SwiftUI view content. Present the view itself rather than invoking this property directly.

available

var available: Bool

init(from:)

init(from decoder: any Decoder) throws

Creates a new instance by decoding from the given decoder. This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. - Parameter decoder: The decoder to read data from.

AppNorthPaywalls

Main-actor paywall controller; retain one per chat identity. Configure catalog, availability, and presentation before passing it to chat. See paywall cards.

@MainActor final class AppNorthPaywalls

snapshot

@Published @MainActor var snapshot: AppNorthPaywallSnapshot? { get }

availabilityProvider

@MainActor var availabilityProvider: ((String) async -> Bool)?

onPaywall

@MainActor var onPaywall: ((String) -> Void)?

onOfferOpened

@MainActor var onOfferOpened: ((String, String) -> Void)?

Called once per card in this controller. Server receipts also deduplicate across app launches. This signals a tap, not a completed purchase.

init(client:)

@MainActor init(client: AppNorthClient)

setPaywalls(_:plan:)

@MainActor func setPaywalls(_ values: [AppNorthPaywall], plan: AppNorthSubscriptionMetadata? = nil) throws

Validates and replaces the complete catalog and optional metadata. Does not send until refresh.

refreshPaywalls(invalidate:)

@MainActor func refreshPaywalls(invalidate: Bool = false) async throws

Re-evaluates availability, reports the snapshot, and retries pending receipts. Concurrent refreshes coalesce; invalidate after state changes.

isAvailable(_:)

@MainActor func isAvailable(_ card: AppNorthActionCard) -> Bool

Requires an offer card, server availability, a matching identity, a presentation callback, and a presentable available snapshot entry.

open(_:messageId:alreadyOpened:)

@MainActor func open(_ card: AppNorthActionCard, messageId: String, alreadyOpened: Bool = false) async

Invokes presentation for an available card and reports its first tap. Already-opened cards may present again without another first-open event.

AppNorthReadCacheStorage

Host-provided cache persistence. Use a different storage instance or file from the offline queue.

protocol AppNorthReadCacheStorage : Sendable

load()

func load() async throws -> Data?

Returns stored bytes, or nil when no snapshot exists. Storage failures may throw.

save(_:)

func save(_ data: Data?) async throws

Replaces stored bytes; nil means remove the persisted snapshot.

AppNorthReadCache

Opt-in actor storing boards and idea arrays. Default TTL is 300 seconds; stale or missing entries return nil. See offline and cache.

actor AppNorthReadCache

init(storage:ttl:clock:)

init(storage: any AppNorthReadCacheStorage, ttl: TimeInterval = 300, clock: @escaping @Sendable () -> Date = { Date() })

boards(scope:)

func boards(scope: String) async throws -> [AppNorthBoard]?

Returns cached values only while fresh under the configured TTL; otherwise nil.

ideas(scope:query:)

func ideas(scope: String, query: String) async throws -> [AppNorthIdea]?

Returns cached values only while fresh under the configured TTL; otherwise nil.

storeBoards(_:scope:)

func storeBoards(_ boards: [AppNorthBoard], scope: String) async throws

Stores the supplied values with the current timestamp under the specified scope and query, then persists them.

storeIdeas(_:scope:query:)

func storeIdeas(_ ideas: [AppNorthIdea], scope: String, query: String) async throws

Stores the supplied values with the current timestamp under the specified scope and query, then persists them.

clear(scope:)

func clear(scope: String) async throws

Removes the scope’s boards and all idea queries in that scope, then persists the new snapshot.

AppNorthTheme

Native view colors. Primary defaults to the host accent; optional secondary, tertiary, and text pairs follow appearance. See appearance.

struct AppNorthTheme

primary

var primary: Color

secondary

var secondary: AppNorthColorPair?

tertiary

var tertiary: AppNorthColorPair?

text

var text: AppNorthColorPair?

init(primary:secondary:tertiary:text:)

init(primary: Color = Self.defaultPrimary, secondary: AppNorthColorPair? = nil, tertiary: AppNorthColorPair? = nil, text: AppNorthColorPair? = nil)

defaultPrimary

static var defaultPrimary: Color { get }

AppNorthTranslateButton

Idea translation control policy. Translation UI requires iOS 18 or later even when set to always.

enum AppNorthTranslateButton

automatic

case automatic

always

case always

hide

case hide

AppNorthUpvoteIcon

Built-in vote icon options or a host-provided SF Symbol name.

enum AppNorthUpvoteIcon

systemName(_:)

case systemName(String)

thumbsUp

case thumbsUp

arrowUp

case arrowUp

chevronUp

case chevronUp

AppNorthUIConfiguration

Native view behavior. Defaults: open/closed filter on, arrow-up vote icon, automatic translation, chat button on.

struct AppNorthUIConfiguration

showsOpenClosedFilter

var showsOpenClosedFilter: Bool

upvoteIcon

var upvoteIcon: AppNorthUpvoteIcon

translateButton

var translateButton: AppNorthTranslateButton

showsChatButton

var showsChatButton: Bool

Shows the floating chat button when chat is enabled for the project.

init(showsOpenClosedFilter:upvoteIcon:translateButton:showsChatButton:)

init(showsOpenClosedFilter: Bool = true, upvoteIcon: AppNorthUpvoteIcon = .arrowUp, translateButton: AppNorthTranslateButton = .automatic, showsChatButton: Bool = true)

AppNorthIdentityProperty

A scalar property included in a v2 identity signature. Preserve the exact backend-issued values.

enum AppNorthIdentityProperty

string(_:)

case string(String)

number(_:)

case number(Double)

boolean(_:)

case boolean(Bool)

encode(to:)

func encode(to encoder: any Encoder) throws

Encodes the value using its wire representation.

AppNorthIdentityCredential

An in-memory credential issued by your authenticated backend. Construction validates shape; only the server verifies the signature. See verified identity.

struct AppNorthIdentityCredential

email

let email: String

expiresAt

let expiresAt: Int64

signature

let signature: String

properties

let properties: [String : AppNorthIdentityProperty]?

init(email:expiresAt:signature:properties:)

init(email: String, expiresAt: Int64, signature: String, properties: [String : AppNorthIdentityProperty]? = nil) throws

isValid(at:)

func isValid(at date: Date = Date()) -> Bool

Checks expiry only. A valid return value does not authenticate the signature.

func captureAppNorthScreenshot(consent: Bool, adapter: (any AppNorthScreenshotCapturing)?, redactor: (any AppNorthScreenshotRedacting)?) async -> String?

Captures only with consent and both adapters. Returns nil on any failure, unsupported data URL, oversize data, or unchanged redactor output. See consented capture.

RevenueCat helper

@MainActor public final class AppNorthRevenueCatPaywalls

This optional source helper lives outside the SDK binary. Copy Helpers/AppNorthRevenueCatPaywalls.swift into your app target and add RevenueCat dependencies as described in paywall cards.

init(client:syncsSubscriptionMetadata:presenter:)

init(client: AppNorthClient, syncsSubscriptionMetadata: Bool = true, presenter: (() -> UIViewController?)? = nil)

Create after configuring Purchases and the matching AppNorth identity. Starts observation and the first reload; retain for the session.

paywalls

let paywalls: AppNorthPaywalls

Pass this controller to AppNorthChatView or AppNorthView.

onError

var onError: ((Error) -> Void)?

Receives offering-load or synchronization failures. Avoid logging identity or customer details.

reload()

func reload()

Loads all offerings and customer information, then refreshes availability and snapshot metadata. Concurrent reloads coalesce.

stop()

func stop()

Stops observation/reload tasks, removes the foreground observer, disables presentation, dismisses the paywall, and clears the catalog. Call before changing accounts; create a new helper for the next session.

Verify

  1. Import AppNorth in your app target and compile the example above with a configured client.

  2. Use public initializers exactly as listed; keep main-actor objects on the main actor.

  3. Run the verification steps in the relevant task guide against your own project. A successful compile does not verify server policy, account ownership, or purchase configuration.

Review request card activity

AppNorthClient.markActionCardTapped(_:) async throws reports the first tap on a review_request card. markActionCardOpened(_:) reports its first display. Both use stable, separate idempotency keys and the current chat identity; neither reports a submitted review or guarantees that StoreKit displayed a prompt.

AppNorthChatMessage.tappedAt and AppNorthChatEntry.tappedAt are optional server timestamp strings, alongside openedAt. AppNorthChatModel updates the existing entry when polling returns new card activity. AppNorthChatView presents review cards without a paywall controller and uses AppStore.requestReview(in:) on iOS 16+. See review requests in support chat for sending, automatic resolution settings, and Apple's presentation limits.

AppNorthChatImage

struct AppNorthChatImage: Codable, Hashable, Identifiable, Sendable

PropertyTypeMeaning
idStringImage identifier.
urlURL?Private, expiring URL; nil when storage is unavailable.
widthIntSanitized image width in pixels.
heightIntSanitized image height in pixels.

AppNorthChatImageUpload

struct AppNorthChatImageUpload: Codable, Hashable, Sendable

PropertyTypeMeaning
imageTokenStringApp- and sender-bound token, valid for 24 hours.
imageAppNorthChatImageSanitized preview; its URL expires in 10 minutes.

Use AppNorthChatModel.send(_:images:) to queue completed uploads with optimistic bubbles, or pass their tokens directly to AppNorthClient.sendChatMessage.