Documentation Index
Fetch the complete documentation index at: https://docs.myme.so/llms.txt
Use this file to discover all available pages before exploring further.
SwiftUI reactive queries
On a pure-local or synced client,client.makeStore() returns a MarfaStore — a @MainActor @Observable factory for live queries driven by ModelContext.didSave with a short debounce window and a FetchDescriptor refetch per save.
ItemQuery—[Item]matching aListFiltersTypedItemQuery<T: MarfaItem>—[T]— e.g.TypedItemQuery<CoreNote>SingleItemQuery— one item by id;nilwhen purgedItemsWithMetadataQuery—[ItemWithMetadata]pairs — item plus its metadata row. Re-runs when either the items or item-metadata tables change. Use when a view needs per-item tags or favorite flags alongside the item (replaces hand-rolling a cache subscribed toSyncEngine.events).EdgesQuery— outbound edges for asourceId, optionally filtered byedgeType. A second initialiser takes only anedgeTypeto track all edges of a type across the local store —store.queryEdges(ofType: "in-thread")for taxonomy-style “every reply” surfaces.TagsQuery—[TagWithCount]for the whole tenant. Re-runs the tag aggregation when items or metadata change.BackrefsQuery—[String: [Edge]]keyed by target id. Re-runs when edges change.PendingMutationsQuery—[PendingMutationSummary]for the mutation queue. See Pending mutations.BlobUploadProgressQuery—[String: BlobUploadProgress]keyed by content hash. See Blob upload progress.
@Observable @MainActor. Views observe them directly — no ObservableObject, no manual Combine plumbing. Each query exposes its data property (items, item, edges, tags, or edgesByTarget), isLoading, and error. Call stop() to tear down the underlying observation.
ItemsWithMetadataQuery:
TagsQuery and ItemsWithMetadataQuery both re-run their aggregations on every relevant write. They’re tuned for UI-scale surfaces, not unbounded analytics.nil from makeStore() — the reactive layer only materialises when a local store is configured.
Pending mutations
store.queryPendingMutations() vends a PendingMutationsQuery over the local mutation queue — every write that’s been enqueued but not yet replayed to the server. Use it to render per-item “syncing” badges, queue-depth banners, or retry toasts without subscribing to SyncEngine.events and reimplementing the projection.
PendingMutationSummary with id, kind (e.g. .createItem, .uploadBlob, .setMetadata), itemId, createdAt, and a projected status:
.pending— queued, not yet attempted..inFlight— the sync engine is issuing the transport call right now..retrying(attemptCount: Int, lastError: String)— a transient failure, will retry on the next drain cycle.
ModelContext.didSave — enqueues, in-flight transitions, retries, and removals all land on the same observation path as every other reactive query. Returns nil on clients without a mutation queue (network-only mode).
Blob upload progress
store.queryBlobUploadProgress() vends a BlobUploadProgressQuery that projects the four .blobUpload* sync events into a dict keyed by content hash. Use it for global upload HUDs or per-attachment progress bars without reading the raw event stream.
.completed — apps wanting a “recently completed” fade should snapshot the final value in their own view layer. .failed entries linger so a consumer can render a retry affordance; a subsequent transient retry overwrites the entry with a fresh .uploading(0, total). Returns nil on clients without a sync engine (network-only and pure-local modes).
Sync engine
In synced mode,client.syncEngine is non-nil. Start it when your app is ready to sync:
- Watches
NWPathMonitorviaConnectionStateManagerand opens an SSE stream onGET /eventswhen the path goes online. - Applies SSE events into the local store. Persists
Last-Event-IDso reconnection resumes from the correct point. - Drains the
MutationQueueboth when the stream is idle and within a short debounce window of any new enqueue, replaying local writes against the server. The mutation queue captures per-callUpdateOptions.conflictso the chosen strategy applies on replay; the.callbackresolver closure is call-site-only and degrades to.autoon replay because closures aren’t serialisable. - On failure, records the attempt against the queue record and retries on the next reachability cycle.
stop() tears the engine down gracefully.
Proactive drain
The engine listens onMutationQueue.drainRequests and schedules a replay within drainDebounceInterval of each enqueue while the connection is online — a device that’s online but idle no longer holds pending writes until the next SSE reconnect. Offline, connecting, and syncing states short out: the queue sits silently, the post-SSE-close drain picks up, or an in-flight drain coalesces the burst respectively. Tune via SyncEngine.init(drainDebounceInterval:) (default .milliseconds(150)).
Sync events
Subscribe toclient.syncEngine?.events for typed sync activity:
.blobUpload* cases are the raw signal that BlobUploadProgressQuery projects into its observable dict — most apps subscribe to the query, not the events directly. .mutationDropped fires when a queued mutation exhausts its retries and is removed permanently.
Replaces the pattern of inferring sync state from ConnectionState transitions. Multi-subscriber — each access to events returns a fresh stream; past events are not replayed to late subscribers.
End-to-end subscription
Own the lifecycle from a single place — typically an@Observable service or a SwiftUI .task { } — so the subscriber task cancels when the view goes away and the sync engine stops when the app backgrounds.
ItemQuery, TagsQuery, BackrefsQuery) already update when SSE writes land in the local store, so most views don’t need to observe events directly. Subscribe when you need sync-lifecycle side effects — a “last synced” footer, a conflict toast, a sign-out-on-auth-failure hook.
See Realtime for the underlying SSE contract, event catalog, and Last-Event-ID semantics.
Local IDs (UUIDv7)
LocalStore.createItem mints a UUIDv7 by default — RFC 9562, timestamp-prefixed, globally unique. Pass CreateItemInput.id explicitly to override (still encouraged for callers that need to reference the new id before createItem returns; both paths produce server-compatible IDs).