Artwork . Games

Scala on iOS

July 24, 2013

Now that RoboVM has started to mature, I decided to see how viable Scala on iOS can be. I have released an sbt plugin, sbt-robovm, that is nearing a 0.1.0 release and added support for that in libgdx-sbt as a proof of concept. After spending many days trying to wrangle Xamarin / IKVM into compiling Scala, it is satisfying to see games running on a real iOS device.

I’ll talk more about the use of libgdx-sbt, especially the iOS bits, another day. Today’s post is going to focus on using the cocoatouch APIs to create an actual Scala iOS app.

RoboVM

RoboVM is a JVM bytecode ahead-of-time compiler and runtime library that can target iOS devices. You should be able to share your backend code between the iOS and Android versions of your app.

At the moment, sbt-robovm does not download and manage a RoboVM distribution install for you like the Maven plugin does, so you will have to get that set up first. Download the latest version and unzip it.

RoboVM searches for the distribution in a few standard places when compiling. The default is $ROBOVM_HOME, so feel free to install anywhere and set the environment variable in your .profile. It will also search in some standard places:

  • ~/Applications/robovm/
  • ~/.robovm/home/
  • /usr/local/lib/robovm/
  • /opt/robovm/
  • /usr/lib/robovm/

sbt-robovm

The next step will be setting up your sbt project to use RoboVM. Until there are giter8 templates ready this is a manual process, but luckily it is pretty straight forward,

Create three files in your project directory:

build.properties

sbt.version=0.12.4

plugins.sbt

resolvers += Resolver.url("scalasbt snapshots", new URL("http://repo.scala-sbt.org/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)

addSbtPlugin("com.hagerbot" % "sbt-robovm" % "0.1.0-SNAPSHOT")

build.scala

import sbt._
import Keys._

import sbtrobovm.RobovmPlugin._

object ScaliOSBuild extends Build {
  lazy val hello = RobovmProject("hello", file("."),
      settings = Defaults.defaultSettings ++ Seq(
        scalaVersion := "2.10.2",
        executableName := "Hello, RoboVM"
      )
    )
  }
}

There are plenty of customizable options, like adding native paths, using an Info.plist file, or setting iOS frameworks to be linked into the executable. For now, all we are going to customize is the name of the app.

Code

First, we need to import some libraries that wrap cocoa touch and set up an entry point for our program.

Main.scala

import org.robovm.cocoatouch.coregraphics._
import org.robovm.cocoatouch.foundation._
import org.robovm.cocoatouch.uikit._

object Main {
  def main(args: Array[String]) {
      val pool = new NSAutoreleasePool()
      UIApplication.main(args, null, classOf[Main])
      pool.drain()
  }
}

Every iOS app needs an application delegate. The one below is both simple enough for a first program and exciting enough to prove that you can do something useful with RoboVM right now. The code should look familiar if you are used to the Objective-C APIs, but the transformation isn’t 1 to 1. You will need to use the documentation and code as a guidebook when translating to the RoboVM way of doing things.

Main.scala

class Main extends UIApplicationDelegate.Adapter {
  var window: UIWindow = _
  var alert: UIAlertView = _

  override def didFinishLaunching(application: UIApplication) {
    alert = new UIAlertView()
    alert.setTitle("Hello, Human!")
    alert.setMessage("Would you like to play a game?")
    alert.addButton("Yes")

    val button = UIButton.fromType(UIButtonType.RoundedRect)
    button.setTitle("Hello, Robo!", UIControlState.Normal)
    button.setFrame(new CGRect(0, 0, 200, 100))

    button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener {
      override def onTouchUpInside(c: UIControl, e: UIEvent) {
        alert.show
      }
    })

    window = new UIWindow(UIScreen.getMainScreen().getBounds())
    window.setBackgroundColor(UIColor.lightGrayColor())

    window.addSubview(button)
    val bounds = button.getSuperview().getBounds()
    button.setCenter(new CGPoint(bounds.origin.x + bounds.size.width / 2, bounds.origin.y + bounds.size.height / 2))

    window.makeKeyAndVisible()
  }
}

The only thing left to do at this point is to test it out on a device or simulator. In sbt-robovm, you can run on a device, iPhone sim, or iPad sim. More options, like retina simulators and building an IPA, will be added in the future.

    $ sbt device
    $ sbt iphone-sim
    $ sbt ipad-sim

You can find the full project source for this demo, as well as many other demos to come, at Scala iOS Demos. Feel free to leave questions and comments there, or send a pull request with a small demo app of your own!

The Future

There is a lot of room here for building DSLs and wrapper libraries on top of Scala to make app development simpler. RoboVM shares the same niche as RubyMotion in this regard, and it will be interesting to see what we can learn from that community.

RoboVM is still in alpha, but it is already proving itself useful. To a bright and exciting future.