Why the app on the phone is the real test
A successful export on a Mac is not a product. Privacy, speed, and offline use only hold when the app generates from the baked resource without a network. That is proof the phone has the model. The clothing-classifier course shows the same last step for a small CNN. This lesson is that last step for a personalized generator. See From the metal to the model for what the GPU actually ran.
The app loads the merged resource. It does not download a LoRA. It does not hot-swap an adapter. If you need a second subject, you bake a second model file and ship a second build or a second bundled resource.
The steps, one box at a time
Keep the export folder intact.
Add that folder to the Xcode target as a resource, not as a flattened file list you renamed.
Prepare the Core AI diffusion pipeline once at launch.
Generate off the main thread. Show the image, the seed, and the elapsed time. Profile on the target device.
Environment setup
Use Xcode 27 on macOS. Create an iOS or macOS SwiftUI App. Add the Core AI framework. Install the Metal Toolchain. Add the whole exported folder to Copy Bundle Resources.
xcode-select --install
xcodebuild -version
# File > New > Project > App, Interface: SwiftUI
# Xcode Settings > Components > Metal Toolchain
# Drag artifacts/personalized into the target. Confirm it is in Copy Bundle Resources.
Python is not required on this lesson's main path. Keep the Mac uv project around so you can regenerate the resource.
cd diffusion-lora-coreai
uv run python -c "import torch; print(torch.__version__); print('MPS:', torch.backends.mps.is_available())"
Worked path
Build a narrow first screen. One prompt field. One seed. One Generate button. One image. One elapsed-time label. Prepare the pipeline in a .task or app setup, not in the button.
import SwiftUI
import CoreAI
// Illustrative shape only. Confirm type names in the installed Xcode 27 docs.
@MainActor
final class Generator: ObservableObject {
@Published var image: Image?
@Published var elapsedMs: Int = 0
@Published var status: String = "Preparing"
private var pipeline: AnyObject?
func prepare() async {
do {
let url = Bundle.main.resourceURL!.appending(path: "personalized")
pipeline = try await loadDocumentedDiffusionPipeline(resource: url)
status = "Ready"
} catch {
status = "Load failed: \(error.localizedDescription)"
}
}
func generate(prompt: String, seed: UInt64) async {
let start = Date()
do {
let cgImage = try await runDocumentedGeneration(pipeline, prompt: prompt, seed: seed)
image = Image(decorative: cgImage, scale: 1)
elapsedMs = Int(Date().timeIntervalSince(start) * 1000)
status = "Seed \(seed)"
} catch {
status = "Generate failed: \(error.localizedDescription)"
}
}
}
The helper names above are placeholders on purpose. Core AI diffusion types move with the SDK. Open the installed documentation for the current pipeline loader and generation call. The clothing course shows the same care for AIModel and NDArray.
Compare like with like. Same prompt. Same seed. Base export versus merged export. Check that the subject or style you wrote down actually appears. Save the prompt, seed, elapsed time, device model, and resource checksum with the screenshot.
Failure modes
- Flattening the resource folder. Missing tokenizer, scheduler, or VAE files fail at prepare, not at export.
- Preparing on every tap. The extra time to get the model ready will look like generation time.
- Running generation on the main actor. The UI will freeze. Move work off the UI path.
- Copying invented API names into production. Verify the installed SDK. This course's Swift is a shape, not a frozen header.
- Judging quality only on the Mac preview. Measure the device that stores the model file.
- Shipping a loader that expects an adapter file. There is no documented Core AI LoRA swap.
Done when
- The Xcode target contains the intact export folder and the Core AI framework.
- Prepare succeeds once at launch. A fixed prompt and seed produce an image and an elapsed time on a real device or the intended simulator.
- A note records prompt, seed, device, checksum, and that no runtime LoRA was loaded.
Keep learning
PyTorch to Core AI in Xcode · From the metal to the model · Core AI models, typed · Model architectures in plain English · LLM LoRA for your writing style
Key concepts
- Mac export success is not the product. Device generation is.
- Bundle the intact multi-component folder in Xcode 27.
- Prepare the pipeline once at launch. Generate off the main thread.
- The app loads the baked merged resource only. No adapter download.
Takeaways
- Record prompt, seed, device, checksum, and elapsed time.
- Flattening the folder breaks prepare.
- A second personalization is a second bake and a second resource.