Swift guide
Query data, manage transactions, select extensions, and restore backups in Apple apps.
These recipes use an open db from the Swift quickstart. Import Oliphaunt and call database methods from an async context.
Query application data
try await db.execute("""
CREATE TABLE IF NOT EXISTS notes (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
body text NOT NULL
)
""")
let inserted = try await db.query(
"INSERT INTO notes (body) VALUES ($1) RETURNING id",
parameters: [.string("First note")]
)
let id: Int64? = try inserted.rows[0].value(named: "id")Parameters bind values without SQL interpolation. Decode a result using a Swift type compatible with its PostgreSQL column type. SQL null becomes an optional value.
Run a transaction
try await db.transaction { tx in
try await tx.execute(
"INSERT INTO notes (body) VALUES ($1)",
parameters: [.string("First")]
)
try await tx.execute(
"INSERT INTO notes (body) VALUES ($1)",
parameters: [.string("Second")]
)
}Returning commits and throwing rolls back. Use tx for every statement in the callback. Do not call the outer database or issue manual transaction-lifecycle commands. Use tx.rollback() for explicit rollback; savepoints are supported.
Choose process isolation
The quickstart uses direct mode. For a separate database process, follow mobile broker setup. Broker mode requires its own storage and restore path; use the recipes below for direct mode.
Back up and restore
Direct mode stays bound to one database root and configuration for the lifetime of the application process. Closing a handle does not let that process switch to another root. Prepare the restored data, then use it on the next launch.
Use archive bytes for export, not a live directory copy:
let archive = try await db.backup()
try await db.close()
try await OliphauntDatabase.restore(destination: restoredURL, bytes: archive)On a subsequent application launch, open the restored destination:
let restored = try await OliphauntDatabase.open(
configuration: OliphauntConfiguration(storage: .directory(restoredURL))
)
try await restored.close()Here restoredURL is an app-owned file URL for a new or empty directory. Restore rejects nonempty destinations. Reapply the original database's required extension selection when reopening. Physical backups require a compatible native runtime.
Add an extension
The base Swift package is extension-free. Add the generated product to your application before selecting it at open.
- Install Bun, then obtain the Swift extension generator from the Oliphaunt source package matching your Swift dependency.
- Download the matching
vectorSwift extension carrier JSON from its release. - Generate a local Swift package with the command below, replacing the two input paths.
bun /path/to/oliphaunt/src/native/sdks/swift/tools/render-extension-products.mts \
--extension-carrier /path/to/oliphaunt-extension-vector-0.3.0-swift-extension-carrier.json \
--extensions vector \
--output-dir ./OliphauntExtensionsThe output directory must not already exist. Add it to Xcode as a local package and link the generated OliphauntExtensionVector product to your app. Select its resource when opening:
import OliphauntExtensionVector
let db = try await OliphauntDatabase.open(
configuration: OliphauntConfiguration(extensions: [OliphauntExtensionVector.resource])
)
try await db.execute("CREATE EXTENSION IF NOT EXISTS vector")For multiple extensions, supply each required carrier with another --extension-carrier option and list the SQL names in --extensions. The generated package includes their required dependencies. Check the catalog before selecting an extension.
Applications that need ICU collations also link the OliphauntICU SwiftPM product. Choose the ICU seed when creating a new iOS database that uses ICU collations.
Cancel and close
Cancelling a Swift task alone does not interrupt PostgreSQL. Use try await db.cancel() to request interruption, then await and handle the database operation's outcome.
Close explicitly with try await db.close(). Close rejects new work and drains earlier accepted work. A shutdown failure leaves the handle terminal. Keep UI updates on the main actor and let the SDK run database work on its owner queue.
A transaction callback error is rethrown after successful rollback. If rollback or independent database recovery also fails, the SDK exposes both causes; see errors.
Troubleshooting
| Symptom | Check |
|---|---|
| Invalid storage URL | Use an app-owned file URL, not a remote URL |
| Missing extension | Link and register its generated Swift product before open |
| Missing runtime resources | The app target links Oliphaunt and includes its resolved framework |
| Root already in use | Keep one owner and close it before reopening |
| Background writes are interrupted | Keep transactions short and follow mobile lifecycle guidance |