You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
gomobile bind -target ios does not allow one to declare an interface in go, subclass that interface in swift, and then pass an instance of that subclass to go. When the instance is passed to go a runtime error occurs:
go_seq_go_to_refnum on objective-c objects is not permitted'
classMyMachine:gostuffMachine{func work(_ data:String!){}}letm=MyMachine()gostuffRun(m) // this line causes the runtime exception
The exception is really thrown in the generated code for Run when the incoming objc object is converted to a refnum.
if ([machineconformsToProtocol:@protocol(goSeqRefInterface)]) {
id<goSeqRefInterface>machine_proxy= (id<goSeqRefInterface>)(machine);
_machine=go_seq_go_to_refnum(machine_proxy._ref); // I believe its this line
} else {
_machine=go_seq_to_refnum(machine);
}
As far as I can tell this is because gostuffMachine has two declarations, one as a @protocol and one as an @interface
When the swift side goes to implemet Machine it picks up the one declared as an interface rather than the protocol (I'm not an objc/swift expert so I don't understand the subtleties here). In any case, the interface version has an initWithRef constructor that only the go code knows how to call when a Machine instance is constructed from go. When swift creates a subclass of Machine it does not call the initWithRef constructor, so _ref is null, and ultimately when the C code gets to
if ([x conformsToProtocol:@protocol(goSeqInterface)])
This condition is true, due to the interface inheriting from goSeqRefInterface, and then the C code dies at trying to invoke go_seq_go_to_refnum(machine_proxy.ref).
The fix I have made so far is to append the word Protocol to the protocols generated by genobjc.go. Essentially this patch:
diff --git a/bind/genobjc.go b/bind/genobjc.go
index 7595fa0..f99ee40 100644
--- a/bind/genobjc.go
+++ b/bind/genobjc.go
@@ -168,7 +168,7 @@ func (g *ObjcGen) GenH() error {
g.Printf("@class %s%s;\n", g.namePrefix, s.obj.Name())
}
for _, i := range g.interfaces {
- g.Printf("@protocol %s%s;\n", g.namePrefix, i.obj.Name())
+ g.Printf("@protocol %s%sProtocol;\n", g.namePrefix, i.obj.Name())
if i.summary.implementable {
g.Printf("@class %s%s;\n", g.namePrefix, i.obj.Name())
// Forward declaration for other cases will be handled at the beginning of GenM.
@@ -874,7 +874,7 @@ func (g *ObjcGen) genInterfaceInterface(obj *types.TypeName, summary ifaceSummar
}
prots := []string{"goSeqRefInterface"}
if isProtocol {
- prots = append(prots, fmt.Sprintf("%[1]s%[2]s", g.namePrefix, obj.Name()))
+ prots = append(prots, fmt.Sprintf("%[1]s%[2]sProtocol", g.namePrefix, obj.Name()))
}
g.Printf(" <%s>", strings.Join(prots, ", "))
g.Printf(" {\n}\n")
@@ -899,7 +899,7 @@ func (g *ObjcGen) genInterfaceH(obj *types.TypeName, t *types.Interface) {
g.genInterfaceInterface(obj, summary, false)
return
}
- g.Printf("@protocol %s%s <NSObject>\n", g.namePrefix, obj.Name())
+ g.Printf("@protocol %s%sProtocol <NSObject>\n", g.namePrefix, obj.Name())
for _, m := range makeIfaceSummary(t).callable {
if !g.isSigSupported(m.Type()) {
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
In fact that blog post does exactly what I'm trying to accomplish, and even says the protocol name ends with Protocol, yet clearly the genobjc.go file in gomobile does not name protocols this way. The naming of protocols seems to have been done in bf2ca7a93b02169b6f8c221ba13b22919a728994, from 2015.
I have not tested this change with pure objc projects, but I don't think it breaks any functionality there (except for changing naming, same as in swift).
If you need a small test case I can produce one.
The text was updated successfully, but these errors were encountered:
Ugh nevermind on this, it was my mistake. The objc protocol can be used directly in swift already by appending the Protocol name.
The generated x.h objc header
@protocolMachine@end@interfaceMachine@end
classMyMachine:MachineProtocol{}
Now everything works fine, I can pass an instance of MyMachine directly to go.
I also see that the medium blogpost I referenced discuss this exact point, but I glossed over it before. It would be useful if the gomobile documentation referenced this point perhaps.
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
The go version is irrelevant, as this is a problem with gomobile.
What operating system and processor architecture are you using (
go env
)?go env
OutputReport (with a fix)
gomobile bind -target ios
does not allow one to declare an interface in go, subclass that interface in swift, and then pass an instance of that subclass to go. When the instance is passed to go a runtime error occurs:Essentially my code is
x.go
x.swift
The exception is really thrown in the generated code for
Run
when the incoming objc object is converted to a refnum.As far as I can tell this is because
gostuffMachine
has two declarations, one as a@protocol
and one as an@interface
When the swift side goes to implemet
Machine
it picks up the one declared as an interface rather than the protocol (I'm not an objc/swift expert so I don't understand the subtleties here). In any case, the interface version has aninitWithRef
constructor that only the go code knows how to call when aMachine
instance is constructed from go. When swift creates a subclass ofMachine
it does not call theinitWithRef
constructor, so_ref
is null, and ultimately when the C code gets toThis condition is true, due to the interface inheriting from
goSeqRefInterface
, and then the C code dies at trying to invokego_seq_go_to_refnum(machine_proxy.ref)
.The fix I have made so far is to append the word
Protocol
to the protocols generated by genobjc.go. Essentially this patch:This question was asked on stack overflow: https://stackoverflow.com/questions/50815664/gomobile-binding-callbacks-for-objc
and that question was also added to the end of an old gomobile issue but left unaddressed: #17102 (comment)
Anyway, I was initially quite confused about the default behavior of gomobile. Based on the following blog post I had assumed this would all work out fine: https://medium.com/@matryer/tutorial-calling-go-code-from-swift-on-ios-and-vice-versa-with-gomobile-7925620c17a4
In fact that blog post does exactly what I'm trying to accomplish, and even says the protocol name ends with
Protocol
, yet clearly the genobjc.go file in gomobile does not name protocols this way. The naming of protocols seems to have been done in bf2ca7a93b02169b6f8c221ba13b22919a728994, from 2015.I have not tested this change with pure objc projects, but I don't think it breaks any functionality there (except for changing naming, same as in swift).
If you need a small test case I can produce one.
The text was updated successfully, but these errors were encountered: