Skip to main content

Quick Start Examples

This page provides examples to quickly get started with the Genies NAFKit iOS SDK.

Integrated Path​

This example demonstrates the progressive path (loadAvatarProgressive) which runs the silhouette preview, the multi-asset composition, animation wiring, the smart-avatar behavior, and background per-asset LOD upgrades internally — consumers only see the final, animated avatar.

import NAFKitUI

let view = try NAFView()
addSubview(view)

let avatar = try NAFAvatar(fromJSON: avatarJSON)
try await view.loadAvatar(avatar, mode: .behavior(silhouette: true), lods: [2, 0])

// Toon shader — bundled "default" preset.
view.applyDefaultToonPreset()

// Camera framing
view.frameAvatar()

Custom Path​

This example shows how to use your own renderer. The SDK still owns asset loading, animation, behavior, and the per-frame skeleton math through NAFAvatarSession; you own the Metal pipeline, scene, and camera. onMeshesRebuilt fires after every composition change (load, equip/unequip, LOD upgrade, silhouette swap) — your renderer just replaces its submesh set.

import NAFKit
import NAFKitUI
import NAFKitUITools

let metalRenderer = NAFRenderer(device: nil)
let session = NAFAvatarSession(device: metalRenderer.device)

// Your renderer conforms to NAFAvatarRenderer (two methods).
let myRenderer = MyMetalRenderer(device: metalRenderer.device)

// Receive meshes when the SDK (re)builds them.
session.onMeshesRebuilt = { [weak myRenderer] meshes in
myRenderer?.applyMeshes(meshes)
}

// Gaze hooks — set once. `tick(...)` reads them per frame.
session.cameraPositionProvider = { [weak cam] in cam?.eyePosition ?? .zero }
session.avatarModelTransform = uprightTransform

// Loading — same as Integrated
let avatar = try NAFAvatar(fromJSON: avatarJSON)
try await session.loadAvatar(avatar, mode: .behavior(silhouette: true), lods: [2, 0])

// Per-frame — one call
metalRenderer.onUpdate = { [weak session, weak myRenderer] dt in
if let session, let myRenderer {
session.tick(deltaTime: Float(dt), renderer: myRenderer)
}
}

metalRenderer.onRender = { [weak myRenderer] encoder in
let frame = FrameContext(view: viewMatrix,
viewProjection: viewProjection,
aspect: aspect)
myRenderer?.render(encoder: encoder, context: frame)
}