Solar2D Native — iOS

The following resources go further in depth on using Solar2D Native for iOS.

Solar2D Native Setup

The following steps will help you set up your native development environment such that it’s optimized for integration with Solar2D Native.

  1. Within the /Applications/Corona/Native/ folder, double click Setup Corona Native. This will create the symbolic link required for the project templates.

  2. To develop native iOS applications, you’ll need to install Xcode, available from the Mac App Store or for direct download. If you already have Xcode installed, ensure you’ve updated to the latest version.

  3. You must have an Apple Developer account to install/test apps on iOS devices and publish to the App Store. Once you’ve established your account, you’ll need to integrate it with Xcode. This process is outlined here.

  4. If your iOS development environment is working correctly, you should be able to build a native iOS sample project such as MyLife. Additionally, you should be able to deploy and run the sample on a device.

Project Templates

New projects can easily be created by using template files as a starting point. Inside the /Applications/Corona/Native/Project Template/App/ folder, the following files/directories are of primary concern:

  • Corona — This folder represents a classic Corona project, containing files like main.lua, app icons, etc. By default, ios/App.xcodeproj is set up to assume that these files reside inside this exact folder (Corona).
  • ios — Contains all of the iOS-specific code, projects, etc.
    • App.xcodeproj — This is the main project for building and running your app within Xcode. We recommend that you use this project template as the starting point for your Xcode project since it contains custom build phases that take care of packaging your Lua sources and resource files (images, audio, etc.). It also allows you to structure your native extensions as plugins that can be re-used in future projects.
    • AppCoronaDelegate.h / AppCoronaDelegate.mm — The main application entry points go here. The class conforms to the CoronaDelegate protocol, so you will get notified immediately before and after main.lua is loaded and executed. You can also add UIApplicationDelegate methods. We recommend that you add new Lua libraries via plugins.
    • Plugin/ — For plugins, this is where the implementation of plugin.library goes.
      • PluginLibrary.h / PluginLibrary.mm — These implement plugin.library.
    • Plugin.xcodeproj — For plugins, this is the main project that contains the Lua library that you are exposing to your application. This is a dependency of App.xcodeproj, so it will get built automatically. You only need to open this project if you want to build a plugin separately.
    • CoronaNative.xcconfig — Configuration file containing common setup elements for Solar2D Native (paths to Corona and Lua header files, libraries, and linker options).
    • CoronaApp.xcconfig — Configuration file generated when building “Download Plugins” target. This configuration may include information necessary to use plugins specified in build.settings. It also includes CoronaNative.xcconfig.
Project Flow

The following steps briefly outline the flow of the /Applications/Corona/Native/Project Template/App/ project for iOS:

  1. ios/main.mm — At launch time, MyCoronaDelegate is registered as the class implementing the CoronaDelegate interface. Corona will instantiate an instance of this class and assume the designated initializer is init.

  2. ios/AppCoronaDelegate.mm — Right before Corona/main.lua is invoked, the willLoadMain method is invoked. At this point, OpenGL is set up and all Corona frameworks are available.

  3. Corona/main.lua — In the Lua code, plugin.library is loaded via require(). The Corona engine will then look for a corresponding C function called luaopen_plugin_library and invoke it. The name of this function is dynamic and is constructed using the original library name passed to require(). It takes that name and adds a prefix luaopen_ to it, then replaces . with _.

  4. ios/Plugin/PluginLibrary.mm — When luaopen_plugin_library is invoked, it calls PluginLibrary::Open which does all of the heavy lifting, for example creating the Lua library table, registering the Lua methods like show(), and then leaving the table at the top of the Lua stack.

Solar2D Native Development

Bridging Lua/C

To bridge Lua and C code, you’ll use functionality made available by the Lua C API. This allows you to add libraries and functions in Lua that call directly into C.

Every function in the Lua C API takes the lua_State pointer L as its first argument. You can get access to the proper lua_State variable via an instance of the CoronaRuntime.

In order to use the Lua C API successfully, you must understand the Lua stack. This stack is different from a function call stack; it is designed to ease the marshalling of data across the Lua-C bridge. See here for more information.

Native APIs

The following are specific to Solar2D Native for iOS:

Native API Description
CoronaDelegate Your code must implement this protocol if you wish to add your own functions at the Lua layer. You can also intercept UIApplicationDelegate events via your implementation of this protocol.
CoronaRuntime Key objects including the Lua state used by the Corona engine are accessible via this protocol. The Corona engine gives you access to this via the CoronaDelegate.
CoronaLuaIOS.h Contains additional functions to help you bridge between Lua and native Obj-C code. This is meant to complement the set of cross-platform functions in CoronaLua.h.
Corona C Functions This is a collection of C functions offered by the Corona engine which help you interact with Lua for Corona-specific patterns such as dispatching events.

Using Plugins

For instructions on how to obtain and include Corona plugins within a Solar2D Native project, please consult the Using Plugins in Solar2D Native — iOS guide.

Setting Orientation

To restrict Corona to specific orientations when using Solar2D Native on iOS, you can use the orientation table in your project’s build.settings. See Project Build Settings for more details.

Alternatively, you can edit the Info.plist file (App-Info.plist in the template app) and add a CoronaViewSupportedInterfaceOrientations array with options similar to UISupportedInterfaceOrientations. For example, to enable all orientations:

<key>CoronaViewSupportedInterfaceOrientations</key>
<array>
    <string>UIInterfaceOrientationLandscapeLeft</string>
    <string>UIInterfaceOrientationLandscapeRight</string>
    <string>UIInterfaceOrientationPortrait</string>
    <string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>

Sending Events to Runtime

All events in Corona are Lua tables. To make sending events to the global Runtime object, we’ve added a convenience utility function in CoronaLua.h called CoronaLuaRuntimeDispatchEvent().

In the following Lua code, we create a custom event of type "delegate". In order for Corona to recognize the event as a Lua table, the name property of the Lua table must be set to the type of event:

local event = { name="delegate" }
Runtime:dispatchEvent( event )

We then translate this call into native code and send the event in the didLoadMain method of CoronaDelegate:

@implementation MyCoronaDelegate

- (void)didLoadMain:(id<CoronaRuntime>)runtime
{
    lua_State *L = runtime.L;

    // DISPATCH CUSTOM EVENT
    // Create 'delegate' event
    const char kNameKey[] = "name";
    const char kValueKey[] = "delegate";
    lua_newtable( L );
    lua_pushstring( L, kValueKey );     // All events are Lua tables
    lua_setfield( L, -2, kNameKey );    // that have a 'name' property

    Corona::Lua::RuntimeDispatchEvent( L, -1 );
}

@end

Registering Custom Libraries

If you want to add your own library in Lua that wraps native C code, you should follow the standard Lua conventions as described in the Native C section of the Plugins guide.

Custom Lua Error Handling

You can register a custom Lua error handler by calling CoronaLuaSetErrorHandler(). Your handler will override Corona’s internal default error handler and it will be called any time a Lua error occurs at runtime.

static int MyTraceback( lua_State *L )
{
    lua_getfield(L, LUA_GLOBALSINDEX, "debug");
    if (!lua_istable(L, -1)) {
        lua_pop(L, 1);
        return 1;
    }
    lua_getfield(L, -1, "traceback");
    if (!lua_isfunction(L, -1)) {
        lua_pop(L, 2);
        return 1;
    }
    lua_pushvalue(L, 1);  // pass error message
    lua_pushinteger(L, 1);  // skip this function and traceback
    lua_call(L, 2, 1);  // call debug.traceback

    // Log result of calling debug.traceback()
    NSLog( @"[LUA ERROR]: %s", lua_tostring( L, -1 ) );

    return 1;
}

@implementation MyCoronaDelegate
- (void)willLoadMain:(id<CoronaRuntime>)runtime
{
    Corona::Lua::SetErrorHandler( MyTraceback );
}
@end

OpenGL Context Mismatch

Many third-party graphics libraries that use OpenGL will use their own OpenGL context. However, Corona performs its rendering on the main thread and does not expect the OpenGL context to change. Consequently, if you use certain graphics libraries, you may be inadvertently changing the OpenGL context which may cause issues with Corona rendering/updating properly.

To ensure your code is dealing with OpenGL properly, you should ensure that the OpenGL context changes are “balanced” as follows:

// 1. Save Corona context
EAGLContext *context = nil;
context = [EAGLContext currentContext];

// 2. Call third-party library

// 3. Restore Corona context
[EAGLContext setCurrentContext:context];

For example, if the interface for the third-party library is shown via a modal view controller, you would perform steps 1 and 2 in the presentViewController:animated:completion: call and then perform step 3 in the dismissViewControllerAnimated:completion: call.

Building for Devices

To build a Solar2D Native project from Xcode, please follow Apple’s guidelines here.