/sbt-robovm

An sbt plugin for iOS development in Scala

Primary LanguageScalaBSD 2-Clause "Simplified" LicenseBSD-2-Clause

sbt-robovm

sbt-robovm is a plugin for the Scala build tool that aims to make it as simple as possible to compile Scala (and Java) code to binaries for iOS, linux, and OSX using RoboVM

Setup

  1. Install Xcode 6
  2. Install JDK 7
  3. Install sbt
  4. See roboscala-samples for example on how to use and configure

Add the Plugin

First, add the plugin to your project by appending addSbtPlugin("org.roboscala" % "sbt-robovm" % "1.12.0") into the project/plugins.sbt file. The file name (not extension) may actually be different, but such is the convention. The plugin's version is in sync with the RoboVM version it uses, so it should always be clear which RoboVM is being used.

Project Creation

All you have to do to use the plugin, is to add iOSRoboVMSettings key (or nativeRoboVMSettings if you are creating a native project) to your build.sbt file.

If you are creating a multi-project build, prepend that to your settings Seq:

lazy val myproject = Project(id = "myproject", base = file("myproject"), settings = iOSRoboVMSettings ++ Seq(
	/* More settings */
))

Tasks

There are different tasks defined for iOS and native console projects.

Shared

  • robovmLicense
    • Allows you to enter your RoboVM license key to get access to premium features, such as line numbers in stack traces, debugger support and interface builder integration.

iOS

  • iphoneSim and ipadSim
    • Build and run the app in a iPhone or iPad simulator, respectively.
  • simulator
    • Build and run the app on a simulator specified by the robovmSimulatorDevice setting.
  • device
    • Build and run the app on a connected device.
    • It is possible to specify the order of preference of devices using the robovmPreferredDevices task.
    • Otherwise, the plugin will attempt to connect to the last device it has used.
  • ipa
    • Create the .ipa archive for upload to the App Store or other distribution.
  • simulatorDevices
    • Print all installed simulator devices.

Native

  • native
    • Build and run a native console application.
    • Connecting the input to interactive apps is not implemented. Recommended workaround is to execute compiled binary (in target/robovm/) in separate Terminal window.
  • nativeBuild
    • Same as native, but does not execute the binary.

Settings

As with tasks, there are some settings that are only meaningful in iOS projects. Some settings are actually implemented as tasks.

Shared

  • robovmConfiguration Either[File,Elem]
    • The most important key, specifies the configuration of your app, the robovm.xml file
    • If you have a real file, set it to Left(file("path-to-your/robovm.xml"))
    • If you want to specify it in-place, use the built-in scala support for XML literals: Right(<config> ... </config>)
    • See examples in the sample repository
  • robovmProperties Either[File, Map[String, String]]
    • robovm.properties file contains key-value pairs substituted into robovm.xml and Info.plist
    • If you have a real file with these, set this to Left(file("path-to-your-file"))
    • If you wish to generate these in your build script: Right(Map("some-key" -> "some-value", ...))
    • By default, this contains set of useful values:
      • app.name - Value of name sbt key
      • app.executable - Name of executable derived from name sbt key
      • app.mainclass - Fully specified main class of your application, either detected or specified in mainClass key
  • robovmTarget64bit Boolean
    • Whether to build 64bit executables for the device
    • Default is false therefore you will need to set it to true if your device is newer and has a 64bit processor
  • robovmHome Config.Home
    • Return the home of RoboVM installation.
    • By default, this task downloads RoboVM distribution into a local maven repository and unpacks it there, so there is no need to touch this unless you have a good reason to.
  • robovmInputJars Seq[File]
    • Jars and classes to feed into the RoboVM compiler
    • By default, this returns fullClasspath, which is in most cases correct. You may want to override this if you want to modify the compiled classes first somehow (for example when using ProGuard).
  • robovmVerbose Boolean
    • Setting this to true will propagate RoboVM debug-level messages to info-level
    • Useful when debugging RoboVM or plugin, otherwise not so much
  • robovmDebug Boolean
    • Whether to enable RoboVM debugger
    • See Debugging section below first
    • Needs commercial license, run robovmLicense task to enter yours
    • Port can be specified with robovmDebugPort
  • robovmDebugPort Int
    • Port on which RoboVM debugger will listen (when enabled, see robovmDebug)

iOS Only

  • robovmProvisioningProfile Option[String]
    • Specify provisioning profile to use when signing iOS code
    • Profile can be specified by name, UUID, app prefix, etc.
    • See Tips section
  • robovmSigningIdentity Option[String]
    • Specify signing identity to use when signing iOS code
    • Signing identity can be specified by name, fingerprint, etc.
    • See Tips section
  • robovmSimulatorDevice Option[String]
    • Name of device to be used in simulator task
    • Use simulatorDevices task to list all installed devices
  • robovmSkipSigning Option[Boolean]
    • Setting this to Some(true/false) overrides default signing behavior and allows you to test without proper certificates and identities
  • robovmPreferredDevices Seq[String]
    • List of iOS device ID's listed in the priority in which you want to connect to them if multiple devices are connected
  • robovmIBScope Scope
    • Scope in which interfaceBuilder command operates. Defaults to ThisScope.
    • Only reason to change this is if you have a custom configuration

Debugging (licensed only)

Line numbers will be enabled automatically when the license is entered (see robovmLicense task).

To use the RoboVM debugger, prefix your task invocations with debug: (example: $ sbt myproject/debug:ipadSim). This sets the scope to the Debug configuration, in which the debugger is enabled and the debug port is set, by default to 5005. Running with the debugger enabled will allow you to connect to a running application with a java debugger.

Using the debugger from IntelliJ IDEA 14

  1. Create a new "Remote" Run/Debug configuration
    • Top bar, right next to the "Make" button -> "Edit configurations" -> "+" -> "Remote"
    • All settings can be left to their default values
  2. Run the project in debug mode from SBT (for example $ sbt debug:ipadSim)
  3. Make sure the configuration from step 1 is selected and press the "Debug" button
  4. IntelliJ will connect to the running application and you can start debugging like you are used to with standard Java debugging

Application execution will pause before your main method and wait for the debugger to attach. Then it will continue normally.

Interface Builder (licensed only)

This plugin offers a basic integration with XCode's Interface Builder. There are some excellent tutorials on how to use IB with IntelliJ on RoboVM website. Getting familiar with them is recommended, since the workflow in sbt is similar.

In the core of this feature is an interactive interfaceBuilder command. Run the command inside your iOS project, it will generate XCode project and open it in the Interface Builder. Then it will watch your code sources and when any of them change, it will recompile the project and update the XCode project accordingly. XCode will show new IBOutlets and IBActions very shortly after that.

You will also notice, that the prompt in the sbt console will change to "interfaceBuilder >". That notes that you are in a special mode, where the interfaceBuilder command is still running, but you can still run any commands/tasks as usual, so you can, for example, run the ipadSimulator task to quickly view your changes on device. Pressing enter, without any command, will exit the interfaceBuilder mode and you will be back to standard sbt prompt.

Because interfaceBuilder is a command and not a task (for technical reasons), it can not be scoped. Therefore, doing something like myProject/interfaceBuilder will not work. To work around this, use project myProject command first, to switch active project to that and then run interfaceBuilder. If you need even more granular scoping, use the robovmIBScope setting.

Tips

  • All paths in the configuration are relative to the base directory.
  • During typical development, you usually end up with two pairs of signing identity and profile, one for development and one for distribution. It is possible to scope the robovmSigningIdentity/Profile keys to automatically use the distribution pair when building an ipa:
robovmProvisioningProfile := Some("name of development profile"),
robovmSigningIdentity := Some("name of development identity"),
robovmProvisioningProfile in ipa := Some("name of distribution profile"),
robovmSigningIdentity in ipa := Some("name of distribution identity")
  • You can download simulators for more iOS versions in Xcode. Xcode includes only the latest iOS simulator by default.
  • The first time you try to compile a program, RoboVM must compile the Java and Scala standard libraries. This can take a few minutes, but the output of this process is cached. Subsequent compilations will be much faster.
  • If you are having issues after installing Xcode, open Xcode and agree to the license or open a Terminal and run xcrun.

Hacking on the plugin

If you need to make modifications to the plugin itself, you can compile and install it locally:

$ git clone git://github.com/roboscala/sbt-robovm.git
$ cd sbt-robovm
$ sbt publish-local

When testing your changes, it is useful to publish locally with different version than what is officially used. That is because if you have already used the official version, your testing projects will most likely use that and not your modified version. To workaround that, change in sbt-robovm's build.sbt:

    version := roboVMVersion.value,

to

    version := roboVMVersion.value + "-YOUR_SUFFIX",

and in the project/plugins.sbt of your (testing) project, instead of standard installation:

// Necessary only when testing with RoboVM snapshot build, such as 1.11.1-SNAPSHOT
resolvers += Resolver.sonatypeRepo("snapshots")

addSbtPlugin("org.roboscala" % "sbt-robovm" % "1.12.0-YOUR_SUFFIX" changing())

Unless you need to use the SNAPSHOT version of RoboVM, it is easier to work with stable version, because for SNAPSHOT dependencies, sbt has to check for new version each run, which adds latency to testing.

However, when working on the plugin, you want it to be "redownloaded" each time it changes, and the changing() in addSbtPlugin line does exactly that. But don't worry, thanks to it being installed/published locally, the latency of these checks is negligible.

Contributing

Reporting any issues you encounter helps. If you want to help improving the plugin, feel free to make a PR.

Projects using the plugin

libgdx-sbt-project.g8 (Uses older version)