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.
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.
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:)
@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.
AppNorthLink
Parsed destination kind and decoded identifier. The link carries no authorization.
struct AppNorthLink
kind
let kind: AppNorthLinkKind
value
let value: String
AppNorthDeepLinks
Custom-scheme URL builders and parser. Parsing does not validate the application scheme; the host must check it. See deep links.
enum AppNorthDeepLinks
idea(scheme:handle:)
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.
feedback(scheme:id:)
static func feedback(scheme: String, id: String) throws -> URL
Creates a custom-scheme private-report URL; it conveys no identity or access rights.
chat(scheme:)
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.
parse(_:)
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.
captureAppNorthScreenshot(consent:adapter:redactor:)
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
Import
AppNorthin your app target and compile the example above with a configured client.Use public initializers exactly as listed; keep main-actor objects on the main actor.
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
| Property | Type | Meaning |
|---|---|---|
id | String | Image identifier. |
url | URL? | Private, expiring URL; nil when storage is unavailable. |
width | Int | Sanitized image width in pixels. |
height | Int | Sanitized image height in pixels. |
AppNorthChatImageUpload
struct AppNorthChatImageUpload: Codable, Hashable, Sendable
| Property | Type | Meaning |
|---|---|---|
imageToken | String | App- and sender-bound token, valid for 24 hours. |
image | AppNorthChatImage | Sanitized 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.