Repository navigation
Releases: nesfeder/supabase-offline-kt
Releases · nesfeder/supabase-offline-kt
Release list
0.1.0
Immutable
release. Only release title and notes can be modified.
The first release.
Breaking
- The library is a supabase-kt plugin. Install it with
install(Offline) { … }in
createSupabaseClientand reach it assupabase.offline, wherecreateSupabaseOfflineClient(…)
returned a client; the settings ofSupabaseOfflineConfigare the plugin'sOffline.Config
(offlineStoragePathisdataDirectory, aString). The old names are gone; see Removed.
supabase-kt closes the plugin with the client, once it has begun closing its HTTP client: a call
made in between, or one whose request had not gone out, is still sent (Ktor can send while its
client finishes closing), or ends as it would offline (a read the cache can answer is answered
from it, a write the mutation queue holds isQueued) or with the closed error, never with a
cancellation; a request already on the wire still answers. Callsupabase.offline.close()first
to let the calls not yet sent go out. - A flow being collected when the client closes (
selectAsFlow,pendingMutations,
pendingMutationCountFlow,rejectedMutations) completes, where it failed with the client's
closed error, so a screen torn down at logout sees its flow end, not an error. A flow collected
afterclose()still fails with the closed error, as every other call does. - Logging goes through supabase-kt's own: the plugin logs to the client's logging processor
(defaultLoggingFactory) with the tagSupabase-Offline, atOffline.Config.logLevelor the
client'sdefaultLogLevel.OfflineLogger,OfflineLogLeveland theloggerandminLogLevel
settings are gone: configure logging once, increateSupabaseClient. MutationOutcome.Queuedis a class that carries the queued write as aPendingMutation: match it
withis MutationOutcome.Queued, notMutationOutcome.Queued ->.MutationOutcome.Appliedand
Queuedhave internal constructors.OfflinePostgrestQueryBuilderis a final class, as supabase-kt's builder is, andOfflineand
OfflineBucketApineed theOfflineInternalopt-in to implement. A test double comes from
supabase-offline-kt-testing.RefreshState.InFlightisRefreshState.Refreshing, andRefreshState.Pausedis new: an
exhaustivewhenoverRefreshStateneeds a branch for it.ReadStrategy.NetworkFirst,CacheFirstandCacheOnlytakemaxAge, notcacheDuration, and
are no longer data classes.FlowReadStrategyandMutationStrategyare sealed interfaces: their
cases are written as before, butentries,values()andvalueOfare gone.- A
CacheOnlyread that finds nothing it may serve throwsCacheMissException, not
NoSuchElementException. AQueueIfOfflinewrite the queue cannot hold (a dry run, or one whose
request could not be inspected to tell; a filter it cannot record; an unknown user; another
schema; a write on its way into the queue while local data was cleared) that cannot be sent for
want of a connection throwsMutationNotQueueableException, with that failure as its cause,
instead of the failure itself or anIOException.isNetworkOrConnectivityFailure()is still
truefor it. - The testing hook's opt-in marker
InternalOfflineTestingApiisOfflineInternal. syncPendingMutations(table: String? = null)returns aSyncReport(what it sent, what the
server refused, what is still queued) in place of a count, and replaces
syncAllPendingMutations(). It no longer throws a failure that stopped the pass: the report says
what is still queued andsyncStatus.value.lastSyncErrorwhy. When the library is sending the
same writes already, it waits for that and then sends what is left.pendingMutationCountandpendingMutationCountFloweach take onetable: String? = nullin
place of two overloads: calls compile as before, compiled callers must be rebuilt. They, and the
new queue and rejected-list members, show the current user's writes only, and none while the
stored session cannot be read (they showed every user's then).selectAsFlowemits aQuerySnapshotin place ofOfflineReadResult, withisFromCache,
fetchedAtandhasPendingMutationsbesideresultandrefresh; it is no longer constructed
by an app.autoSyncis on by default and does what it says: the library sends queued writes by itself
when the session settles into a user with writes queued, and when the network comes back. Set
autoSync = falseto leave those passes to the app; a write queued behind an earlier one is
sent by itself either way.- On Android the library's manifest declares
android.permission.ACCESS_NETWORK_STATE, for its
connectivity monitor: it appears in the merged manifest of every app that uses the library. - A test builds its client as an app does, with
install(Offline) { inMemory(clock = …) }from
supabase-offline-kt-testing;buildInMemorySupabaseOfflineClientis gone (see Removed). The
client no longer runs in the test's own scope: its background work runs on the library's, and a
failure there is logged, never thrown into the test. ReadStrategy.NetworkFirst,CacheFirstandCacheOnlytake aDurationonly: the
constructors taking milliseconds are gone, andNetworkFirst(null)now compiles.- The Storage file helpers extend
OfflineBucketApi, the bucketoffline.storage.from(…)returns,
in place of supabase-kt'sBucketApi: on AndroidensureLocalFilereturns aFileand
ensureLocalUriaUri, on iOSensureLocalPathaString, none of them nullable any more, and
each takespathas the bucket's own members do.localUriIfCachedandlocalPathIfCachedare
nullonly when nothing is cached. On supabase-kt's own bucket they no longer compile, where
they returnednull. - A read, an update or a delete with two top-level filters on one column, such as
filter { gte("age", 1); lte("age", 10) }, throwsIllegalArgumentExceptionbefore anything is
sent or queued. supabase-kt sends only the first of them, so the read showed rows outside the
window and the write reached them, and a queued write replayed that way later. Write the two as
oneand { }group, which sends both. - A read, an update or a delete whose pattern list (
likeAll,likeAny,ilikeAll,ilikeAny)
is empty, or holds a pattern that is empty,NULL, edged with white space or holding a brace
(and none of,.:()), throwsIllegalArgumentExceptionbefore anything is sent or queued, at
the top level or in a group. supabase-kt sends such a pattern unquoted, so the server read another
list:likeAll("code", listOf(""))went out aslike(all).{}, which matches every row. - On iOS the database and the file cache live in
Library/Application Supportby default
(supabase_offline.dbandsupabase_offline/), not inDocuments, which an app with file
sharing on shows in the Files app. A client that finds a database inDocumentsmoves it, with
its unsent writes and its cached files, when it opens; while another client holds it, it stays
there and that client's claim fails the open, and when both places hold a database the one in
Documentsis left and a warning logged. - The iOS framework carries no Swift-only helpers and no SKIE-generated Swift (see Removed). The
Swift package reaches it through a bridge of its own. - On Android, an interface member with a body compiles to a JVM default method only, so the
$DefaultImplsclasses are gone (-jvm-default=no-compatibility). SyncStatus.lastSyncErroris aSyncFailedException?, which is all it ever held: read its
httpStatusandcodewithout a cast. The testing artifact'ssyncStatus(lastSyncError = …)
takes one.syncPendingMutations(table)no longer sends a table's writes ahead of an earlier write of the
same user to another table: it stops at the first write with an earlier one still queued
elsewhere, andSyncReport.heldBehindOtherTablesCountcounts what it held back. Sync every table
to send them in order.- On Swift, the requirement of
OfflineFilterValueisofflineFilterText, where it was
filterText, a name that showed in completion onString,Int,Date,UUIDand every other
standard type that conforms, and could collide with one an app declares. A type of your own that
conforms declaresvar offlineFilterText: String.
Added
- Built and tested against supabase-kt 3.6 (
supabase-bomin the version catalog), with Ktor 3.4.3
and kotlinx.coroutines 1.10.2, the versions it was released with. Not 3.8: supabase-kt 3.7.0 and
3.8.0 carry a Realtime crash and a protocol-V2 leave defect, fixed upstream but not yet released,
and this library's pin sets an app's Realtime too; it moves to the first release with both fixes.
On 3.6 there is nodryRun(),maybeSingle(),isdistinctfilter orpurgeCache(write a dry
run by addingPrefer: tx=rollbackto the request), and a Storage object name with%,?or
#is downloaded as supabase-kt downloads it but never cached, since supabase-kt 3.6 puts it in
the URL unencoded and can fetch a different object (see the compatibility page). Each minor
release of this library names exactly one supabase-kt minor: the artifact compiles supabase-kt's
inline functions into itself and implements itsBucketApi, which gains members in minor
releases, so a range cannot be promised. When an app resolves another supabase-kt minor, the
plugin logs one warning when it is installed, naming both versions, and carries on; a patch
difference is no warning. - The
Offlineplugin for supabase-kt:install(Offline) { … }andsupabase.offline, with its
settings inOffline.Config: the two cache sizes,dataDirectory,primaryKeys(each table's
key,idunless set;primaryKey(table, columns)),responseTrust(ResponseTrust, with the
earlier rule asAuthenticatedSessionOnly),logLevel,networkAvailability, the app's own
connectivity signal in place of the library's...