Working with partially generated content in Xcode previews

With the introduction of the Foundation Models framework, Apple provides tools to integrate AI-generated content directly into SwiftUI apps. One particularly useful feature is guided generation of Swift data structures. In this post, we'll explore techniques for working with partially generated content in Xcode previews.
Code examples are tested in Xcode 26.0 beta 4 (17A5285i). APIs may change in the final release.
Generating content
Let's start with a simple example from Apple documentation and generate a cat profile:
import FoundationModels
@Generable(description: "Basic profile information about a cat")
struct CatProfile: Equatable {
let name: String
@Guide(description: "A one sentence profile about the cat's personality")
let profile: String
@Guide(description: "The age of the cat", .range(0...20))
let age: Int
}
To show it, create a view that generates and displays the cat profile:
import SwiftUI
import FoundationModels
struct ContentView: View {
@State private var session = LanguageModelSession()
@State private var catProfile: CatProfile.PartiallyGenerated?
var body: some View {
NavigationStack {
List {
if let catProfile {
// Displays the cat profile
CatProfileView(catProfile: catProfile)
}
}
.navigationTitle("Cat Profile")
.task {
do {
let stream = session.streamResponse(generating: CatProfile.self) {
"Generate a cute rescue cat"
}
for try await catProfile in stream {
self.catProfile = catProfile
}
} catch {
print("Error generating cat profile: \(error)")
}
}
}
}
}
The session responds with a delay, so we can use streamResponse
to get a stream of partially generated content. Here's what PartiallyGenerated
may look like if you expand @Generable
macro:
nonisolated struct PartiallyGenerated: Identifiable, nonisolated FoundationModels.ConvertibleFromGeneratedContent, Equatable {
var id: GenerationID
var name: String.PartiallyGenerated?
var profile: String.PartiallyGenerated?
var age: Int.PartiallyGenerated?
nonisolated init(_ content: FoundationModels.GeneratedContent) throws {
self.id = content.id ?? GenerationID()
self.name = try content.value(forProperty: "name")
self.profile = try content.value(forProperty: "profile")
self.age = try content.value(forProperty: "age")
}
}
To display the cat profile, we create a separate view:
import SwiftUI
import FoundationModels
struct CatProfileView: View {
let catProfile: CatProfile.PartiallyGenerated
var body: some View {
VStack(alignment: .leading) {
if let name = catProfile.name {
Text(name)
.font(.headline)
}
if let profile = catProfile.profile {
Text(profile)
.font(.subheadline)
}
if let age = catProfile.age {
Text("Age: \(age)")
.font(.caption)
}
}
}
}
Let's check the result:

Looks good, but how do we check the layout and intermediate states of CatProfileView
in Xcode preview?
Working with previews
Any Generable
type can be converted to PartiallyGenerated
by calling asPartiallyGenerated()
function:
extension CatProfile {
static let mock = CatProfile(name: "Trisha",
profile: "A playful and curious cat who loves to explore her surroundings.",
age: 8)
}
#Preview("Mock", traits: .sizeThatFitsLayout) {
CatProfileView(catProfile: CatProfile.mock.asPartiallyGenerated())
}
Here we can check the final layout of the view:
Additionally, we can create a CatProfile
from raw JSON fragments:
#Preview("GeneratedContent", traits: .sizeThatFitsLayout) {
let jsons = [
#"{"name": "Trisha"#,
#"{"name": "Trisha", "profile": "A playful and curious cat"#,
#"{"name": "Trisha", "profile": "A playful and curious cat", "age": 8"#,
]
VStack(spacing: 8) {
ForEach(jsons, id: \.self) { json in
let content = try! GeneratedContent(json: json)
CatProfileView(catProfile: try! .init(content))
}
}
}
Note that the JSON may be invalid; GeneratedContent
will still attempt to parse it and return PartiallyGenerated
value with whatever fields are present.
Initially, I tried to use the generated content's type directly:
CatProfileView(catProfile: try! CatProfile.PartiallyGenerated(content))
But I got an error related to the type mismatch:
Cannot convert value of type 'CatProfile.PartiallyGenerated' (aka 'CatProfile') to expected argument type 'CatProfile.PartiallyGenerated'
I'm not yet sure why this fails — if you have ideas, please let me know.
Let's check the preview:

Here we can validate intermediate states. For instance, if CatProfile
has only the name
property, the content becomes centered — something we may want to adjust.
Next, let's add some animations:
import SwiftUI
import FoundationModels
struct CatProfileView: View {
let catProfile: CatProfile.PartiallyGenerated
var body: some View {
VStack(alignment: .leading) {
if let name = catProfile.name {
Text(name)
.font(.headline)
.transition(.opacity)
}
if let profile = catProfile.profile {
Text(profile)
.font(.subheadline)
.transition(.opacity)
}
if let age = catProfile.age {
Text("Age: \(age)")
.font(.caption)
.transition(.opacity)
}
}
.animation(.easeInOut, value: catProfile)
}
}
To simulate a stream response and verify animations with partially generated content, let's write an extension:
import FoundationModels
extension Generable {
static func streamResponse(from json: String,
offsetBy distance: Int = 4,
delay: Duration = .milliseconds(500)) -> AsyncThrowingStream<Self.PartiallyGenerated, Error> {
AsyncThrowingStream { continuation in
Task {
var index = json.startIndex
while index < json.endIndex {
let nextIndex = json.index(index, offsetBy: distance, limitedBy: json.endIndex) ?? json.endIndex
let substring = String(json[..<nextIndex])
let generatedContent = try GeneratedContent(json: substring)
let content = try PartiallyGenerated(generatedContent)
continuation.yield(content)
index = nextIndex
try await Task.sleep(for: delay)
}
continuation.finish()
}
}
}
}
First, let's check it in a playground:
import Playgrounds
#Playground {
let json = #"{"name": "Trisha", "profile": "A playful and curious cat", "age": 8}"#
for try await catProfile in CatProfile.streamResponse(from: json) {
print(catProfile)
}
}
When you run it, you'll see the stream in action:
You might consider adding Encodable
conformance to avoid hard-coding JSON. The model from LanguageModelSession
generates Generable
properties in the order they're declared. But JSONEncoder
supports only .sortedKeys
output formatting. That would change the on-screen appearance order, so it's not ideal for previews.
To use this extension in a preview, we can write a wrapper view to handle the stream response:
private struct WrapperView: View {
@State private var catProfile: CatProfile.PartiallyGenerated?
var body: some View {
ZStack {
if let catProfile {
CatProfileView(catProfile: catProfile)
}
}
.task {
do {
let json = #"{"name": "Trisha", "profile": "A playful and curious cat", "age": 8}"#
for try await catProfile in CatProfile.streamResponse(from: json) {
self.catProfile = catProfile
}
} catch {
print("Error generating cat profile: \(error)")
}
}
}
}
#Preview("Stream") {
WrapperView()
}
Now we can inspect the stream response in a preview:

I also explored a more convenient approach (a custom modifier or an @Previewable
macro), but ran into persistent type-mismatch errors. Still, this approach works as expected without calling or waiting for a real model session.
Conclusion
The Foundation Models framework changes how we build SwiftUI views. With partially generated content, you can preview intermediate UI states. We can also use Xcode Previews or snapshot tests to validate layout. While this post's approach requires some boilerplate, it provides a practical way to test AI-generated content.
The final example is available on GitHub. Thanks for reading!