From b46854af90f4ebb4c975a5ee2542dc142a2f19d9 Mon Sep 17 00:00:00 2001
From: August Cayzer
Date: Tue, 27 Oct 2026 06:28:46 +0000
Subject: [PATCH 1/2] docs(sdk): describe drawing and setting a type's icon in
the Swift SDK
---
content/docs/(reference)/swift-sdk.mdx | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/content/docs/(reference)/swift-sdk.mdx b/content/docs/(reference)/swift-sdk.mdx
index f49cc9b5..d93966fe 100644
--- a/content/docs/(reference)/swift-sdk.mdx
+++ b/content/docs/(reference)/swift-sdk.mdx
@@ -284,6 +284,7 @@ _ = try await api.register(itemType: entry)
| --- | --- |
| `check(itemType:)` | [Check a type definition](/api-reference/types/normalizeType) without registering it. |
| `register(itemType:)`, `replace(itemType:with:)`, `delete(itemType:force:)` | [Register](/api-reference/types/registerType), [replace](/api-reference/types/replaceType) or [delete](/api-reference/types/deleteType) an item type. |
+| `set(icon:forType:)`, `clearIcon(ofType:)` | [Set](/api-reference/types/setTypeIcon) or [clear](/api-reference/types/clearTypeIcon) the owner's icon for a type. [Draw a type's icon](#draw-a-types-icon) shows them. |
| `register(edgeType:)`, `edgeTypes()`, `delete(edgeType:force:)` | [Register](/api-reference/edge-types/registerEdgeType), [list](/api-reference/edge-types/listEdgeTypes) or [delete](/api-reference/edge-types/deleteEdgeType) an edge type. |
Every other operation the client makes is a method named after its operation ID, such as `listItemVersions(path:)` or `listAuditLog()`. A refusal throws a `MarfaError`, so read a success from the answer's `ok` case: `try await api.listAuditLog().ok.body.json`.
@@ -321,6 +322,31 @@ if job.status == .completed, job.result?.settingsRewritten == true {
A move refuses with `server` and the code `type_move_blocked` when a setting names the type in a way it can't rewrite, and moves nothing. **A delete of a type that an item, folder, view or webhook still names is refused with `server`, status 409 and the code `type_in_use`.** The `message` of both ends with the refusal's `details` as JSON, which lists each setting by its `kind`, `id` and `field`.
+## Draw a type's icon
+
+Every item type carries one icon: the name of an icon in the [Lucide](https://lucide.dev/icons/) release Marfa pins, such as `book-open`. **`ItemType.icon` holds the name the type resolves to**: the owner's own choice for the type or its nearest parent that has one, else the nearest author's. It is `nil` when no type in the chain sets one. [Display hints](/catalog/your-own-types) explains how a type gets its icon.
+
+The `MarfaIcons` product draws the name, so an app adds it beside `Marfa`:
+
+```swift
+import MarfaIcons
+
+TypeIcon(type) // from an ItemType
+TypeIcon(name: type.icon) // from a name
+Image(typeIcon: type.icon) // where a view needs an Image
+```
+
+Apple apps draw SF Symbols, so `TypeIcon` uses the SF Symbol that stands for the Lucide name, and for a name with no good match it draws Lucide's own artwork, shipped in the package as a template image. **Both tint with `foregroundStyle`**, follow light and dark appearance, and size with the surrounding font. A type with no icon, and a name this version of the SDK doesn't know, draw a fallback symbol and never fail, because an instance newer than the SDK can send such a name.
+
+The owner can choose the icon of any type, including one Marfa ships. `Client` sets and clears the choice, and answers the type as the instance now reads it, so the app can draw the new icon at once:
+
+```swift
+let type = try await api.set(icon: "scale", forType: "app.readinglist.entry")
+_ = try await api.clearIcon(ofType: "app.readinglist.entry")
+```
+
+Both calls need a credential with `schema.write` and write on the type, and throw `forbidden` without them: with the code `forbidden` when the credential lacks `schema.write`, and `type_not_permitted` when it may not write the type. **A name outside the pinned Lucide release is refused** with `validation` and the code `invalid_schema`. Clearing a type that has no owner icon succeeds and changes nothing. The copy's `catalog` takes the change the next time it reads the catalog, as [Display hints](/catalog/your-own-types) describes, so another device shows it later.
+
## Follow changes
Each `Change` names the `itemId` or `edgeId` it concerns, and its `origin` says where it came from:
From 0dbd5c9ca7913600213cb7f114b36126ecae9edb Mon Sep 17 00:00:00 2001
From: August Cayzer
Date: Tue, 27 Oct 2026 07:59:50 +0000
Subject: [PATCH 2/2] docs(sdk): link the display hints section
---
content/docs/(reference)/swift-sdk.mdx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/content/docs/(reference)/swift-sdk.mdx b/content/docs/(reference)/swift-sdk.mdx
index d93966fe..7c9f6060 100644
--- a/content/docs/(reference)/swift-sdk.mdx
+++ b/content/docs/(reference)/swift-sdk.mdx
@@ -324,7 +324,7 @@ A move refuses with `server` and the code `type_move_blocked` when a setting nam
## Draw a type's icon
-Every item type carries one icon: the name of an icon in the [Lucide](https://lucide.dev/icons/) release Marfa pins, such as `book-open`. **`ItemType.icon` holds the name the type resolves to**: the owner's own choice for the type or its nearest parent that has one, else the nearest author's. It is `nil` when no type in the chain sets one. [Display hints](/catalog/your-own-types) explains how a type gets its icon.
+Every item type carries one icon: the name of an icon in the [Lucide](https://lucide.dev/icons/) release Marfa pins, such as `book-open`. **`ItemType.icon` holds the name the type resolves to**: the owner's own choice for the type or its nearest parent that has one, else the nearest author's. It is `nil` when no type in the chain sets one. [Display hints](/catalog/your-own-types#display-hints) explains how a type gets its icon.
The `MarfaIcons` product draws the name, so an app adds it beside `Marfa`:
@@ -345,7 +345,7 @@ let type = try await api.set(icon: "scale", forType: "app.readinglist.entry")
_ = try await api.clearIcon(ofType: "app.readinglist.entry")
```
-Both calls need a credential with `schema.write` and write on the type, and throw `forbidden` without them: with the code `forbidden` when the credential lacks `schema.write`, and `type_not_permitted` when it may not write the type. **A name outside the pinned Lucide release is refused** with `validation` and the code `invalid_schema`. Clearing a type that has no owner icon succeeds and changes nothing. The copy's `catalog` takes the change the next time it reads the catalog, as [Display hints](/catalog/your-own-types) describes, so another device shows it later.
+Both calls need a credential with `schema.write` and write on the type, and throw `forbidden` without them: with the code `forbidden` when the credential lacks `schema.write`, and `type_not_permitted` when it may not write the type. **A name outside the pinned Lucide release is refused** with `validation` and the code `invalid_schema`. Clearing a type that has no owner icon succeeds and changes nothing. The copy's `catalog` takes the change the next time it reads the catalog, as [Display hints](/catalog/your-own-types#display-hints) describes, so another device shows it later.
## Follow changes