PowerSync Integration
ZyraForm integrates seamlessly with PowerSync for offline-first database operations. Load existing records directly into forms and sync changes automatically.
Loading Records into Forms
Section titled “Loading Records into Forms”Load from PowerSync Service
Section titled “Load from PowerSync Service”struct EditUserFormView: View { @StateObject var form = PowerSyncForm<UserFormValues>(schema: UsersSchema) @StateObject var service = GenericPowerSyncService( tableName: "\(AppConfig.dbPrefix)users", userId: currentUserId )
let recordId: String
var body: some View { Form { // Form fields... } .task { do { try await form.loadFromPowerSync( recordId: recordId, service: service ) } catch { print("Failed to load record: \(error)") } } }}Load Using Table Name and User ID
Section titled “Load Using Table Name and User ID”.task { do { try await form.loadFromPowerSync( recordId: recordId, tableName: "\(AppConfig.dbPrefix)users", userId: currentUserId ) } catch { print("Failed to load record: \(error)") }}Load from Already-Fetched Record
Section titled “Load from Already-Fetched Record”struct EditUserFormViewFromRecord: View { @StateObject var form = PowerSyncForm<UserFormValues>(schema: UsersSchema)
let record: [String: Any] // Already loaded from PowerSync
var body: some View { Form { // Form fields... } .onAppear { form.loadFromRecord(record) } }}Complete Create/Edit Flow
Section titled “Complete Create/Edit Flow”Handle both create and edit scenarios:
struct UserFormView: View { @StateObject var form = PowerSyncForm<UserFormValues>(schema: UsersSchema) @StateObject var service = GenericPowerSyncService( tableName: "\(AppConfig.dbPrefix)users", userId: currentUserId )
let recordId: String? // nil for create, String for edit
var isEditing: Bool { recordId != nil }
var body: some View { Form { Section("User Information") { if let emailBinding = form.binding(for: "email") { TextField("Email", text: emailBinding) .textContentType(.emailAddress) }
if let nameBinding = form.binding(for: "name") { TextField("Name", text: nameBinding) } }
Section { Button(action: { form.handleSubmit { values in Task { if isEditing, let id = recordId { // Update existing record try await service.updateRecord(id: id, fields: [ "email": values.email, "name": values.name ]) } else { // Create new record let newId = try await service.createRecord(fields: [ "email": values.email, "name": values.name ]) print("Created record with ID: \(newId)") } } } }) { Text(isEditing ? "Save Changes" : "Create User") .frame(maxWidth: .infinity) } .disabled(!form.isValid || form.isSubmitting) } } .navigationTitle(isEditing ? "Edit User" : "New User") .task { // Load record if editing if let id = recordId { try? await form.loadFromPowerSync( recordId: id, service: service ) } } }}Loading with Field Selection
Section titled “Loading with Field Selection”Load only specific fields:
try await form.loadFromPowerSync( recordId: recordId, service: service, fields: ["email", "name", "age"] // Only load these fields)Updating Records
Section titled “Updating Records”After form submission, update PowerSync:
form.handleSubmit { values in Task { do { let config = UsersSchema.toTableFieldConfig()
try await service.updateRecord( id: recordId, fields: [ "email": values.email, "name": values.name, "age": values.age ?? NSNull() ], encryptedFields: config.encryptedFields )
print("✅ Record updated successfully") } catch { print("❌ Failed to update: \(error)") } }}Creating Records
Section titled “Creating Records”Create new records from form values:
form.handleSubmit { values in Task { do { let config = UsersSchema.toTableFieldConfig()
let newId = try await service.createRecord( fields: [ "email": values.email, "name": values.name, "age": values.age ?? NSNull() ], encryptedFields: config.encryptedFields )
print("✅ Created record with ID: \(newId)")
// Optionally load the new record into the form try await form.loadFromPowerSync( recordId: newId, service: service ) } catch { print("❌ Failed to create: \(error)") } }}PowerSync Schema Setup
Section titled “PowerSync Schema Setup”Ensure your PowerSync schema includes your tables:
let PowerSyncSchema = Schema( tables: [ UsersSchema.toPowerSyncTable(), ProjectsSchema.toPowerSyncTable(), // ... other tables ])
let db = PowerSyncDatabase( schema: PowerSyncSchema, dbFilename: "myApp-powersync.sqlite")Batch Operations
Section titled “Batch Operations”Load multiple records and populate forms:
struct UserListView: View { @StateObject var service = GenericPowerSyncService( tableName: "\(AppConfig.dbPrefix)users", userId: currentUserId )
@State private var selectedRecordId: String?
var body: some View { List { ForEach(service.records, id: \.self) { record in Button(action: { selectedRecordId = record["id"] as? String }) { Text(record["name"] as? String ?? "Unknown") } } } .sheet(item: $selectedRecordId) { id in EditUserFormView(recordId: id) } .task { let config = UsersSchema.toTableFieldConfig() try? await service.loadRecords( fields: config.allFields, encryptedFields: config.encryptedFields ) } }}Error Handling
Section titled “Error Handling”Handle loading errors gracefully:
.task { do { try await form.loadFromPowerSync( recordId: recordId, service: service ) } catch { if let nsError = error as NSError?, nsError.code == 404 { // Record not found print("Record not found") } else { // Other error print("Failed to load: \(error.localizedDescription)") } }}Syncing Changes
Section titled “Syncing Changes”PowerSync automatically syncs changes to Supabase:
// After updating via formtry await service.updateRecord(id: recordId, fields: [...])
// PowerSync automatically syncs to Supabase// No manual sync needed!Next Steps
Section titled “Next Steps”- CRUD Operations - Learn more about database operations
- Form Components - Build your form UI
- Schema Definition - Define your data structure