Documentation
¶
Overview ¶
Package objc is a pure-Go (CGO_ENABLED=0) bridge to the macOS Objective-C runtime. It reaches the OS entirely through github.com/ebitengine/purego — dlopen + objc_msgSend — so it links with no cgo and no shelling out to osascript.
It exists to end the copy-paste of the same Objective-C plumbing across the fleet's CGO=0 macOS code (go-macos/notify, go-widgets/tray, the go-news-reader and go-reddit readers, …): every one of them independently re-implemented selector/class lookup, NSString<->Go bridging, framework dlopen, autorelease-pool handling and a run loop. This package is the single shared home for those primitives.
Surface ¶
- Sel / GetClass / ClassID: cached selector and class lookup.
- [ID.Send] and the generic Send: typed objc_msgSend helpers (the generic form carries float64/CGFloat and struct returns through the correct calling convention).
- RegisterClass: define an Objective-C class at runtime from Go method closures.
- NSString / GoString: NSString<->Go string conversion. GoString copies via -getCString:maxLength:encoding: (never a raw -UTF8String pointer deref, which trips go vet's unsafeptr check).
- Stringify / DictToMap / MapToDict: NSObject/NSDictionary helpers.
- Load plus the framework-path constants (Foundation, AppKit, WebKit, CoreFoundation, LibSystem): idempotent dlopen.
- AutoreleasePool: run a closure inside an NSAutoreleasePool scope.
- App / RunApp: the shared NSApplication and its [NSApp run] loop.
- Run: a Foundation run-loop runner pinned to a locked OS thread, with a task queue serviced on that thread — for observer-driven Foundation work (e.g. NSDistributedNotificationCenter).
Portability ¶
Every exported symbol is defined on all platforms so consumers cross-compile. On non-darwin GOOS the runtime entry points report ErrUnsupported (or return a zero ID); the OS-independent logic — the selector/class caches, name validation and the string-marshalling core — stays fully functional and testable there.
Index ¶
- Constants
- Variables
- func AutoreleasePool(fn func())
- func DictToMap(dict ID) map[string]string
- func GoString(id ID) string
- func Load(paths ...string) error
- func Run(ctx context.Context, runnerClass Class, setup func(r *Runner)) error
- func RunApp(policy int)
- func Send[T any](id ID, sel SEL, args ...any) T
- func Stringify(v ID) string
- func ValidateName(name string) error
- type Class
- type ID
- type IMP
- type MethodDef
- type Runner
- type SEL
Constants ¶
const ( Foundation = "/System/Library/Frameworks/Foundation.framework/Foundation" AppKit = "/System/Library/Frameworks/AppKit.framework/AppKit" WebKit = "/System/Library/Frameworks/WebKit.framework/WebKit" CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" Security = "/System/Library/Frameworks/Security.framework/Security" LibSystem = "/usr/lib/libSystem.B.dylib" )
Standard framework and dylib paths for Load.
Variables ¶
var ( // ErrUnsupported is returned by the runtime entry points on non-darwin // platforms (the whole runtime is macOS-only). ErrUnsupported = errors.New("objc: unsupported on this platform (darwin only)") // ErrEmptyName is returned when a selector or class name is empty. ErrEmptyName = errors.New("objc: empty name") // ErrNameHasNUL is returned when a selector or class name contains a NUL // byte (the C runtime terminates names at the first NUL, so an embedded one // would silently truncate). ErrNameHasNUL = errors.New("objc: name contains NUL byte") // ErrDlopen wraps a framework dlopen failure from [Load]. ErrDlopen = errors.New("objc: dlopen failed") )
Errors reported by the package. They are stable and may be tested with errors.Is.
Functions ¶
func AutoreleasePool ¶
func AutoreleasePool(fn func())
AutoreleasePool runs fn inside a fresh NSAutoreleasePool, draining the pool when fn returns (even if it panics). Use it around a burst of autoreleased allocations on a thread that has no ambient pool.
func DictToMap ¶
DictToMap flattens an NSDictionary to map[string]string, rendering both keys and values with Stringify. The nil dictionary yields an empty map.
func GoString ¶
GoString copies an NSString's UTF-8 bytes into a Go-owned buffer and returns them. It returns "" for the nil object or an empty string. It fills a Go slice via -getCString:maxLength:encoding: rather than dereferencing the ObjC-owned -UTF8String pointer, so it stays free of any uintptr->Pointer arithmetic (the buffer handed to ObjC is a live Go pointer the collector tracks).
func Load ¶
Load dlopens each of paths (idempotently — dlopen refcounts, and a repeat open of an already-resident framework is cheap). It returns the first failure wrapped in ErrDlopen. Use the framework-path constants, e.g. Load(Foundation, AppKit).
func Run ¶
Run drives a Foundation run loop on the calling goroutine — which it pins to its OS thread — until ctx is cancelled, then returns ctx.Err(). It creates an instance of runnerClass (register it with RegisterClass), calls setup with the Runner once the instance exists (attach your observers there, using r.Object() and r.Submit), and services work queued through r.Submit on this thread. It is the mechanism behind observer-driven Foundation delivery such as NSDistributedNotificationCenter, which requires a running CFRunLoop.
runnerClass must implement a no-op keepAlive: method (a repeating timer targets it to keep the loop from busy-spinning before observers attach). Call Run on a dedicated goroutine; it blocks for the lifetime of ctx.
func RunApp ¶
func RunApp(policy int)
RunApp sets the shared application's activation policy and enters the AppKit run loop ([NSApp run]), which blocks until the application terminates. It must be called on the process main OS thread. policy is an NSApplicationActivationPolicy value (0 = Regular, 1 = Accessory/menu-bar).
func Send ¶
Send sends selector sel to id with args and returns the result typed as T. Use it when the message returns a scalar or a by-value struct (e.g. a CGFloat via Send[float64] or an NSRect via a matching struct), so purego marshals the return through the correct calling convention. For an object (or ignored) result, use the inherited ID.Send, which returns an ID.
func Stringify ¶
Stringify renders any object as a Go string: NSStrings directly, anything else through -description (so a non-string value degrades to its textual form rather than crashing the getCString path). The nil object yields "".
func ValidateName ¶
ValidateName rejects names the C runtime cannot carry: empty (ErrEmptyName) or containing a NUL byte (ErrNameHasNUL, since objc_registerName / objc_getClass terminate at the first NUL and would silently truncate). It is a convenience for consumers that build selector or class names from untrusted input before passing them to Sel, GetClass or RegisterClass.
Types ¶
type Class ¶
Class is an Objective-C class object.
func GetClass ¶
GetClass returns the class named name, caching the result. An unknown class (or the zero name) yields the zero class.
func RegisterClass ¶
RegisterClass defines a new Objective-C class named name with superclass super and the given instance methods, and returns it. Each method's Fn is a Go closure whose first two parameters are the receiver ID and the invoked SEL; RegisterClass wraps it into an IMP. It is the runtime-class mechanism behind delegates, timer targets and menu-action handlers. (Instance variables and protocols are intentionally omitted; the fleet's classes need neither.)
type ID ¶
ID is an Objective-C object pointer (`id`); the zero value is nil.
func App ¶
func App() ID
App returns the shared NSApplication instance (+[NSApplication sharedApplication]), creating it on first call.
func ClassID ¶
ClassID returns the class named name as an ID, so class ("factory") messages (alloc, sharedApplication, …) can be sent to it directly.
type MethodDef ¶
MethodDef binds a selector to a Go closure for RegisterClass. Its Fn's first two parameters must be (ID, SEL); RegisterClass wraps it into an IMP.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner is the live handle to a Run loop. Its methods are safe to call from any goroutine; each marshals its work onto the run-loop thread.
type SEL ¶
SEL is an Objective-C selector.
func RegisterName ¶
RegisterName is an alias for Sel. It carries purego's spelling so a call site migrated from github.com/ebitengine/purego/objc compiles unchanged while gaining the selector cache.