top of page

Codex SwiftUI Skill: Does Every Edit Need a Save Button? How the CFA AI Toolkit Handles Inline Edits and Failed Saves

10 hours ago
6 min read

The short answer

No, not every edit needs a Save button. When Codex writes SwiftUI screens with the free, open-source CFA AI Toolkit for iOS, an inline editable row saves each valid change on its own, with no extra Save button squeezed into the row. A dedicated editing screen or sheet can still hold a draft with Cancel and Save. Either way, the toolkit's rules say the same thing about failure: keep the user's change, say clearly that it wasn't saved, offer Retry, and never show an unsaved edit as saved.

💡 What this article covers

What CFA is, when a Save button belongs and when it doesn't, what happens when a save fails, how the toolkit stops double-tapped saves, what your AI should test, and how to install the toolkit for Codex or Claude Code.


What is CFA?

CFA stands for Cooperative Feature Architecture. It's a basic skeleton for an iOS app in Xcode, a blueprint rather than a library, built only on Apple's frameworks such as SwiftUI, SwiftData and Swift Concurrency. Views draw the screen, ViewModels hold one screen's state (including drafts and error messages), and Features own the app's rules and shared data. You build your own app on top of it.

The CFA AI Toolkit packages that skeleton as AI skills, Xcode templates, starter apps and local tools, so your AI builds and changes iOS apps the same way every time. It's MIT-licensed and free.


Why do AI-written SwiftUI screens get editing wrong?

Ask an AI for an editable list and you'll often get one of three habits. It adds a tiny Save button to every row, which is an extra confirmation step nobody asked for. It fades or disables the whole screen while each save runs, so typing stalls. Or it catches the error, prints it, and carries on as if the change was saved. The toolkit's decision note on keeping edits visible and failures honest names all three as problems, because the last one quietly tells your user something false.


When should there be a Save button?

The toolkit's Swift coding guide draws a simple line:

  • Inline editing saves directly. An editable row changes the existing value. Each valid edit is persisted automatically through the screen's ViewModel and its Feature, with no separate Save button on the row.

  • Dedicated editors may have Save and Cancel. A separate editing screen or sheet can deliberately hold a draft and offer Cancel and Save in the navigation bar.

  • Your existing app's behaviour comes first. When you're maintaining an app that already has an approved way of editing, the decision note says to keep it. A destructive or transactional editor can also legitimately need explicit confirmation.


What happens when a save fails?

This is the honest part, and it's the same for both kinds of editing. The guide asks the AI to:

  • Keep typing responsive while the save runs, instead of dimming or locking the screen.

  • Keep the latest value. Writes are serialised or coalesced, so an older save finishing late can't overwrite a newer edit.

  • Protect saved data from half-typed input. An empty or invalid in-between value must not erase what's already saved.

  • Keep the draft on failure, point out which change wasn't saved, and offer Retry.

  • Never claim success. A change is shown as saved only after the real save succeeds.

The guide also says durable edits carry on after the user navigates away, so leaving a screen doesn't silently drop a save that was already under way. And it rules out the shortcut of replacing a force unwrap with fatalError or a fake success just to make a warning go away.


How does the toolkit stop double-tapped saves?

For a dedicated editor, the coding guide shows the pattern it wants your AI to follow. The View just forwards the tap, and the ViewModel owns one save task:

// Dedicated editor: describe the Save control and forward intent.
Button("Save") { _ = viewModel.save() }

// ViewModel: own the event task and presentation transitions.
func save() -> Task<Void, Never> {
    if let saveTask { return saveTask }
    let draftToSave = draft
    let task = Task {
        defer { saveTask = nil }
        do {
            try await feature.save(draftToSave)
            message = "Saved."
        } catch {
            self.error = error.localizedDescription
        }
    }
    saveTask = task
    return task
}

A second tap while a save is running gets the same task back, so the draft isn't saved twice. "Saved." only appears after the Feature's save succeeds, and if it throws, the ViewModel shows the error while the draft stays in place. Tests can wait for the result with await viewModel.save().value. The guide adds that if the user changes the input during a save, that new edit should be queued rather than thrown away as a duplicate, and the business rules stay in the Feature, not the screen.


What should my AI test?

The guidance asks for behaviour tests in the screen's ViewModel tests: rapid edits, invalid in-between input, a failed save followed by Retry, and input boundaries. The decision note puts it plainly: test behaviour, not just the absence of a Save label. Check that the latest edit survives and that controls stay usable with larger text.

Related to that, the guide asks for custom controls with at least a 48-by-48-point tap area, and for controls to move onto a second line when a row gets cramped at larger Dynamic Type sizes. The decision note is careful to call 48 points CFA's own preference, not an official Apple number.


How do I ask Codex to do this?

Once the toolkit is installed, ask in plain words, for example: "Use the CFA App Creation skill. Make the rows in this list editable inline, with no Save button per row. If a save fails, keep my edit, tell me it wasn't saved and offer Retry. Add ViewModel tests for rapid edits and a failed save." For an existing app, use the CFA Architecture Adoption skill and say whether you want to keep your current editing flow.

If you want to know why the toolkit works this way, it ships offline decision notes with the skills, and there's a public explanation on our site: Why use recoverable editing workflows in our code?


How do I install it?

The toolkit's README documents two routes. With Codex, download and extract the plugin release, then double-click Install.command (or run node scripts/install.mjs). The Codex plugin install also gives you the cfa command-line tool. With Claude Code, copy the skill folders into its skills folder. From the downloaded repository, run:

node scripts/install.mjs --mode skills --skills-directory ~/.claude/skills

Then restart Claude Code or start a new conversation. Other AI coding tools that read portable skill folders work the same way, using their own skills folder. A skills-folder-only install may not include the cfa command, but the editing guidance lives inside the skills' own reference files, so it travels with them.


Key takeaways

  • Inline edits in a CFA app save each valid change on their own, with no Save button per row.

  • A dedicated editor screen or sheet may keep a draft with Cancel and Save.

  • A failed save keeps the draft, says it wasn't saved and offers Retry, and the app never fakes success.

  • The ViewModel owns one save task, so double taps don't save twice and tests can await the result.

  • Your AI should test rapid edits, invalid input and failed saves, not just whether a Save label exists.


Is the CFA AI Toolkit free?

Yes. It's open source under the MIT licence, so you can use, change and share it, including in commercial apps. We're working towards a soft launch of 3DaysOfSwiftConcurrency.com, with a target of 1 December 2026, though that date isn't guaranteed.


Related articles


About Us

3 Days of Swift Concurrency

3 Days of Swift Concurrency is created by Matthew Thomas, an iOS developer building in public since 2022. He publishes the free, MIT-licensed CFA AI Toolkit for iOS and reference apps on GitHub. Visit 3DaysOfSwiftConcurrency.com to follow along. The paid training programme is still being built: a soft launch is targeted for 1 December 2026, but that date depends on deadlines being met and isn't guaranteed.

Comments


Commenting on this post isn't available anymore. Contact the site owner for more info.
bottom of page