Repository navigation
Releases: withmarfa/marfa-swift
Release list
v0.0.23
Carries MarfaCore built from withmarfa/marfa@a8497e4.
v0.0.22
Carries MarfaCore built from withmarfa/marfa@05d14a9.
v0.0.21
Carries MarfaCore built from withmarfa/marfa@c00e0cf.
v0.0.20
Carries MarfaCore built from withmarfa/marfa@0620b2f.
v0.0.19
This release carries MarfaCore built from Marfa v0.0.11 (withmarfa/marfa@e56c09f66606e09f213eb0400bd251e0590025a2). It has one change to public API, and two additions to the generated wire types.
To update, pin 0.0.19. A server at Marfa v0.0.11 or later can list an edge type that no file writes, which the earlier core could not read, so an app talking to such a server needs this release.
Public API that changed or was removed
| Before | Now |
|---|---|
EdgeEnd (source, target) |
EdgeWrittenAt (source, target, none). none means no file writes the edge, as for in-folder. |
EdgeType.writtenAt: EdgeEnd |
EdgeType.writtenAt: EdgeWrittenAt. The EdgeType initializer takes writtenAt: EdgeWrittenAt, still .source by default. |
An app that switches over EdgeType.writtenAt needs a case for .none. An app that compares with == .source or == .target needs no change.
Public API added
MarfaCore.EdgeWrittenAt, the core binding's catalog type behindEdgeType.writtenAt, with the same three cases.Components.Schemas.RetentionPolicyinMarfaTypes, withfeedDays(a whole number of days, ornull): a type definition now carriesretention_policy, which says how long the server keeps the type's feed items.written_atinMarfaTypesgains the valuenone.
Behavior that follows from the server
These come from the server and need no code change. Marfa v0.0.11 checks each element of an array property against its items_type, so a write the earlier server accepted can now be refused invalid_properties; its source filter also narrows event streams and webhook deliveries. The Marfa v0.0.11 changelog entry has the list.
v0.0.18
This release carries MarfaCore built from Marfa v0.0.10 (withmarfa/marfa@63cb85eaa25613b447360d0b8ff65e086f236165). It has three changes to what an app writes or sees: properties cross as values, each changes() stream takes its own place in the core's watch, and a new LiveQuery keeps a SwiftUI view current. Two of them change public API, as listed below.
To update, pin 0.0.18 and change the calls in the first table below.
Public API that changed or was removed
These come from #327. The core now hands properties, bodies and answers across as JSON values, not JSON text. The records that only held them are the core's own types, and Marfa re-exports each name as a typealias, so import Marfa still names them.
| Before | Now |
|---|---|
Edit(.merge(x), baseVersion:) |
Edit(properties: x, baseVersion:) |
Edit(.replace(x), baseVersion:) |
Edit(properties: x, replaceProperties: true, baseVersion:) |
Edit.Properties |
Removed. Use Edit.properties (a JSONObject) and Edit.replaceProperties (a Bool, false by default). |
declareTypes(_ types: [String]) |
declareTypes(_ types: [JSONObject]). Pass a dictionary literal, such as ["id": "app.readinglist.entry", "fields": [...]], in place of a JSON string. |
declaredTypes() -> [String] |
declaredTypes() -> [JSONObject] |
QueuedWrite.answer: String? |
QueuedWrite.answer: JSONValue?. Read row.answer?.object?["item"] where you parsed the text. An answer that was not JSON, such as a gateway's page, arrives as .string. |
Item, Edge, SearchHit, BinPage, Draft, Edit, ListFilters, SearchFilters, Tier, ItemState as structs and enums of this package |
typealiases of the core's types. Initializers keep their labels, order and defaults. Their stored properties are var. Item and Edge are Identifiable. |
JSONValue and JSONObject as types of this package |
typealiases of the core's JsonValue and JsonObject. JSONMember is new. A JSONObject holds its pairs in members: [JSONMember], and JSONObject(members:) makes one. The cases, keys, values, lookup by key, the literals, Codable, init(json:) and json() are unchanged. |
New fields and behavior that follow from the same change:
Item.unreadablePropertiesandEdge.unreadableProperties(String?) are new. A row whose stored properties are not one JSON object no longer fails a whole list, search or edge read. It comes back with emptypropertiesandunreadablePropertiessaying why. Check it before you write back fromproperties, because the empty object is not what is stored. A list or search filtered on a property leaves such a row out. A write to that item or edge throws until a hydration replaces the row from the server.- A value the core cannot hold is refused
invalidbefore anything is queued. That is a number that is not finite, a key that repeats in one object, or a write nested deeper than the server reads. The server refuses a body nested more than 64 levels, so a value inside a property may nest 62 levels, and an extension's body 63 inside its own object. JSONObjectequality compares keys as Swift compares strings. Two keys that differ only in Unicode normalization compare equal with==, though a lookup by key still tells them apart by their UTF-8 bytes. A lookup scans the keys in order, and building or decoding an object takes time linear in its size.- A float with no fraction, such as
2.0, reads back as the integer2, as the server writes numbers.
Public API added
From #329. A LiveQuery reads rows or one item from a working copy and reads again when the copy's changes() says something that can alter the read, so a SwiftUI view stays current with no refresh code. It uses the Observation framework only; Marfa still does not import SwiftUI.
LiveQuery<Value: Sendable & Equatable>is a@MainActor @Observableclass. Create it on the main actor withLiveQuery(copy, reading:). It hascopy,reading(assigning it reads again at once),value,isLoading,error,followingandrefresh(). Releasing the query ends itschanges()stream.Reading<Value>says what to read:.list(_:sort:),.search(_:filters:limit:),.item(_:),.queue, andReading(placeholder:_:)for any other read.Followingis.live,.stopped(MarfaError)or.ended(MarfaError?)..stoppedmeans the follow of the server failed and the stream is still open..endedmeans the stream finished, andrefresh()asks the copy for a new one.
An app that kept its own refresh code can replace it with one LiveQuery for each read. It no longer lists, loops over changes() or reads again after a write. A query does not page: bound a result with ListFilters.limit and grow it by assigning reading.
Changes to changes() that need no code change
From #328, which hands the watch to the core. changes(), Change, hydrate, catchUp, queue.drain(), useKey(_:) and close() have the signatures they had.
- A held stream drains once after a drain your app started left a write undelivered, the next time the server is reached, and tells
.queueDrained. An app that drained again by hand after.serverReachablecan stop. A drain it starts still works. - A follow that ends on the server's own answer after an outage, such as a revoked key, tells
.serverReachablebefore.stopped, because the server answered. If your app treated.stoppedafter.serverUnreachableas "still offline", read.serverReachablefirst. .serverReachableafter a hydration, catch-up or drain can arrive after that call's own.refreshed(.hydrated)or.answered. Do not depend on the order of the two.- A stream the core ended on a fault, or refused a place in the watch, is told
.stoppedand then finishes, so afor awaitloop ends. Askchanges()for a new stream. A failed follow differs: the stream is told.stoppedand stays open, and the next hydration or catch-up restarts it. - A watch failure no longer outlives the last stream. The next
changes()starts a fresh follow at once and is not told the old.stopped. With a revoked key, each new stream asks the server again and is told the refusal again. - A stream added when none is held starts with no memory of an earlier outage. It is told
.serverUnreachableonly when its own follow cannot reach the server.
What Marfa v0.0.10 changes for an app
See the Marfa v0.0.10 entry in the changelog for the full list. The ones an app on this package notices:
- A listener is told
.serverUnreachableonce for each loss of the server and.serverReachableonce for each return. Any request that reaches the server ends an outage: the stream ending on the server's own answer, a hydration or catch-up that succeeded, or a drain that answered a write. - A hydration or catch-up no longer needs your listeners stopped around it. One called from inside a listener's change handler is refused
invalid. - A drain that left a write undelivered is finished by the first stream a watch has afterward, even after the app restarts.
- A write whose JSON body nests more than 64 levels is refused
invalidwhen it is queued, and nothing is queued. Before, a body only a little too deep was queued and left for the server to refuse. - A list or search filtered on properties no longer fails as a whole because one row's stored text is not JSON. That row matches no property condition,
not_existsincluded. - A link in a body written on another device keeps naming its item after that item is renamed, and after another item takes the old title.
v0.0.17
Carries MarfaCore built from withmarfa/marfa@75434a6.
v0.0.16
Carries MarfaCore built from withmarfa/marfa@f928fcb.
v0.0.15
Carries MarfaCore built from withmarfa/marfa@6f28298.
v0.0.14
Carries MarfaCore built from withmarfa/marfa@d162acd.