proposal: expose ObjC API to gomobile bind programs #17102

Closed
eliasnaur opened this Issue Sep 14, 2016 · 10 comments

5 participants

@eliasnaur
Contributor

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
@gopherbot

CL https://golang.org/cl/29175 mentions this issue.

@gopherbot

CL https://golang.org/cl/29174 mentions this issue.

@gopherbot

CL https://golang.org/cl/29173 mentions this issue.

@adg
Contributor
adg commented Sep 26, 2016
@joeblew99

This is nice.
I played around last week with calling Java code from go, and this is the same for IOS.
Its going to be really interesting how people use this.

@quentinmit quentinmit modified the milestone: Proposal Oct 4, 2016
@gopherbot gopherbot pushed a commit to golang/mobile that referenced this issue Oct 5, 2016
@eliasnaur eliasnaur internal/importers: introduce package to analyze ObjC types
The objc package adds a parser that uses the clang -cc1 -ast-dump
command to extract type information about ObjC classes and protocol.

The resulting type information is needed to generate ObjC API wrappers
in Go.

This is the first part of the implementation of proposal golang/go#17102.

For golang/go#17102

Change-Id: I8382b54c0bd315703ec5a62cc177e1a2ace061e9
Reviewed-on: https://go-review.googlesource.com/29173
Reviewed-by: David Crawshaw <crawshaw@golang.org>
6ea0bb5
@gopherbot gopherbot pushed a commit to golang/mobile that referenced this issue Oct 16, 2016
@eliasnaur eliasnaur bind,cmd: add generator for ObjC API wrappers
Using the new ObjC type analyzer API, scan the bound packages for
references to ObjC classes and protocols and generate Go wrappers for them.

This is the second part of the implementation of proposal golang/go#17102.

For golang/go#17102

Change-Id: I773db7b0362a7ff526d0a0fd6da5b2fa33301144
Reviewed-on: https://go-review.googlesource.com/29174
Reviewed-by: David Crawshaw <crawshaw@golang.org>
6ecf8ee
@gopherbot gopherbot pushed a commit to golang/mobile that referenced this issue Oct 17, 2016
@eliasnaur eliasnaur bind,cmd: accept ObjC wrapper types in bound packages
Accept ObjC API wrapper types as arguments and return values from
bound Go package functions and methods. Also, allow Go structs
to extend ObjC classes and implement ObjC protocols as well as override
and implement methods.

This is the third and final part of the implementation of the golang/go#17102
proposal.

Fixes golang/go#17102

Change-Id: I601d90fb6d22b8d6f8b7d5fe0130daa1a4dd4734
Reviewed-on: https://go-review.googlesource.com/29175
Reviewed-by: David Crawshaw <crawshaw@golang.org>
1c49d29
@gopherbot gopherbot pushed a commit to golang/mobile that closed this issue Oct 17, 2016
@eliasnaur eliasnaur bind,cmd: accept ObjC wrapper types in bound packages
Accept ObjC API wrapper types as arguments and return values from
bound Go package functions and methods. Also, allow Go structs
to extend ObjC classes and implement ObjC protocols as well as override
and implement methods.

This is the third and final part of the implementation of the golang/go#17102
proposal.

Fixes golang/go#17102

Change-Id: I601d90fb6d22b8d6f8b7d5fe0130daa1a4dd4734
Reviewed-on: https://go-review.googlesource.com/29175
Reviewed-by: David Crawshaw <crawshaw@golang.org>
1c49d29
@joeblew99
joeblew99 commented Oct 17, 2016 edited

@crawshaw @rakyll @hyangah
I would be allot happier if an example can be added to the main examples...

https://github.com/golang/mobile/tree/master/example

@eliasnaur
Contributor

There is some documentation and an Android example incoming in https://go-review.googlesource.com/c/31170/

There is not iOS app example yet, but you can check out
https://github.com/golang/mobile/tree/master/bind/testpkg/objcpkg in the meantime.

@joeblew99
joeblew99 commented Oct 18, 2016 edited

@eliasnaur thanks for reply. Ok good to know.

i see from the docs its called "direct integration mode" Which leads me to ask this side question:
gomobile when building complete apps (not binding & not direct integration mode ), compiles into a NativeActivity as i understand it. Can one of these exposed http services locally and run as a daemon service ?
Or can one use the "direct integration mode" to build local daemon / services that expose http services locally.

@eliasnaur
Contributor

I don't see why not.
tir. 18. okt. 2016 kl. 13.18 skrev jow blew notifications@github.com:

@eliasnaur https://github.com/eliasnaur thanks for reply. Ok good to
know.

Side question:
gomobile when building complete apps (not binding), compiles into a
NativeActivity as i understand it. can one of these exposed http services
locally and run as a daemon service ?


You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
#17102 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAgCDKpA8z5qnX3zuB_SpPOsCZJEmN-wks5q1KshgaJpZM4J8600
.

@joeblew99

@eliasnaur
It would be nice if an equivalent IOS example was added that does the same thing as Reverse at https://go-review.googlesource.com/#/c/31170/.
Maybe it can be added after this new code settles down..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment