gomobile

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: May 17, 2026 License: MIT Imports: 5 Imported by: 0

README

gomobile binding

Bytes-only subset of ygo for use with 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 the underlying types with bytes-in / bytes-out methods only — everything maps cleanly onto the JavaScript-style API surface that Objective-C / Java consumers can call.

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.

API shape mirrors the y-protocols sync flow: encode local state to bytes, apply remote bytes, exchange via your transport of choice. Higher-level types (Map, Array, Text) are NOT exposed here — adopters who need them either (a) wait for richer gomobile support, (b) use bytes-only APIs and parse on the UI side, (c) write their own wrapper that exposes typed accessors.

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 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 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) 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) 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.

Jump to

Keyboard shortcuts

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