~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atirc-ios-client

0
1
main
48 lines · 2.5 KB Raw History
# Go + Swift in AT-IRC

PoC, 2026-10-30.

## Result
Possible. `gomobile bind` (`golang.org/x/mobile`) emits a static `AtircGo.xcframework` linked into the app. No dynamic library, no embedding; App Store-safe.

## Setup
- Install both tools: `go install golang.org/x/mobile/cmd/{gomobile,gobind}@latest`. `bind` shells out to `gobind`; installing only `gomobile` fails with "gobind was not found".
- `GoLib/go.mod`: `module atirc/golib`; requires `golang.org/x/mobile/bind`.
- `GoLib/golib.go`: exported `Version`, `Echo`, `SHA256Hex`, `ParseCommand`, `Counter`.
- Build: `gomobile bind -target=ios,iossimulator -o GoLib/AtircGo.xcframework atirc/golib`.

## Bridging
- `string` <-> `NSString`; `[]byte` <-> `NSData` (byte slices are mutable, passed by reference).
- Exported struct -> ObjC class (`GolibCounter`), methods/properties bound.
- `(T, error)` -> `NSError**`; Swift sees an explicit `NSErrorPointer`, not `throws`. Max two results; second must be `error`.
- Calls are synchronous; goroutines work.

## Type limits (gobind docs)
- Supported: signed ints, floats, `string`, `bool`, `[]byte`, functions, interfaces, structs with supported exported fields/methods.
- Unsupported: other slices, maps, channels, generics; docs list signed ints only.
- A panic crossing the boundary exits the process.
- Proxy reference cycles leak.
- Go can also call ObjC directly via reverse bindings (`import "ObjC/Foundation/NSDate"`).

## Xcode
- Per-SDK `FRAMEWORK_SEARCH_PATHS` must point at the slice, not the xcframework parent:
  `.../AtircGo.xcframework/ios-arm64` (device) and `.../ios-arm64_x86_64-simulator` (sim).
- `OTHER_LDFLAGS = $(inherited) -framework AtircGo`. Static framework: nothing to embed.
- `UpdateTargetBuildSetting` rejects `[sdk=...]` keys; set them in `project.xcproj` JSON.

## Validated (iPhone, iOS 27.2)
- Tests pass: strings, `[]byte` <-> `Data`, struct + methods, `(T, error)` -> `NSError`, goroutines, Go -> Swift callbacks.
- Interface collides with generated class, so Swift sees `GolibCallbackProtocol`.
- Callbacks arrive on a Go thread; hop to `MainActor` before touching UI.

## Size (Release, arm64)
- Wrapper unreferenced: 0 delta (dead-stripped).
- Wrapper referenced (force-loaded): 1.1 MB -> 3.1 MB binary (+2.0 MB); Go `__text` +768 KB.

## Rebuild
- `GoLib/build.sh`; `GoLib/AtircGo.xcframework` is gitignored.

## Caveats
- iOS sandbox: no `os/exec`/fork; networking OK.
- Device slice ~5.2 MB unstripped; app stripping applies.
- Regenerate after Go changes (candidate Run Script phase; script sandboxing is ON).