Using Go from iOS Apps

February 19th, 2016

gomobile , you could easily share code between Android, iOS, and your server. The gomobile tool lets you compile your Go code so that you can call it from an iOS app. Using, you could easily share code between Android, iOS, and your server.

This article will go over how to do this using Xcode 7.2. If you do not have Xcode, you can download it from the Xcode Download Page . It is free, and also you can install apps directly on your phone without a paid developer account.

Creating an App in Xcode

Open Xcode and select Create a new Xcode project from the splash screen. Choose the type iOS->Application->Game and name it whatever you want. For Language select Objective-C and for Game Technology select OpenGL ES . Once you have the app created, run the app by selecting Product->Run . You should see the iPhone simulator pop up with two cubes orbiting each other.

Putting Go in Your iOS

The first thing to do is to get the simplest possible library running on iOS:

package example func Add ( x int , y int ) int { return x + y }

go get goroutines.com/ios-example

Setup the gomobile tool:

go get golang . org / x / mobile / cmd / gomobile gomobile init

Compile your library into a .framework file:

go get goroutines . com / ios - example gomobile bind - target ios goroutines . com / ios - example

This will create a file named Example.framework in the current directory. Drag that file into your Xcode project and make sure Copy items if needed is checked. Open AppDelegate.m and add the following lines:

// at the top of the file # import < Example / Example . h > // inside - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions NSLog ( @ "add: %d" , GoExampleAdd ( 1 , 2 ) ) ;

add: 3 to the console. Now you can call all the Go code you want from iOS. Well maybe not all the code you want. There are When you run the app, you will see this outputto the console. Now you can call all the Go code you want from iOS. Well maybe not all the code you want. There are type restrictions . Functions can return a pointer to a struct, for instance, but not the entire struct.

Drawing Triangles

NSLog() is okay, but let's draw some triangles using OpenGL.

var blue float32 = 0.8 var delta float32 = 0.03 func Frame ( ) { // change the background color a little bit each frame C . glClearColor ( 1.0 , 0.3 , C . GLfloat ( blue ) , 1.0 ) if blue + delta < 0.0 { delta = 0.03 } if blue + delta > 1.0 { delta = - 0.03 } blue + = delta // clear the screen to the background color C . glClear ( C . GL_COLOR_BUFFER_BIT | C . GL_DEPTH_BUFFER_BIT ) // draw some triangles C . glDrawArrays ( C . GL_TRIANGLES , 0 , 24 ) }

go get goroutines.com/ios-triangles

Install this example and build it:

go get goroutines . com / ios - triangles gomobile bind - target ios goroutines . com / ios - triangles

Copy Triangles.framework into your project. Make sure to remove Example.framework since multiple Go frameworks don't seem to work well together.

Replace the contents of the file GameViewController.m with this:

# import "GameViewController.h" # import < OpenGLES / ES2 / glext . h > # import < Triangles / Triangles . h > @ implementation GameViewController - ( void ) viewDidLoad { [ super viewDidLoad ] ; EAGLContext * context = [ [ EAGLContext alloc ] initWithAPI : kEAGLRenderingAPIOpenGLES2 ] ; if ( ! context ) { NSLog ( @ "Failed to create ES context" ) ; return ; } GLKView * view = ( GLKView * ) self . view ; view . context = context ; view . drawableDepthFormat = GLKViewDrawableDepthFormat24 ; [ EAGLContext setCurrentContext : context ] ; GoTrianglesSetup ( ) ; } - ( BOOL ) prefersStatusBarHidden { return YES ; } - ( void ) glkView : ( GLKView * ) view drawInRect : ( CGRect ) rect { GoTrianglesFrame ( ) ; } @ end

This sets up the OpenGL context used by the Go code. It calls GoTrianglesSetup() to setup the OpenGL state and 30 times per second it calls GoTrianglesFrame() to draw the current frame.

Run the code on your simulator or device and you should see a screen like this:

In Setup() , the Go code compiles shaders, sets the OpenGL state, and gives OpenGL a buffer of vertices. On each frame it calls Frame() , which clears the screen (to a slightly different color from the previous frame) and draws the same shape made out of a few triangles.

Xcode comes with a rather nice OpenGL debugger that is able to capture the calls from the Go code, so if you make any errors it helps a lot with finding them.

Calling Objective-C Functions from Go

When writing an iOS app, you often need to call iOS library functions that are written in Objective-C. There are some cgo features that let you do this from Go:

package objc /* #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Foundation -framework UIKit #import <UIKit/UIKit.h> void PrintIOSVersion(void) { NSLog(@"ios version: %@", [[UIDevice currentDevice] systemVersion]); } */ import "C" func CallPrintIOSVersion ( ) { C . PrintIOSVersion ( ) }

go get goroutines.com/ios-objc

Calling GoObjcCallPrintIOSVersion() from your iOS app will call the C function PrintIOSVersion() which calls the Objective-C function [[UIDevice currentDevice] systemVersion] .

Running Go on an Actual Phone

Running the app on an actual phone is annoying, but possible. Plug your phone into your computer. Go to Product->Destination and select your phone from the list of devices. Then select Product->Run . Xcode will tell you Failed to code sign , so click Fix Issue . It will then say Could not launch . Take out your iPhone and go to Settings->General->Device Management->(your apple id email address) and select the Trust button so that your app is given permission to launch on the phone. To run Go code on your device you will also need to set Enable Bitcode to No under the Build Settings for your app.

Building the Entire App in Go