gomobile

package
v1.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 15 Imported by: 0

README

gomobile binding

App-level mobile SDK for ygo via gomobile bind, the official Go cross-compilation toolchain for iOS and Android.

The main ygo package exposes a fully idiomatic Go API (channels, any, callbacks, generics) that gomobile bind cannot generate bindings for. This subpackage wraps those types with a bind-safe surface so an iOS / Android app can edit a document and render UI while sync runs in the background.

API levels

  • App level — editable Text (InsertAt / DeleteAt / String, UTF-16 indices) and Map (string keys/values), UndoManager, cursor anchors (Text.EncodeCursor / ResolveCursor), and a sync Client (NewClient / Connect / Listener) that connects to a yserve / Hocuspocus / y-websocket server.
  • Wire level — bytes-in / bytes-out (ApplyUpdate, EncodeStateAsUpdate, EncodeDiff) for adopters bringing their own transport.

Offline-first storage

Client.EnableOfflineStore(dbPath) (call before Connect) persists the document to a pure-Go on-device SQLite file. The document loads from it on connect so the app is usable with no network, every change is saved, and edits made offline sync up when a connection is next established:

c := gomobile.NewClient("wss://collab.example.com", "note", doc)
c.EnableOfflineStore(appSupportDir + "/note.db")
c.Connect()

Pass the app's document path (e.g. applicationSupportDirectory on iOS). No CGO, so the SQLite database needs no native library.

Observing changes

Text.ObserveChanges and Map.ObserveChanges deliver the exact change of each transaction as a Quill-style delta in JSON — the shape a native editor (or Quill / ProseMirror) applies directly, so the UI updates only what changed instead of re-rendering the whole document:

text := doc.Text("note")
text.ObserveChanges(listener) // listener.OnTextChange([]byte)
// delivers e.g. [{"retain":3},{"insert":"hi","attributes":{"bold":true}}]

Implement TextChangeListener / MapChangeListener in Swift or Kotlin. Callbacks run on a background goroutine while the document lock is held; dispatch to the main thread before touching UI.

Rich text

Text.ApplyDelta is the write side of the round trip — the same Quill-style delta ObserveChanges delivers, applied in one transaction. A native editor produces a delta on user input and hands it straight in:

text.ApplyDelta([]byte(`[{"retain":3},{"insert":"hi","attributes":{"bold":true}}]`))

An insert value is a string (text) or a JSON object (an embed such as an image); indices and retain / delete counts are UTF-16 units. For discrete toolbar actions there are direct calls: Format(index, length, attributesJSON) (bold a selection, {"bold":null} clears a key), InsertWithAttributes(index, text, attributesJSON), and InsertEmbed(index, embedJSON). Pair these with ObserveChanges and a native editor renders and edits formatted text end to end.

Map, Array, and nested types

Beyond Text, the SDK binds the full shared type set. Values cross the binding as JSON, so they carry numbers, booleans, strings, and plain arrays / objects — byte-compatible with JS Yjs:

m := doc.Map("meta")
m.SetJSON("count", []byte(`7`))      // typed scalar; GetJSON reads it back
list := doc.Array("items")
list.PushJSON([]byte(`{"id":1}`))    // InsertJSON / GetJSON / ToJSON / DeleteAt

Types nest. Map.SetMap / SetArray / SetText and Array.InsertMap / InsertArray / InsertText create a child and return its handle; GetMap / GetArray / GetText read an existing one back (returning nil if the slot holds a different type), so an app builds and walks a tree of collaborative objects.

XML (ProseMirror / Tiptap)

Doc.XmlFragment is the root of an XML document tree — the model ProseMirror and Tiptap map onto. XmlElement carries a node name, string attributes (SetAttribute / GetAttribute / AttributesJSON), and ordered children; XmlText is a rich-text leaf whose Text() handle exposes the full ApplyDelta / Format surface. ToString renders the subtree as HTML-like markup:

frag := doc.XmlFragment("prose")
p := frag.InsertElement(0, "paragraph")
p.SetAttribute("align", "center")
p.InsertText(0).Text().InsertAt(0, "hello")
// frag.ToString() == `<paragraph align="center">hello</paragraph>`

Collaborator presence and live cursors

The sync Client carries an awareness (presence) channel for ephemeral per-user state — who is online, their name, their cursor. Publishing your own and observing everyone else's is the full round trip for rendering live remote cursors.

Publish your state with SetAwarenessState, typically a small JSON object with your name and a cursor encoded from the text you are editing:

cur, _ := doc.Text("note").EncodeCursor(index, 0) // anchor at a position
state, _ := json.Marshal(map[string]any{
    "name":   "ian",
    "cursor": base64.StdEncoding.EncodeToString(cur),
})
client.SetAwarenessState(state)

Observe everyone else with Client.ObservePresence. The listener receives the whole room as a JSON object keyed by clientID, and fires on every change — a cursor move, a join, a leave:

client.ObservePresence(listener) // listener.OnPresenceChange([]byte)
// delivers e.g. {"42":{"name":"ian","cursor":"BASE64..."},"77":{"name":"sam"}}

For each peer, base64-decode its cursor and call Text.ResolveCursor to map it to a local index — the position stays correct even after concurrent edits shift the text. Skip your own entry with Client.ClientID(); call Client.PresenceStates() for the initial paint before the first change arrives. A peer that disconnects or calls RemoveAwarenessState drops out of the next snapshot.

Implement PresenceListener in Swift or Kotlin. Callbacks run on a background goroutine; dispatch to the main thread before touching UI.

Verified iOS xcframework build

# One-time setup
go install golang.org/x/mobile/cmd/gomobile@latest
go install golang.org/x/mobile/cmd/gobind@latest
$(go env GOPATH)/bin/gomobile init

# In a fresh checkout of github.com/Deln0r/ygo:
go get golang.org/x/mobile/bind   # gomobile build dependency
$(go env GOPATH)/bin/gomobile bind -target=ios,iossimulator \
    -o /tmp/Ygo.xcframework \
    github.com/Deln0r/ygo/gomobile

Produces a .xcframework containing:

  • ios-arm64/Ygo.framework (~6.6 MB) — real-device slice (arm64)
  • ios-arm64_x86_64-simulator/Ygo.framework (~13 MB) — simulator slice (arm64 + x86_64, fat)
  • Auto-generated Objective-C headers in each slice's Headers/ dir (Ygo.h, Gomobile.objc.h, Universe.objc.h, ref.h)

Drag the .xcframework into Xcode under "Frameworks, Libraries, and Embedded Content"; the auto-generated Swift bridging header exposes GomobileDoc, GomobileAwareness and helpers like GomobileNewDoc, GomobileNewDocWithClientID. Verified on Xcode 16+, Go 1.26, macOS 26 (Apple Silicon, May 2026).

Verified Android AAR build

# One-time: install NDK + at least one SDK platform via sdkmanager.
# (Android Studio's first-launch wizard installs the SDK but not NDK.)
SDK=$HOME/Library/Android/sdk
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
$SDK/cmdline-tools/latest/bin/sdkmanager --install "ndk;27.0.12077973"
$SDK/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-21"

# Per-build:
export ANDROID_HOME=$SDK
export ANDROID_NDK_HOME=$SDK/ndk/27.0.12077973
go get golang.org/x/mobile/bind   # gomobile build dependency

$(go env GOPATH)/bin/gomobile bind \
    -target=android \
    -androidapi 21 \
    -o /tmp/ygo.aar \
    github.com/Deln0r/ygo/gomobile

Produces an .aar (Android archive) ~8.4 MB containing native JNI libraries for all four standard Android architectures:

Slice Size
jni/arm64-v8a/libgojni.so (modern devices) 3.8 MB
jni/armeabi-v7a/libgojni.so (older 32-bit) 3.7 MB
jni/x86_64/libgojni.so (emulator) 4.1 MB
jni/x86/libgojni.so (older emulator) 3.7 MB

Plus classes.jar exposing the Java surface:

  • gomobile.Doc — the CRDT document handle (NewDoc / ApplyUpdate / EncodeStateAsUpdate / EncodeStateVector / EncodeDiff / HasPending / MissingSV)
  • gomobile.Awareness — the presence layer
  • gomobile.Gomobile — package-level static helpers (NewDoc(), NewDocWithClientID(long), NewAwareness(long))
  • go.Seq + supporting runtime classes

Drop the .aar into your Android Studio project's app/libs/ directory, add implementation files('libs/ygo.aar') to build.gradle, and import gomobile.Doc; from Kotlin or Java. Verified on Android Studio Ladybug + NDK 27.0 + Go 1.26 / macOS 26 Apple Silicon (May 2026).

NB on androidapi 21: NDK 27 dropped support for API levels below 21 (Android 5.0 Lollipop). Without the explicit -androidapi 21 flag, gomobile defaults to 16 and fails with "unsupported API version". 21+ covers >99% of Android devices in service.

Note on go.mod

gomobile bind requires golang.org/x/mobile/bind to be present in the module's dependency graph at build time. The main go.mod does NOT carry this dep (it would bump the go directive past 1.22 and break our CI Go-version matrix). Adopters running their own gomobile bind should go get golang.org/x/mobile/bind in their fresh checkout before the bind step (see commands above); the dep is build-time only, no runtime cost.

Documentation

Overview

Package gomobile is the bytes-in/bytes-out subset of the ygo API designed to survive `gomobile bind`. The full public API at github.com/Deln0r/ygo exposes Go types that gomobile cannot bind (the `any` interface, callbacks, generic maps), so iOS / Android consumers import this package instead and exchange state with their UI layer via byte arrays.

What gomobile filters out and we work around:

  • Function parameters of `any` / `map[K]V` / `chan T` / `func(...)` are silently skipped from the generated binding. ygo.Map.Set takes `value any`; gomobile produces a Map type with no Set.
  • Slices of non-byte types (e.g. `[]any`, `[]string`) are skipped. ygo.Array.ToSlice() returns `[]any`.
  • Generics break the bind step entirely.
  • Callback registration (Sub, OnUpdate, OnChange) is skipped.

What survives:

  • Opaque struct pointers (Doc, this package's wrapper types).
  • Methods that take/return `string`, `int*`/`uint*`, `bool`, `float*`, `[]byte`, and `error`.
  • Bytes-in/bytes-out wire-format operations: this package exposes those plus a tiny Doc wrapper.

The package exposes two API levels:

  • App level (types.go, client.go): Text (insert / delete / cursors), Map (string keys and values), UndoManager, and Client — a complete background sync provider (WebSocket, handshake, reconnect) so a Swift / Kotlin app only renders UI and edits the Doc. Reads run under internal read transactions, safe against the background client.

  • Wire level (this file): bytes-in/bytes-out y-protocols flow — encode local state to bytes, apply remote bytes, bring your own transport.

gomobile bind verification: actual `gomobile bind -target=ios` or `-target=android` requires the corresponding toolchain (Xcode / Android NDK) and is not run in CI. The package compiles under pure Go to guarantee no inadvertent CGO leak via dependencies.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Array added in v1.8.0

type Array struct {
	// contains filtered or unexported fields
}

Array is the bindable shared Array (ordered sequence) CRDT. Elements are scalar JSON values or nested Text / Map / Array types.

func (*Array) DeleteAt added in v1.8.0

func (a *Array) DeleteAt(index, length int) error

DeleteAt removes length elements starting at index.

func (*Array) GetArray added in v1.8.0

func (a *Array) GetArray(index int) *Array

GetArray returns the nested Array at index, or nil if not an Array.

func (*Array) GetJSON added in v1.8.0

func (a *Array) GetJSON(index int) []byte

GetJSON returns the element at index as JSON, or "null" when the index is out of range or holds a nested type (use GetMap / GetArray / GetText for those).

func (*Array) GetMap added in v1.8.0

func (a *Array) GetMap(index int) *Map

GetMap returns the nested Map at index, or nil if not a Map.

func (*Array) GetText added in v1.8.0

func (a *Array) GetText(index int) *Text

GetText returns the nested Text at index, or nil if the element is not a Text.

func (*Array) InsertArray added in v1.8.0

func (a *Array) InsertArray(index int) *Array

InsertArray inserts a new nested Array at index and returns it.

func (*Array) InsertJSON added in v1.8.0

func (a *Array) InsertJSON(index int, valueJSON []byte) error

InsertJSON inserts a scalar JSON value at the given index.

func (*Array) InsertMap added in v1.8.0

func (a *Array) InsertMap(index int) *Map

InsertMap inserts a new nested Map at index and returns it.

func (*Array) InsertText added in v1.8.0

func (a *Array) InsertText(index int) *Text

InsertText inserts a new nested Text at index and returns it.

func (*Array) Length added in v1.8.0

func (a *Array) Length() int

Length returns the number of elements.

func (*Array) ObserveChanges added in v1.8.0

func (a *Array) ObserveChanges(l ArrayChangeListener)

ObserveChanges registers a listener that fires after every transaction changing this array, delivering the change as a delta. Replaces any previously registered listener on this array.

func (*Array) PushJSON added in v1.8.0

func (a *Array) PushJSON(valueJSON []byte) error

PushJSON appends a scalar JSON value (string, number, bool, null, or a plain array / object) to the end of the array.

func (*Array) ToJSON added in v1.8.0

func (a *Array) ToJSON() []byte

ToJSON renders the whole array as a JSON array. Nested types appear as null; use the typed accessors to descend into them.

type ArrayChangeListener added in v1.8.0

type ArrayChangeListener interface {
	OnArrayChange(deltaJSON []byte)
}

ArrayChangeListener receives array change deltas. deltaJSON is a Quill-style delta whose inserts are arrays of values, the shape a collaborative list view applies directly:

[{"retain":2},{"insert":[42,true]},{"delete":1}]

type Awareness

type Awareness struct {
	// contains filtered or unexported fields
}

Awareness is the gomobile-bindable handle for the y-protocols Awareness presence layer. Independent of any Doc; the local clientID is passed at construction. Embedders typically pair an Awareness with a Doc and use d.ClientID() as the awareness clientID.

All state is exchanged as raw JSON bytes. Mobile UIs that need typed access (cursor positions, user names) JSON-parse on the caller side after pulling via States.

func NewAwareness

func NewAwareness(clientID uint64) *Awareness

NewAwareness returns a fresh Awareness for the given clientID.

func (*Awareness) Apply

func (a *Awareness) Apply(rawBytes []byte, origin string) error

Apply integrates a wire-encoded awareness update. The origin string is forwarded to subscribers — but since callback registration is not gomobile-bindable, mobile callers typically pass an empty string.

Returns an error only for malformed wire bytes.

func (*Awareness) ClientID

func (a *Awareness) ClientID() uint64

ClientID returns the local awareness clientID.

func (*Awareness) EncodeAll

func (a *Awareness) EncodeAll() []byte

Encode returns the V1 wire bytes for the awareness states of the given clientIDs. Pass an empty clientIDs slice to encode every known client.

Note: gomobile-bound callers cannot pass `[]uint64` directly (slices of non-byte types are skipped). Use EncodeAll for the common "send everything" case; for per-client encoding adopters must wrap one ID at a time or extend this package.

func (*Awareness) LocalState

func (a *Awareness) LocalState() []byte

LocalState returns the local client's current state JSON bytes, or nil if the state has not been set / has been removed.

func (*Awareness) RemoveLocalState

func (a *Awareness) RemoveLocalState()

RemoveLocalState marks the local client offline. The wire- format representation is an entry with JSON "null"; the local entry is kept (with state cleared) so the clock survives subsequent revival races.

func (*Awareness) SetLocalState

func (a *Awareness) SetLocalState(jsonBytes []byte)

SetLocalState replaces the local client's awareness state with the given raw JSON bytes. Nil clears the state (equivalent to RemoveLocalState). The clock bumps on every call — even no-op equal sets — per the y-protocols 15-second heartbeat invariant.

type Client added in v1.2.0

type Client struct {
	// contains filtered or unexported fields
}

Client is the gomobile-bindable sync provider: a live WebSocket session syncing one Doc with a yserve / Hocuspocus / y-websocket server. Construct with NewClient, optionally SetListener, start with Connect, stop with Close. The native app edits the Doc through Text / Map wrappers; sync happens in the background.

func NewClient added in v1.2.0

func NewClient(url, docName string, d *Doc) *Client

NewClient prepares a sync client for the document. url is the server base ("wss://collab.example.com"); docName addresses the document on it.

func (*Client) ClientID added in v1.6.0

func (c *Client) ClientID() uint64

ClientID returns this client's awareness/document clientID, the key of its own entry in the presence snapshot. Use it to skip yourself when rendering peer cursors.

func (*Client) Close added in v1.2.0

func (c *Client) Close() error

Close stops the connection loop and releases the listener hooks. Safe to call more than once.

func (*Client) Connect added in v1.2.0

func (c *Client) Connect() error

Connect starts the background connection loop (handshake, update relay, reconnect with exponential backoff). Returns an error when already connected or the configuration is invalid.

func (*Client) EnableOfflineStore added in v1.3.0

func (c *Client) EnableOfflineStore(dbPath string)

EnableOfflineStore makes the client offline-first, persisting the document to a pure-Go on-device SQLite database at dbPath. On the next Connect the document loads from that file (usable with no network), every change is persisted, and edits made offline sync up when a connection is next established. Call before Connect; a no-op after. Pass the app's document path, e.g. FileManager applicationSupportDirectory on iOS.

func (*Client) ObservePresence added in v1.6.0

func (c *Client) ObservePresence(l PresenceListener)

ObservePresence registers a listener fired whenever the room's presence changes — a peer's cursor or identity updates, or a peer joins or leaves. It delivers the full current snapshot each time, ready to render. Replaces any previously registered presence listener. May be called before or after Connect; pass nil to stop observing.

Publish your own presence with SetAwarenessState, typically a small JSON object carrying your name and an encoded cursor:

cur, _ := text.EncodeCursor(index, 0)
state, _ := json.Marshal(map[string]any{
    "name":   "ian",
    "cursor": base64.StdEncoding.EncodeToString(cur),
})
client.SetAwarenessState(state)

A peer reading that entry decodes the cursor bytes and calls text.ResolveCursor to map it to its own local index.

func (*Client) PresenceStates added in v1.6.0

func (c *Client) PresenceStates() []byte

PresenceStates returns the current presence snapshot — the same JSON shape ObservePresence delivers — or "{}" when not connected. Use it for the initial render before the first change arrives.

func (*Client) RemoveAwarenessState added in v1.2.0

func (c *Client) RemoveAwarenessState()

RemoveAwarenessState clears the local awareness entry (peers see this client leave).

func (*Client) SetAwarenessState added in v1.2.0

func (c *Client) SetAwarenessState(jsonState []byte)

SetAwarenessState sets and broadcasts the local awareness state (JSON bytes, e.g. {"name":"ian","cursor":"<base64 rpos>"}).

func (*Client) SetListener added in v1.2.0

func (c *Client) SetListener(l Listener)

SetListener registers the event listener. Call before Connect; calls after Connect are ignored.

func (*Client) Synced added in v1.2.0

func (c *Client) Synced() bool

Synced reports whether the last handshake completed and the connection is up.

type Doc

type Doc struct {
	// contains filtered or unexported fields
}

Doc is the gomobile-bindable opaque handle for a ygo CRDT replica. Construct via NewDoc / NewDocWithClientID. All shared-type operations route through wire-format bytes — push updates in via ApplyUpdate, pull state out via EncodeStateAsUpdate.

Concurrency: safe for use from multiple goroutines / gomobile-bound callers. Each method opens its own transaction internally; gomobile callers do not see Transaction or TransactionMut directly.

func NewDoc

func NewDoc() *Doc

NewDoc returns a fresh Doc with a random ClientID.

func NewDocWithClientID

func NewDocWithClientID(clientID uint64) *Doc

NewDocWithClientID returns a fresh Doc with the given ClientID. Use this when the calling application wants deterministic per-device IDs (typical mobile pattern: derive from a stable device identifier).

func (*Doc) ApplyUpdate

func (d *Doc) ApplyUpdate(rawBytes []byte) error

ApplyUpdate decodes raw V1 wire bytes and integrates them into the doc. Items whose dependencies the local store has not yet seen queue in the per-doc pending buffer and integrate automatically on subsequent ApplyUpdate calls that satisfy them.

Returns an error only for malformed wire bytes; missing- dependency cases queue silently and are queryable via HasPending / MissingSV.

func (*Doc) Array added in v1.8.0

func (d *Doc) Array(name string) *Array

Array returns the shared array registered under name, creating the root type on first access.

func (*Doc) ClientID

func (d *Doc) ClientID() uint64

ClientID returns this replica's client identifier.

func (*Doc) EncodeDiff

func (d *Doc) EncodeDiff(remoteSV []byte) ([]byte, error)

EncodeDiff returns the wire-encoded V1 update carrying blocks this doc has that the remote (per remoteSV bytes) does not. A nil remoteSV is treated as the empty state vector — emit everything.

remoteSV is the V1 wire-encoded form of the remote's state vector (same shape EncodeStateVector produces). Pass straight from a sync-protocol message; the function decodes internally.

func (*Doc) EncodeStateAsUpdate

func (d *Doc) EncodeStateAsUpdate() []byte

EncodeStateAsUpdate returns wire-encoded V1 bytes carrying the doc's full state. Apply to a peer doc to bring it up to speed. Interoperates with JS Yjs Y.encodeStateAsUpdate output.

func (*Doc) EncodeStateVector

func (d *Doc) EncodeStateVector() []byte

EncodeStateVector returns wire-encoded V1 state vector — what the y-sync protocol's SyncStep1 sends. The peer replies with the diff.

func (*Doc) HasPending

func (d *Doc) HasPending() bool

HasPending reports whether the doc has any queued items awaiting causal dependencies (items that arrived via ApplyUpdate before their Origin / RightOrigin / Parent ID was visible locally).

func (*Doc) Map added in v1.2.0

func (d *Doc) Map(name string) *Map

Map returns the shared map registered under name, creating the root type on first access.

func (*Doc) MissingSV

func (d *Doc) MissingSV() []byte

MissingSV returns the wire-encoded V1 state vector identifying the clocks this doc needs to receive in order to drain its pending buffer. Send to a peer as a re-fetch request.

An empty return ([]byte{0x00}, the encoded-empty-SV) means the pending buffer is empty.

func (*Doc) NewMapUndoManager added in v1.2.0

func (d *Doc) NewMapUndoManager(name string) *UndoManager

NewMapUndoManager returns an UndoManager watching the shared map registered under name.

func (*Doc) NewTextUndoManager added in v1.2.0

func (d *Doc) NewTextUndoManager(name string) *UndoManager

NewTextUndoManager returns an UndoManager watching the shared text registered under name.

func (*Doc) Text added in v1.2.0

func (d *Doc) Text(name string) *Text

Text returns the shared text registered under name, creating the root type on first access.

func (*Doc) XmlFragment added in v1.8.0

func (d *Doc) XmlFragment(name string) *XmlFragment

XmlFragment returns the shared XML fragment registered under name, creating the root type on first access.

type Listener added in v1.2.0

type Listener interface {
	// OnSynced fires on synced-state transitions: true after each
	// completed handshake, false on every disconnect.
	OnSynced(synced bool)
	// OnDocChanged fires after any transaction commits on the local
	// doc, local or remote — the "refresh your views" signal.
	OnDocChanged()
	// OnError reports non-fatal connection errors (the client keeps
	// reconnecting).
	OnError(message string)
}

Listener receives connection and document events from a Client. Implement it in Swift / Kotlin and pass to Client.SetListener; all methods are called from background goroutines, so dispatch to the main thread before touching UI.

type Map added in v1.2.0

type Map struct {
	// contains filtered or unexported fields
}

Map is the gomobile-bindable handle for a shared map restricted to string keys and string values (the subset that crosses the bridge without an `any` in sight). Other value kinds written by JS peers read back as empty strings; use the bytes-level Doc API for those.

func (*Map) Clear added in v1.8.0

func (m *Map) Clear()

Clear removes every entry.

func (*Map) DeleteKey added in v1.2.0

func (m *Map) DeleteKey(key string)

DeleteKey removes key.

func (*Map) GetArray added in v1.8.0

func (m *Map) GetArray(key string) *Array

GetArray returns the nested Array at key, or nil if not an Array.

func (*Map) GetJSON added in v1.8.0

func (m *Map) GetJSON(key string) []byte

GetJSON returns the value at key as JSON, or "null" when the key is absent or holds a nested type (use GetMap / GetArray / GetText).

func (*Map) GetMap added in v1.8.0

func (m *Map) GetMap(key string) *Map

GetMap returns the nested Map at key, or nil if the value is not a Map.

func (*Map) GetString added in v1.2.0

func (m *Map) GetString(key string) string

GetString returns the string value under key, or "" when the key is absent or holds a non-string value. Safe against a concurrent background Client: the read runs under a read transaction.

func (*Map) GetText added in v1.8.0

func (m *Map) GetText(key string) *Text

GetText returns the nested Text at key, or nil if not a Text.

func (*Map) Has added in v1.2.0

func (m *Map) Has(key string) bool

Has reports whether key holds a live value.

func (*Map) KeysJSON added in v1.8.0

func (m *Map) KeysJSON() []byte

KeysJSON returns the map's live keys as a JSON array of strings.

func (*Map) Len added in v1.2.0

func (m *Map) Len() int

Len returns the number of live entries.

func (*Map) ObserveChanges added in v1.2.0

func (m *Map) ObserveChanges(l MapChangeListener)

ObserveChanges registers a listener that fires after every transaction changing this map, delivering the changed keys. Replaces any previously registered listener on this map.

func (*Map) SetArray added in v1.8.0

func (m *Map) SetArray(key string) *Array

SetArray sets key to a new nested Array and returns it.

func (*Map) SetJSON added in v1.8.0

func (m *Map) SetJSON(key string, valueJSON []byte) error

SetJSON sets key to a scalar JSON value (string, number, bool, null, or a plain array / object) — the typed-value counterpart to SetString. Syncs byte-compatibly with JS Yjs.

func (*Map) SetMap added in v1.8.0

func (m *Map) SetMap(key string) *Map

SetMap sets key to a new nested Map and returns it.

func (*Map) SetString added in v1.2.0

func (m *Map) SetString(key, value string)

SetString sets key to a string value.

func (*Map) SetText added in v1.8.0

func (m *Map) SetText(key string) *Text

SetText sets key to a new nested Text and returns it.

type MapChangeListener added in v1.2.0

type MapChangeListener interface {
	OnMapChange(keysJSON []byte)
}

MapChangeListener receives map change summaries. keysJSON maps each changed key to {"action":"add|update|delete","oldValue":...}:

{"title":{"action":"update","oldValue":"draft"}}

type PresenceListener added in v1.6.0

type PresenceListener interface {
	OnPresenceChange(statesJSON []byte)
}

PresenceListener receives the room's presence (awareness) snapshot whenever any peer's ephemeral state changes — a cursor moves, a user joins, or a user leaves. statesJSON is a JSON object mapping each online peer's clientID (as a decimal string) to that peer's raw state JSON:

{"42":{"name":"ian","cursor":"BASE64..."},"77":{"name":"sam"}}

Render every collaborator's cursor and identity from it. Your own entry is included; filter it with Client.ClientID() if you only want peers. Called from a background goroutine; dispatch to the main thread before touching UI and do not call back into the client synchronously.

type Text added in v1.2.0

type Text struct {
	// contains filtered or unexported fields
}

Text is the gomobile-bindable handle for a shared text type: the editable surface a native note-taking / editor UI binds to. All indices and lengths are UTF-16 code units, matching JS Yjs (and NSString / Java String length semantics, which is what makes the mobile bridge clean).

func (*Text) ApplyDelta added in v1.7.0

func (t *Text) ApplyDelta(deltaJSON []byte) error

ApplyDelta applies a Quill-style delta to the text in one transaction — the write side of the rich-text round trip, symmetric with ObserveChanges. A native editor (Quill / ProseMirror / a custom view) produces a delta on user input and hands it straight here:

[{"retain":3},{"insert":"hi","attributes":{"bold":true}},{"delete":2}]

An insert value is either a string (text) or a JSON object (an embed, e.g. an image). Indices and retain / delete counts are UTF-16 units, matching ObserveChanges and JS Yjs.

func (*Text) DeleteAt added in v1.2.0

func (t *Text) DeleteAt(index, length int) error

DeleteAt removes length UTF-16 units starting at index.

func (*Text) EncodeCursor added in v1.2.0

func (t *Text) EncodeCursor(index, assoc int) ([]byte, error)

EncodeCursor anchors the given index as a relative position and returns its binary form (byte-compatible with Y.encodeRelativePosition), suitable for sharing with JS peers via awareness. assoc >= 0 sticks to the character after the position, assoc < 0 to the character before it.

func (*Text) Format added in v1.7.0

func (t *Text) Format(index, length int, attributesJSON []byte) error

Format applies attributes (e.g. {"bold":true}) to length UTF-16 units starting at index — the action a toolbar fires when the user bolds a selection. A nil-valued attribute ({"bold":null}) clears that key.

func (*Text) InsertAt added in v1.2.0

func (t *Text) InsertAt(index int, s string) error

InsertAt inserts s at the given UTF-16 index.

func (*Text) InsertEmbed added in v1.7.0

func (t *Text) InsertEmbed(index int, embedJSON []byte) error

InsertEmbed inserts a single embedded value (an image, mention, or other non-text node) at the UTF-16 index. embedJSON is any JSON value, typically an object: {"image":"https://..."}.

func (*Text) InsertWithAttributes added in v1.7.0

func (t *Text) InsertWithAttributes(index int, s string, attributesJSON []byte) error

InsertWithAttributes inserts s at the UTF-16 index with formatting already applied (attributesJSON is an object like {"italic":true}).

func (*Text) Length added in v1.2.0

func (t *Text) Length() int

Length returns the text length in UTF-16 code units.

func (*Text) ObserveChanges added in v1.2.0

func (t *Text) ObserveChanges(l TextChangeListener)

ObserveChanges registers a listener that fires after every transaction changing this text, delivering the change as a Quill delta. This is the fine-grained signal a native editor needs to update only what changed, instead of re-rendering the whole document. Replaces any previously registered listener on this text.

func (*Text) ResolveCursor added in v1.2.0

func (t *Text) ResolveCursor(encoded []byte) int

ResolveCursor resolves an encoded relative position back to a current index in this text. Returns -1 when the anchor is unknown to this replica, garbage-collected, or belongs to another type.

func (*Text) String added in v1.2.0

func (t *Text) String() string

String returns the current text content. Safe against a concurrent background Client: the read runs under a read transaction.

type TextChangeListener added in v1.2.0

type TextChangeListener interface {
	OnTextChange(deltaJSON []byte)
}

TextChangeListener receives text change deltas. Implement it in Swift / Kotlin and pass to Text.ObserveChanges. deltaJSON is a Quill-style delta array, the same shape Quill / ProseMirror / a JS Yjs binding consumes, so a native editor can apply it directly:

[{"retain":3},{"insert":"hi","attributes":{"bold":true}},{"delete":2}]

Called from a background goroutine while the document lock is held; dispatch to the main thread before touching UI and do not call back into the document synchronously.

type UndoManager added in v1.2.0

type UndoManager struct {
	// contains filtered or unexported fields
}

UndoManager is the gomobile-bindable scoped undo/redo handle. Construct via Doc.NewTextUndoManager / Doc.NewMapUndoManager.

func (*UndoManager) CanRedo added in v1.2.0

func (u *UndoManager) CanRedo() bool

CanRedo reports whether the redo stack is non-empty.

func (*UndoManager) CanUndo added in v1.2.0

func (u *UndoManager) CanUndo() bool

CanUndo reports whether the undo stack is non-empty.

func (*UndoManager) Close added in v1.2.0

func (u *UndoManager) Close()

Close detaches the UndoManager from the document.

func (*UndoManager) Redo added in v1.2.0

func (u *UndoManager) Redo() bool

Redo re-applies the most recently undone change. Returns false when there is nothing to redo.

func (*UndoManager) StopCapturing added in v1.2.0

func (u *UndoManager) StopCapturing()

StopCapturing forces the next edit to start a new undo group.

func (*UndoManager) Undo added in v1.2.0

func (u *UndoManager) Undo() bool

Undo reverts the most recent captured local change. Returns false when there is nothing to undo.

type XmlElement added in v1.8.0

type XmlElement struct {
	// contains filtered or unexported fields
}

XmlElement is a named node with string attributes and ordered children (elements or text).

func (*XmlElement) AttributesJSON added in v1.8.0

func (e *XmlElement) AttributesJSON() []byte

AttributesJSON returns all attributes as a JSON object of strings.

func (*XmlElement) DeleteAt added in v1.8.0

func (e *XmlElement) DeleteAt(index, length int)

DeleteAt removes length children starting at index.

func (*XmlElement) GetAttribute added in v1.8.0

func (e *XmlElement) GetAttribute(name string) string

GetAttribute returns the attribute value, or "" when absent (use HasAttribute to distinguish an empty value from an absent one).

func (*XmlElement) GetElement added in v1.8.0

func (e *XmlElement) GetElement(index int) *XmlElement

GetElement returns the child element at index, or nil if not an element.

func (*XmlElement) GetText added in v1.8.0

func (e *XmlElement) GetText(index int) *XmlText

GetText returns the child text node at index, or nil if not text.

func (*XmlElement) HasAttribute added in v1.8.0

func (e *XmlElement) HasAttribute(name string) bool

HasAttribute reports whether name is set.

func (*XmlElement) InsertElement added in v1.8.0

func (e *XmlElement) InsertElement(index int, nodeName string) *XmlElement

InsertElement inserts a child <nodeName> element at index.

func (*XmlElement) InsertText added in v1.8.0

func (e *XmlElement) InsertText(index int) *XmlText

InsertText inserts a child text node at index.

func (*XmlElement) Length added in v1.8.0

func (e *XmlElement) Length() int

Length returns the number of children.

func (*XmlElement) NodeName added in v1.8.0

func (e *XmlElement) NodeName() string

NodeName returns the element's tag name.

func (*XmlElement) RemoveAttribute added in v1.8.0

func (e *XmlElement) RemoveAttribute(name string)

RemoveAttribute deletes the attribute.

func (*XmlElement) SetAttribute added in v1.8.0

func (e *XmlElement) SetAttribute(name, value string)

SetAttribute sets a string attribute.

func (*XmlElement) ToString added in v1.8.0

func (e *XmlElement) ToString() string

ToString renders the element and its subtree as HTML-like markup.

type XmlFragment added in v1.8.0

type XmlFragment struct {
	// contains filtered or unexported fields
}

XmlFragment is the bindable root of an XML document tree — the shape a ProseMirror / Tiptap model maps onto. It holds an ordered list of XmlElement and XmlText children.

func (*XmlFragment) DeleteAt added in v1.8.0

func (f *XmlFragment) DeleteAt(index, length int)

DeleteAt removes length children starting at index.

func (*XmlFragment) GetElement added in v1.8.0

func (f *XmlFragment) GetElement(index int) *XmlElement

GetElement returns the child element at index, or nil if it is not an element.

func (*XmlFragment) GetText added in v1.8.0

func (f *XmlFragment) GetText(index int) *XmlText

GetText returns the child text node at index, or nil if not text.

func (*XmlFragment) InsertElement added in v1.8.0

func (f *XmlFragment) InsertElement(index int, nodeName string) *XmlElement

InsertElement inserts a new <nodeName> element at index and returns it.

func (*XmlFragment) InsertText added in v1.8.0

func (f *XmlFragment) InsertText(index int) *XmlText

InsertText inserts a new text node at index and returns it.

func (*XmlFragment) Length added in v1.8.0

func (f *XmlFragment) Length() int

Length returns the number of children.

func (*XmlFragment) ToString added in v1.8.0

func (f *XmlFragment) ToString() string

ToString renders the fragment and its subtree as HTML-like markup.

type XmlText added in v1.8.0

type XmlText struct {
	// contains filtered or unexported fields
}

XmlText is a rich-text leaf in an XML tree. Use Text() for the full formatting surface (ApplyDelta / Format / InsertAt).

func (*XmlText) String added in v1.8.0

func (x *XmlText) String() string

String returns the text content.

func (*XmlText) Text added in v1.8.0

func (x *XmlText) Text() *Text

Text returns the text node as a Text handle, exposing the full rich-text surface (ApplyDelta / Format / InsertAt / ObserveChanges).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL