A checkpoint is a small Codable record. It names the workflow, the current step, the inputs you still need, and the outputs you already have. Write it after every successful step, before you start the next model call.
import Foundation
import FoundationModels
enum WorkflowStep: String, Codable {
case plan
case gather
case decide
case act
case done
}
struct WorkflowCheckpoint: Codable, Sendable {
var id: UUID
var step: WorkflowStep
var userPrompt: String
var plan: String?
var gathered: String?
var decision: String?
var transcriptData: Data?
var updatedAt: Date
}
actor CheckpointStore {
private let url: URL
init(url: URL) {
self.url = url
}
func save(_ checkpoint: WorkflowCheckpoint) throws {
let data = try JSONEncoder().encode(checkpoint)
try data.write(to: url, options: .atomic)
}
func load() throws -> WorkflowCheckpoint? {
guard FileManager.default.fileExists(atPath: url.path) else { return nil }
let data = try Data(contentsOf: url)
return try JSONDecoder().decode(WorkflowCheckpoint.self, from: data)
}
}
Save the file in the app container. Use an atomic write so a kill in the middle of a save does not leave half a JSON. Do not put secrets in the checkpoint if the file is later backed up. Redact first, as in Stop prompt attacks and leaks.
Store a serialized transcript next to the step fields when the next step still needs that conversation. The exact encode API for Transcript can vary by SDK. Confirm it in the Xcode 27 docs. If you cannot encode the transcript yet, store your own message list and rebuild instructions on resume.
Read Make several agents agree first, Talk to tools with MCP, and Stop prompt attacks and leaks if a saved step is a vote tally or a tool result.
Next, open that file and continue instead of starting over.
Key concepts
- A checkpoint holds id, step, prompt, plan, gathered facts, decision, and transcript data.
- Write after every successful step, before the next model call.
- Save JSON atomically in the app container.
- Redact before persist. Avoid secrets in backup-eligible files.
Takeaways
- A step is not done until the checkpoint file exists.
- Persist step fields and transcript when the next call needs conversation context.
- An atomic write prevents half JSON if the process dies mid-save.