-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
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