Skip to content

proposal: expose ObjC API to gomobile bind programs #17102

@eliasnaur

Description

@eliasnaur

Abstract

Today, gomobile bind can take a set og Go packages and expose their public API to Java or ObjC apps. The proposal is to support the reverse, exposing ObjC API to the bound Go packages.

This is the twin proposal to #16876, describing how ObjC API is accessed from Go.

Motivation

The motivation for this functionality is the same as for Java: to allow more of a mobile app to be written in Go in a convenient way that reduces boiler plate.

Proposed features

Importing ObjC classes and protocols from Go

The Go wrappers for the ObjC API references by the bound packages are generated each time gomobile bind is called. To access a ObjC type, use import statements on the form:

    import "ObjC/Module"

Which is the Go equivalent to @import Module in ObjC.

To access the static methods on a ObjC class, use

    import "ObjC/Module/NSSomeClass"

Static methods

After importing, the resulting packages NSSomeClass will contain the static methods of its ObjC counterpart. For example

import "ObjC/Foundation/NSDate"

will allow Go code to use call NSDate.Date(), which is equivalent to [NSDate date] in ObjC

ObjC classes and protocols

The package "ObjC/Module" contains Go interfaces wrapping every referenced ObjC type in the module. The wrapper types represent their wrapped ObjC types across the language barrier and can be used to call methods on wrapped instances. For example, the following Go function takes an ObjC NSDate instance and returns its description:

import "ObjC/Foundation"

func F(d Foundation.NSDate) string {
    return d.Description()
}

Creating new ObjCinstances

To create a new instance of a Java class, use the New functions defined in the class package. There is a constructor function for every ObjC instance method whose name starts with init. For example the following function performs the equivalent of [[NSObject alloc] init]:

import (
    "ObjC/Foundation/NSObject"
)

func NewNSObject() Foundation.NSObject {
    return NSObject.New()
}

Errors

ObjC methods that return errors are automatically converted to return a Go error result.

Inheriting from ObjC classes or conform to ObjC protocols in Go

Gomobile already exposes exported Go structs to ObjC; this proposal adds support for constructing Go structs directly from ObjC. In addition, Go structs will be able to extend ObjC classes and implement ObjC protocols.

To declare a Go struct that extends or implements ObjC types, use the form:

import "ObjC/UIKit"
import "ObjC/Foundation/NSCopying"

type S struct {
    UIKit.UIResponder// extends UIResponder
    Foundation.NSCopying // implements NSCopying
}

New instances of S are created in ObjC with [[GoPkgS alloc] init]. The default init initializer results in the ObjC instance referring to a new(S) from Go.

Overriding ObjC methods

To override a method from a super class or implement a method from a protocol, declare a Go method with the same name and its first letter capitalized. For example, to override the description method in GoObject:

func (o *GoObject) Description() string {
    ...
}

Exposing this

Whenever an foreign object is passed across the language barrier, a proxy is created to represent it. In the example above, there is a GoObject ObjC instance created in ObjC, and it contains a reference to its counterpart GoObject Go instance in Go. That means that when a Go method is called from ObjC, its method receiver contains the Go instance, while the ObjC instance is only accessible to Java.
To access the Java instance (for passing back to other ObjC APIs), any Go overriding method can declare a this argument with one of the Java types the enclosing class extends or implements. For example, to access the this from the description method, use:

func (o *GoObject) Description(this Foundation.NSObject) string {
    ...
}

The this variable will behave just as if it were a pure ObjC NSObject, and if passed or returned to ObjC, will have the same identity as the ObjC reference.

Calling super

In Go, delegation is achieved through delegation, but in ObjC, the keyword super is needed to access overridden methods. To call a super method from Go, use the Super() method on the this variable:

func (o *GoObject) Descriptionthis Foundation.NSObject) string {
    return this.Super().Description()
}

Type name mangling

In ObjC, classes and protocols have difference namespaces. For example, there is both a NSObject class and a NSObject protocol. In such cases, the Go names for the class has "C" appendded and the protocol has "P" appended. Thus, Foundation.NSObjectC is the name for the Go NSObject class, while Foundation.NSObjectP is the name for the protocol.

Method names

In ObjC, the parameters are named and part of the function signature. The Go name for a method is the upper case first part of the method signature. If that is ambiguous, the full signature is used with colons removed and parameter names upper-cased. The final fallback is the ObjC signature with colons replaced with underscores, resulting in guaranteed unique, albeit ugly, names. For example, the ObjC methods

someMethod
someMethod:
anotherMethod:
anotherMethod:withArgument:
thirdMethod:withArgument

are translated to the following Go method names:

SomeMethod
SomeMethod_
AnotherMethod
AnotherMethodWithArgument
ThirdMethod

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions