Swift Order Tutorial
Build understanding around one draft Order: load its complete item list, change Notebook quantity from 2 to 3, save the Order and line totals together, then read the result in a new process. Follow the recorded native SQLite application and learn why each query, expression and transaction boundary is needed. The version and publication boundaries below apply to this specific example.
This native SQLite tutorial and the separate lifecycle/soak receipts use the recorded published Swift 0.3.1. Retained-ID invalidation still uses the documented application adapter; existing generator deployment qualifications and the other runtimes’ pending-release markers remain unchanged.
1. Prepare the example
You need the Swift toolchain, the site checkout, and the retained built
swift-app-console workspace from the core Order receipt.
Read its build.toolchain, build.runtimeSource, build.runtimePackage and
verification.qualification entries for swift before selecting dependencies.
The commands below assume that recorded workspace and its dependencies already
exist; they are not a clean-machine bootstrap or a claim that every published
runtime passes. For build/replay preparation use the retained workspace instructions.
Start a new model with the Swift Quick Start, then use Order-scoped Assist for its generated Q/E/mutation methods. This page reuses the evaluated Order v1.0 model and the three-order fixture. Do not substitute the isolation/stock fixture: those are separately evaluated model revisions.
The complete application
contains imports, helpers, startup and all phase handlers. The excerpts below
are function-body excerpts from that application, not standalone files.
Keep Foundation, TeaQLCore, TeaQLSQLite and GeneratedTeaQL from the console. required, require, nullable, report and OrderError are application helpers. The complete file includes the request policy and retained-ID store used by its other phases.
2. Understand startup and run the first edit
The console creates the SQLiteDataService actor, installs GeneratedRuntimeModule.module into TeaQLRuntime, and passes the native query/mutation executors plus an application-owned RequestPolicy into UserContext. It explicitly awaits context.ensureSchema(GeneratedRuntimeModule.module) before normal phases.
Module installation registers metadata and behavior. Schema reconciliation is an explicit database operation. Keep startup/admin schema work separate from request execution, and obtain tenant/actor/policy from trusted server context.
From the site root, run this sequence with a new disposable database:
export ORDER_WORKSPACE="$PWD/artifacts/order/2026-10-27/swift/swift-app-console"
export ORDER_FIXTURE="$PWD/static/examples/order/fixture.json"
export ORDER_DATABASE="$(mktemp -d)/orders.sqlite"
cd "$ORDER_WORKSPACE"
for phase in bootstrap seed draft-graph note-value note-null note-empty note-not-loaded invalid-quantity draft-edit restart-readback; do
./.build/debug/TeaQLConsole "$phase"
done
Run seed once on the fresh database; the fixture program is not an import/upsert
service. Each phase starts a fresh process against the same absolute database
path. The two NotLoaded probes are intentional negative examples; their handler
reports the native error and finishes successfully.
| Stop and inspect | Expected result |
|---|---|
bootstrap | Explicit schema setup and ORDER_OK phase=bootstrap. |
seed and draft-graph | Three Orders, six items; ORD-2026-1001 is DRAFT with total 3850 USD cents. |
| Note phases | Value, SQL NULL, empty text and NotLoaded remain distinct. |
invalid-quantity | 0, -1, 1.5 and 101 rejected before save. |
draft-edit | Notebook quantity 3, Notebook line total 3750, Order total 5100. |
restart-readback | The same totals in a new process; Order and changed item version 2, untouched item version 1. |
The handlers print ORDER_CASE JSON and an ORDER_OK completion line. Find the
Order by business number and the line by SKU; generated numeric IDs are not
portable fixture keys. Compare the returned graph with these expected values
before adapting the operation to your own model.
3. Load the full Order before editing
let rows = try await Q.customerOrders().withOrderNumberIs("ORD-2026-1001")
.selectOrderItemListWith(Q.orderItems().orderBySkuCodeAscending().orderByIdAscending().limit(101)).limit(2)
.comment("Load full draft and bounded full items").purpose("Validate and edit one draft order").executeForList(context)
The outer bound of 2 lets the application reject a duplicate business identity. The child bound of 101 is an overflow sentinel for a supported maximum of 100 items. Require exactly one Order, DRAFT status and at most 100 loaded items before continuing. A one-item preview is useful for a list screen, but cannot supply the complete total for an edit. The full source performs these checks after the query; keep them when adapting the excerpt.
comment describes the operation and purpose records the business reason.
Both travel with execution through the trusted context. A buyer filter in this
v1.0 fixture is a business predicate; it does not authenticate or authorize a
buyer. Use the separate buyer/tenant isolation contract
when implementing protected access.
4. Validate money and save one atomic edit
The application validates integral quantity in [1, 100], USD currency, each stored
line amount and the complete new Order total. Prices and totals are integer cents.
It finds exactly one SKU-NOTEBOOK, computes 3 × 1250 + 3 × 450 = 5100, then
changes the selected item and Order together. The following write excerpt assumes
those validated, fully loaded variables; it is not a replacement for validation.
The audit reason is Edit draft Order quantity and totals atomically, named
EDIT in most examples and editReason/EditReason in Go, Swift and .NET.
let price = try required(E.orderItem(order.orderItemList[index]).unitPriceMinor().eval())
order.orderItemList[index].updateQuantity(Int64(quantity)); order.orderItemList[index].updateLineTotalMinor(Int64(quantity) * price)
order.updateTotalMinor(total); print("ORDER_GRAPH_ATTEMPT")
_ = try await order.auditAs(editReason).save(context)
Generated entities have value semantics. Update order.orderItemList[index] in the parent value you will save; changing an unrelated local copy is insufficient. The edit uses reporting-overflow arithmetic. Await saves and child Tasks before letting the provider’s owning scope end.
A successful audited save returns the persisted typed entity. The core example reads again after mutation, rather than treating a pre-save object as independent evidence of committed state. A rejected stale write requires reloading authoritative state and a business decision before retrying; it is not permission to overwrite the winner or retry every provider error.
5. Use projections, nulls and pages deliberately
| Task | Swift API in this example | Result / responsibility |
|---|---|---|
| Execute a bounded query | try await query.executeForList(context) | Typed SmartList; preserve native errors and check cardinality. |
| Read selected money | try E.customerOrder(order).totalMinor().eval() | Loaded integer cents; not a formatting or currency conversion API. |
| Access selected items | order.orderItemList | Only the relation that was explicitly loaded. |
| Default a selected SQL NULL note | try orderNote().orElse("No note") | Null-only fallback; an empty string stays empty and NotLoaded still reports a missing field. |
| Save an audited mutation | try await order.auditAs(editReason).save(context) | Persisted typed entity; the required transaction includes all changed entities. |
| Request an exact page total | offset(offset).limit(limit).count() → page.totalCount | Total of the filtered set, independent of page length. |
The note accessors in the table are called on the corresponding E expression.
required, report, pageRows, load, edit and prepare_edit are application
helpers where present; they are not additional generated terminal methods.
| Note state | Expected behavior |
|---|---|
| Selected value | Leave at reception stays that value. |
| Selected SQL NULL | A null-only default produces No note. |
| Selected empty text | Remains the empty string. |
| Omitted field | Native NotLoaded diagnostic, including when a null-only default is attempted. |
For a read-only list screen, the existing bounded-page handler validates
offset >= 0 and 1 <= limit <= 3, sets a stable total/ID order and explicitly
selects one item preview per Order. With validated offset, limit and
buyer, its native query is:
let page=try await Q.customerOrders().withBuyerReferenceIs(buyer).orderByTotalMinorAscending().orderByIdAscending()
.selectOrderItemListWith(Q.orderItems().orderBySkuCodeAscending().orderByIdAscending().limit(1)).offset(offset).limit(limit).count()
.comment("Read bounded Order pages").purpose("Compare filtered totals and per-order item previews").executeForList(context)
This query only reads. Do not save its one-item preview as if it were a full
Order edit or infer deletion of omitted items. The pagination fixture has eight
matching BUYER-EXPORT Orders, whereas the first-edit fixture above has three
Orders overall. See the page API decision guide
for fixture selection, continuous export, retained-ID jumps and aggregate results.
6. Diagnose a failure before changing code
A changed local child copy can leave the saved parent unchanged without a SQL failure. First check which parent value contains the mutation, then inspect the generated audited save. A Node/HTTP example is not evidence for Apple UI lifecycle behavior.
| Symptom | First action |
|---|---|
| Workspace or package missing | Confirm the retained artifact prerequisite; follow the Quick Start for fresh generation instead of assuming this checkout ships compiled outputs. |
| Empty Order or duplicate row | Confirm absolute DB path, fixture and business number; preserve the cardinality check. |
| Wrong total after edit | Check complete item loading and integer validation before the audited save. |
| Missing field error | Reload the field/relation explicitly; do not substitute a null default. |
| Version conflict | Reload authoritative state and decide whether the requested edit is still valid. |
| Busy/locked, commit or cancellation failure | Inspect native provider error and transaction boundary; follow the Swift Order troubleshooting table. |
Continue with the Order API usage guide, Swift advanced data operations, version and qualification table and documentation coverage matrix. The School regression baseline and all prior Order receipts remain unchanged.
Continue with advanced Order writes
Follow Swift Advanced Order Mutations for explicit line deletion, legal state transitions and stale-version recovery. Each recipe explains its starting state, transaction boundary, native errors and independent readback using the existing qualified example. Start a fresh recipe database; do not reuse the quantity-edited database above.
Continue with Order queries
Use Swift Order Queries for continuous export, retained-ID page jumps and provider summaries. The query recipes use separate nine-/ten-order datasets; keep the fixture and context/store lifetime with the operation you are learning.
Continue with buyer and tenant isolation
Use Swift Order Isolation to upgrade the model to v1.1 and build root/buyer scope from an application-owned trusted session.