Skip to Content
iOS OptionsCustomization

Theme, language, logging, and telemetry

Theme

Configure the native UI palette when constructing the session:

import UIKit import GraffityARCloud @MainActor func makeBrandedSession(accessToken: String) throws -> GraffityARCloud { let theme = GraffityARTheme( primary: .systemIndigo, surface: .white, onSurface: .black, onSurfaceSecondary: .darkGray, danger: .systemRed ) return try GraffityARCloud(configuration: GraffityARCloudConfiguration( accessToken: accessToken, theme: theme )) }
SlotDefaultUse
primary#19A6B6Continue, dismiss, and retry fills; choose a fill readable under white titles.
secondary#002D40Reserved; currently has no visible effect.
surface#FFFFFFNative panels, including navigation banner and minimap sheet.
onSurface#1A1A1APrimary panel text and glyphs.
onSurfaceSecondary#8A8A8ESecondary panel text.
danger#FF3B30Navigation exit fill.

GraffityARTheme.default uses the brand palette. init?(primaryHex:secondaryHex:) accepts six-digit RGB strings, optionally prefixed with #, returning nil for invalid input. Neutral slots retain defaults; use the color initializer to customize them. Pass fixed colors when your palette should stay stable over the camera feed.

The native palette is shared internally: overlapping sessions use the most recently installed palette. Prefer one active session. There is no public theme API for styling the destination web map.

Language

The SDK bundles English and Thai. It matches preferred languages against bundled languages and falls back to English. Missing strings fall back to English, then the key.

GraffityARLocalizationProvider declares string(forKey:locale:) -> String?, but the current configuration has no provider injection point and no public locale setter exists. Host apps cannot install custom translations yet. Localize your permission descriptions separately with your app’s InfoPlist.strings.

Logging

GraffityARLogging.default uses the SDK’s os.Logger adapter at .info and above. Severities are .debug, .info, .warning, and .error, ordered lowest to highest. Supply a Sendable sink with .custom(_:minimum:):

import OSLog import GraffityARCloud final class AppARLogger: GraffityARLogger { private let logger = Logger(subsystem: "com.example.app", category: "ARCloud") func log( _ level: GraffityARLogLevel, _ message: @autoclosure () -> String, file: String, line: Int ) { let text = message() switch level { case .debug: logger.debug("\(text, privacy: .public)") case .info: logger.info("\(text, privacy: .public)") case .warning: logger.warning("\(text, privacy: .public)") case .error: logger.error("\(text, privacy: .public)") } } } @MainActor func makeLoggedSession(accessToken: String) throws -> GraffityARCloud { try GraffityARCloud(configuration: GraffityARCloudConfiguration( accessToken: accessToken, logging: .custom(AppARLogger(), minimum: .debug) )) }

Sinks may be called from different actors; make them thread-safe. The SDK filters severity and scrubs recognized token/coordinate patterns before forwarding messages. Custom sinks receive a category prefix; file and line identify the source.

sdk.setLogger(logger) changes the sink at runtime. Passing nil restores the default sink. Neither changes the configured minimum severity.

Disable all output

Although .disabled exists, 5.1.1 still routes error-level records to fallback os.Logger for that setting. For silence, use a no-op sink:

import GraffityARCloud final class SilentARLogger: GraffityARLogger { func log( _ level: GraffityARLogLevel, _ message: @autoclosure () -> String, file: String, line: Int ) {} } @MainActor func makeSilentSession(accessToken: String) throws -> GraffityARCloud { try GraffityARCloud(configuration: GraffityARCloudConfiguration( accessToken: accessToken, logging: .custom(SilentARLogger(), minimum: .error) )) }

Telemetry

GraffityARTelemetry.default is .disabled. Opt in with telemetry: .enabled or sdk.setTelemetryEnabled(true). Turn analytics off with sdk.setTelemetryEnabled(false). Connect this to your app’s user preference or consent flow where appropriate.

Telemetry controls analytics. It does not disable camera-frame localization, location lookup, assets, map requests, or player collection requests required for the experience. userId scopes content and collection independently of telemetry.