Skip to content

Commit 5d9290f

Browse files
committed
wip
1 parent 53fb7fb commit 5d9290f

2 files changed

Lines changed: 51 additions & 50 deletions

File tree

Sources/SharingGRDBCore/Documentation.docc/Articles/CloudKit.md

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -131,18 +131,18 @@ version.
131131

132132
#### Primary keys
133133

134-
> TLDR: Primary keys should be globally unique identifiers, such as UUID. We further recommend
135-
> specifying a "NOT NULL" constraint with a "ON CONFLICT REPLACE" action.
134+
> TL;DR: Primary keys should be globally unique identifiers, such as UUID. We further recommend
135+
> specifying a `NOT NULL` constraint with a `ON CONFLICT REPLACE` action.
136136
137137
Primary keys are an important concept in SQL schema design, and SQLite makes it easy to add a
138-
primary key by using an "autoincrement" integer. This makes it so that newly inserted rows get
138+
primary key by using an `AUTOINCREMENT` integer. This makes it so that newly inserted rows get
139139
a unique ID by simply adding 1 to the largest ID in the table. However, that does not play nicely
140140
with distributed schemas. That would make it possible for two devices to create a record with
141141
`id: 1`, and when those records synchronize there would be an irreconcilable conflict.
142142

143143
For this reason, primary keys in SQLite tables should be globally unique, such as a UUID. The
144-
easiest way to do this is to store your table's ID in a "TEXT" column, adding a
145-
default with a freshly generated UUID, and further adding a "ON CONFLICT REPLACE" constraint:
144+
easiest way to do this is to store your table's ID in a `TEXT` column, adding a
145+
default with a freshly generated UUID, and further adding a `ON CONFLICT REPLACE` constraint:
146146

147147
```sql
148148
CREATE TABLE "reminders" (
@@ -151,7 +151,7 @@ CREATE TABLE "reminders" (
151151
)
152152
```
153153

154-
> Tip: The "ON CONFLICT REPLACE" clause must be placed directly after "NOT NULL".
154+
> Tip: The `ON CONFLICT REPLACE` clause must be placed directly after `NOT NULL`.
155155
156156
This allows you to insert a row with a NULL value for the primary key and SQLite will compute
157157
the primary key from the default value specified. This kind of pattern is commonly used with the
@@ -160,10 +160,10 @@ the primary key from the default value specified. This kind of pattern is common
160160
```swift
161161
try database.write { db in
162162
try Reminder.upsert {
163-
// Do not provide 'id', let database initialize it for you.
164-
Reminder.Draft(title: "Get milk")
165-
}
166-
.execute(db)
163+
// Do not provide 'id', let database initialize it for you.
164+
Reminder.Draft(title: "Get milk")
165+
}
166+
.execute(db)
167167
}
168168
```
169169

@@ -182,7 +182,7 @@ CREATE TABLE "reminders" (
182182

183183
#### Primary keys on every table
184184

185-
> TLDR: Each synchronized table must have a single, non-compound primary key to aid in
185+
> TL;DR: Each synchronized table must have a single, non-compound primary key to aid in
186186
> synchronization, even if it is not used by your app.
187187
188188
_Every_ table being synchronized must have a single primary key and cannot have compound primary
@@ -206,7 +206,7 @@ TODO: think more about this
206206
207207
#### Default values for columns
208208
209-
> TLDR: All columns must have a default in order to allow for multiple devices to run your
209+
> TL;DR: All columns must have a default in order to allow for multiple devices to run your
210210
> app with different versions of the schema.
211211
212212
Your tables' schemas should be defined to provide a default for every non-null column. To see why
@@ -221,7 +221,7 @@ a ``NonNullColumnMustHaveDefault`` error will be thrown.
221221
222222
#### Unique constraints
223223
224-
> TLDR: SQLite tables cannot have "UNIQUE" constraints on their columns in order to allow
224+
> TL;DR: SQLite tables cannot have `UNIQUE` constraints on their columns in order to allow
225225
> for distributed creation of records.
226226
227227
Tables with unique constraints on their columns, other than on the primary key, cannot be
@@ -238,7 +238,7 @@ when a ``SyncEngine`` is first created. If a uniqueness constraint is detected a
238238

239239
#### Foreign key relationships
240240

241-
> TLDR: Foreign key constraints can be enabled and you can use "ON DELETE" actions to
241+
> TL;DR: Foreign key constraints can be enabled and you can use `ON DELETE` actions to
242242
> cascade deletions.
243243
244244
SharingGRDB can synchronize many-to-one and many-to-many relationships to CloudKit,
@@ -248,13 +248,13 @@ such as receiving a child record before its parent, the sync engine will cache t
248248
until the parent record has been synchronized, at which point the child record will also be
249249
synchronized.
250250

251-
Currently the only actions supported for "ON DELETE" are "CASCADE", "SET NULL" and "SET DEFAULT".
252-
In particular, "RESTRICT" and "NO ACTION" are not supported, and if you try to use those actions
253-
in your schema an ``InvalidParentForeignKey`` error will be thrown when constructing ``SyncEngine``.
251+
Currently the only actions supported for `ON DELETE` are `CASCADE`, `SET NULL` and `SET DEFAULT`.
252+
In particular, `RESTRICT` and `NO ACTION` are not supported, and if you try to use those actions
253+
in your schema an error will be thrown when constructing ``SyncEngine``.
254254

255255
## Record conflicts
256256

257-
> TLDR: Conflicts are handled automatically using a "last edit wins" strategy for each
257+
> TL;DR: Conflicts are handled automatically using a "last edit wins" strategy for each
258258
> column of the record.
259259
260260
Conflicts between record edits will inevitably happen, and it's just a fact of dealing with
@@ -269,7 +269,7 @@ the only strategy available and we feel serves the needs of the most number of p
269269

270270
## Backwards compatible migrations
271271

272-
> TLDR: Database migrations should be done carefully and with full backwards compatibility
272+
> TL;DR: Database migrations should be done carefully and with full backwards compatibility
273273
> in mind in order to support multiple devices running with different schema versions.
274274
275275
Migrations of a distributed schema come with even more complications than what is mentioned above.
@@ -286,9 +286,9 @@ has been added to the schema, it will populate the table with the cached records
286286

287287
#### Adding columns
288288

289-
> TLDR: When adding columns to a table that has already been deployed to user's devices, you will
290-
either need to make the column nullable, or it can be "NOT NULL" but a default value must be
291-
provided with an "ON CONFLICT REPLACE" clause.
289+
> TL;DR: When adding columns to a table that has already been deployed to user's devices, you will
290+
either need to make the column nullable, or it can be `NOT NULL` but a default value must be
291+
provided with an `ON CONFLICT REPLACE` clause.
292292

293293
As an example, suppose the 1.0 of your app shipped a table for a reminders list:
294294

@@ -338,18 +338,18 @@ VALUES
338338
(NULL, 'Personal', NULL)
339339
```
340340

341-
This will generate a SQL error because the "position" column was declared as "NOT NULL", and so this
341+
This will generate a SQL error because the "position" column was declared as `NOT NULL`, and so this
342342
record will not properly synchronize to devices running a newer version of the app.
343343

344-
The fix is to allow for inserting "NULL" values into "NOT NULL" columns by using the default of the
344+
The fix is to allow for inserting `NULL` values into `NOT NULL` columns by using the default of the
345345
column. This can be done like so:
346346

347347
```sql
348348
ALTER TABLE "remindersLists"
349349
ADD COLUMN "position" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0
350350
```
351351

352-
> Important: The "ON CONFLICT REPLACE" clause must come directly after "NOT NULL" because it
352+
> Important: The `ON CONFLICT REPLACE` clause must come directly after `NOT NULL` because it
353353
> modifies that constraint.
354354
355355
Now when this query is executed:
@@ -361,7 +361,7 @@ VALUES
361361
(NULL, 'Personal', NULL)
362362
```
363363

364-
…it will use 0 for the "position" column.
364+
…it will use 0 for the `position` column.
365365

366366
Sometimes it is not possible to specify a default for a newly added column. Suppose in version 1.2
367367
of your app you add groups for reminders lists. This can be expressed as a new field on the
@@ -410,7 +410,7 @@ And your migration will need to add a nullable column to the table:
410410
REFERENCES "remindersListGroups"("id")
411411
```
412412

413-
It may be disappointing to have to weaken your domain modeling to accomodate synchronization, but
413+
It may be disappointing to have to weaken your domain modeling to accommodate synchronization, but
414414
that is the unfortunate reality of a distributed schema. In order to allow multiple versions of your
415415
schema to be run on devices so that each device can create new records and edit existing records
416416
that all devices can see, you will need to make some compromises.
@@ -420,9 +420,9 @@ that all devices can see, you will need to make some compromises.
420420
Certain kinds of migrations are simply not allowed when synchronizing your schema to multiple
421421
devices. They are:
422422

423-
* Removing columns
424-
* Renaming columns
425-
* Renaming tables
423+
* Removing columns
424+
* Renaming columns
425+
* Renaming tables
426426

427427
## Sharing records with other iCloud users
428428

@@ -436,7 +436,7 @@ See <doc:CloudKitSharing> for more information.
436436

437437
## Assets
438438

439-
> TLDR: The library packages all BLOB columns in a table into `CKAsset`s and seamlessly decodes
439+
> TL;DR: The library packages all BLOB columns in a table into `CKAsset`s and seamlessly decodes
440440
> `CKAsset`s back into your tables. We recommend putting large binary blobs of data in their own
441441
> tables.
442442
@@ -495,7 +495,7 @@ to construct a SQL query for fetching the meta data associated with one of your
495495

496496
For example, if you want to retrieve the `CKRecord` that is associated with a particular row in
497497
one of your tables, say a reminder, then you can use ``SyncMetadata/lastKnownServerRecord`` to
498-
retreive the `CKRecord` and then invoke a CloudKit database function to retreive all of the details:
498+
retrieve the `CKRecord` and then invoke a CloudKit database function to retrieve all of the details:
499499

500500
```swift
501501
let lastKnownServerRecord = try database.read { db in
@@ -516,12 +516,12 @@ let ckRecord = try await container.privateCloudDatabase
516516
> a shared record, which can be determined from [SyncMetadata.share](<doc:SyncMetadata/share>),
517517
> then you must use `sharedCloudDatabase` to fetch the newest record.
518518
519-
You are free to invoke any CloudKit functions you want with the `CKRecord` retreived from
519+
You are free to invoke any CloudKit functions you want with the `CKRecord` retrieved from
520520
``SyncMetadata``. Any changes made directly with CloudKit will be automatically synced to your
521521
SQLite database by the ``SyncEngine``.
522522

523523
It is also possible to fetch the `CKShare` associated with a record if it has been shared, which
524-
will give you access to the most current list of paricipants and permissions for the shared record:
524+
will give you access to the most current list of participants and permissions for the shared record:
525525

526526
```swift
527527
let share = try database.read { db in
@@ -586,14 +586,16 @@ return true if the write to your database originates from the sync engine. You c
586586
trigger like so:
587587

588588
```swift
589-
#sql("""
589+
#sql(
590+
"""
590591
CREATE TEMPORARY TRIGGER "…"
591-
AFTER DELETE ON "…""
592+
AFTER DELETE ON "…"
592593
FOR EACH ROW WHEN NOT \(SyncEngine.isSynchronizingChanges())
593594
BEGIN
594595
595596
END
596-
""")
597+
"""
598+
)
597599
```
598600

599601
Or if you are using the trigger building tools from [StructuredQueries] you can use it like so:
@@ -602,9 +604,8 @@ Or if you are using the trigger building tools from [StructuredQueries] you can
602604

603605
```swift
604606
Model.createTemporaryTrigger(
605-
"",
606607
after: .insert { new in
607-
608+
// ...
608609
} when: { _ in
609610
!SyncEngine.isSynchronizingChanges()
610611
}

Sources/SharingGRDBCore/Documentation.docc/Articles/CloudKitSharing.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct RemindersListView: View {
5050
Task {
5151
await withErrorReporting {
5252
sharedRecord = try await syncEngine.share(record: remindersList) { share in
53-
share[CKShare.SystemFieldKey.title] = "Join '\(remindersList.title)!'"
53+
share[CKShare.SystemFieldKey.title] = "Join '\(remindersList.title)'!"
5454
}
5555
}
5656
}
@@ -104,7 +104,7 @@ shared record. There is, however, a lot more to know about sharing. There are im
104104
placed on what kind of records you are allowed to share, and what associations of those records are
105105
shared.
106106

107-
In a nutshell, only "root" records can be directly shared, i.e. records with no foreign keys.
107+
In a nutshell, only "root" records can be directly shared, _i.e._ records with no foreign keys.
108108
Further, an association of a root record can only be shared if it has only one foreign key pointing
109109
to the root record. And this last rule applies recursively: a leaf association is shared only if
110110
it has exactly one foreign key pointing to a record that also satisfies this property.
@@ -113,7 +113,7 @@ For more in-depth information, keep reading.
113113

114114
### Sharing root records
115115

116-
> Important: It is only possible to share "root" records, i.e. records with no foreign keys.
116+
> Important: It is only possible to share "root" records, _i.e._ records with no foreign keys.
117117
118118
A record can be shared only if it is a "root" record. That means it cannot have any
119119
foreign keys whatsoever. As an example, the following `RemindersList` table is a root record because
@@ -141,16 +141,16 @@ struct Reminder: Identifiable {
141141
```
142142

143143
Such records cannot be shared because it is not appropriate to also share the parent record
144-
(i.e. the reminders list).
144+
(_i.e._ the reminders list).
145145

146146
For example, suppose you have a list named "Personal" with a reminder "Get milk". If you share this
147147
reminder with someone, then it becomes difficult to figure out what to do when they make certain
148148
changes to the reminder:
149149

150-
* If they decide to reassign the reminder to their personal "Life" list, what should
151-
happen? Should their "Life" list suddenly be synchronized to your device?
152-
* Or what if they delete the list? Would you want that to delete your list and all of the reminders
153-
in the list?
150+
* If they decide to reassign the reminder to their personal "Life" list, what should
151+
happen? Should their "Life" list suddenly be synchronized to your device?
152+
* Or what if they delete the list? Would you want that to delete your list and all of the reminders
153+
in the list?
154154

155155
For these reasons, and more, it is not possible to share non-root records, like reminders. Instead,
156156
you can share root records, like reminders lists. If you do invoke
@@ -237,7 +237,7 @@ As a more complex example, consider the following diagrammatic schema:
237237

238238
In this schema, a `RemindersList` can have many `Reminder`s and a `CoverImage`, and a `Reminder`
239239
can have many `ChildReminder`s. Sharing a `RemindersList` will share all associated reminders,
240-
cover image, and even child reminderes. The child reminders are synchronized because it has a
240+
cover image, and even child reminders. The child reminders are synchronized because it has a
241241
single foreign key pointing to a table that also has a single foreign key pointing to the root
242242
record.
243243

@@ -347,8 +347,8 @@ it is also the primary key of the table it enforces that at most one cover image
347347
## Controlling what data is shared
348348

349349
It is possible to specify that certain associations that are shareable not be shared. For example,
350-
suppose that you want reminders lists to be orderable by your user, and so add a `position`
351-
column to the table:
350+
suppose that you want reminders lists to be sorted by your user, and so add a `position` column to
351+
the table:
352352

353353
```swift
354354
@Table

0 commit comments

Comments
 (0)