Plugins

Plugins allow you to extend the functionality of Corona. This guide outlines the fundamental details.

Architecture

Corona plugins leverage Lua's module system in which plugins are lazily loaded by a corresponding call to the Lua require() function.

Typically, these plugins are just shared native libraries which are supported on platforms like macOS (.dylib), Windows (.dll), and Android (.so).

Note
  • On iOS, shared libraries are not supported, so plugins must be static libraries (.a).
  • On Android, it's often convenient to create pure Java plugins (.jar).

In these two cases, Corona adds special loaders which ensure that these plugins can be easily loaded, assuming certain conventions are followed.

Plugin Types

Library Plugins

Library plugins are simply Lua modules which can be used to wrap native functionality. These libraries are namespaced with the prefix plugin.. The prefix helps avoid namespace collisions with Corona libraries.

These libraries should be loaded with the Lua require() function. For example, if there were a new library plugin named myLibraryPlugin, you could access it in Lua like this:

local myLibraryPlugin = require( "plugin.myLibraryPlugin" )

Provider Plugins

Provider plugins are also Lua modules, but you do not load them directly via require(). Instead, the wrapper library (or function) allows you to specify the provider and then it loads the plugin for you.

In order for the provider to be loaded, it needs to follow a specific namespace convention. Provider plugins are prefixed with CoronaProvider. followed by the name of the wrapper and the provider plugin name.

!

Most plugins you create will be library plugins, not provider plugins. The main difference is that library plugins are just standard Lua libraries, whereas provider plugins are a Corona convention. In particular, provider plugins are designed to be used in conjunction with specific Corona wrapper interfaces such as native.showPopup().

Using Plugins

Corona SDK

Plugins are hosted on our servers. You can incorporate a plugin by adding entries to the plugins table of build.settings. When added, the build server will integrate the plugin during the build phase.

For details on adding a specific plugin, please locate the appropriate plugin documentation.

Corona Enterprise

Native plugins can easily be added to your iOS or Android project.

On iOS, plugins will be in the form of static library (.a) files that need to be linked into your app executable.

On Android, plugins can come in the form of .so (shared library) or .jar files.

!

For projects based on Android Studio, .jar files should be in your project's libs directory and .so files should be in the project's src/main/jniLibs directory. For Ant projects, these files should be in your project's libs directory to ensure that the ant script sees them.

Note

When you add a plugin via Corona Enterprise, be aware of the following:

  • Resources — If the plugin comes with resources, you should ensure those resources are available.
  • Dependencies — If the plugin depends on linking to system frameworks or libraries, ensure the appropriate linker flags are set.

Plugin Projects

If you want to create/submit a plugin for availability in the Corona Marketplace, you can use our App project templates to simplify development of your plugin, as well as test it. After all, a plugin itself is not an executable and it must be run inside an actual application. The project templates can be located within the Corona Enterprise download package (for example CoronaEnterprise.2016.2949.tgz):

CoronaEnterprise/ProjectTemplates/App/

This folder has separate platform-specific subfolders for each target platform:

There's also a Corona folder that sits alongside these folders which contains a classic Corona project (main.lua). You can modify these files to test the Lua APIs that are offered by your plugin.

iOS

In the ios folder, there are two Xcode projects:

  1. App.xcodeproj — This is the project that builds the .app executable which runs on the device. It automatically builds the plugin as an implicit dependency.

  2. Plugin.xcodeproj — For plugin development, you should modify this project. It builds the plugin as a static library (.a) and you can use this project directly if you want to share your plugin outside of the App project.

Note

Most often, you will want to create a universal binary for your plugin. In other words, you'd like to ship one static library that supports both the device and the Xcode iOS Simulator.

You can accomplish this via the lipo tool. In the following example, we are creating a universal library for staticlibrary.a:

lipo -create "/path/to/iphoneos/staticlibrary.a" "/path/to/iphoneos-simulator/staticlibrary.a" -output "/path/to/dst/staticlibrary.a"

Android

In the android folder, the contents vary depending on what daily build you're using.

For projects based on Android Studio starting with daily build 2016.2886, there are two modules:

  1. app — This module builds the .apk that installs on the device. The app module automatically builds the plugin as an explicit dependency per this entry in the dependencies block of the build.gradle script for the app module (compile project(':plugin')).

  2. plugin — This module builds the plugin as a .jar file, only including the Java files under plugin/src/main/java in your project. You can use this project directly if you want to share your plugin outside of the App project.

For Ant-based projects below daily build 2016.2886, there are two shell scripts:

  1. build.sh — This builds the .apk that installs on the device.

  2. build.plugin.sh — This builds the plugin as a .jar file, only including the Java files under src/plugin in your project. You can use this project directly if you want to share your plugin outside of the App project.

Coding Plugins

Plugins build upon Lua's module system, so they can come in threee different flavors:

  1. Pure Lua code
  2. Native code
  3. Hybrid of Lua and native code
Submitting Plugins

Interested in creating a plugin and submitting it for availability in the Corona Marketplace? Please see our Plugin Submission Guide.

Pure Lua

A Lua library plugin can be created using the CoronaLibrary class. Here's how a library called plugin.myLibrary might look:

-- Create library
local Library = require( "CoronaLibrary" )
local lib = Library:new{ name="plugin.myLibrary", publisherId="com.mycompany" }

-- Create a function for the library
lib.doSomething = function()
    print( "I did something!" )
end

-- Return "lib"
return lib

Native C

In addition to the plugin naming conventions discussed above, Corona expects plugins to follow some additional conventions that ensure Lua can locate these modules. In C, these conventions are just the standard Lua naming conventions for modules:

  • The name of the module function must be prefixed by luaopen_.
  • The signature of this function should match lua_CFunction.
  • If a module contains a dot (.), it is replaced by an underscore (_) in the name of the function, since C does not allow dots in symbol names.
  • This function should be publicly visible.

For example, the Lua library plugin.myLibrary would have a Lua function called luaopen_plugin_myLibrary:

static int doSomething( lua_State *L )
{
    lua_getglobal( L, "print" );
    lua_pushstring( "I did something!" );
    CoronaLuaDoCall( L, 1, 0 );
    return 0;
}

// Export so it's visible to "require()"
CORONA_EXPORT
int luaopen_plugin_myLibrary( lua_State *L )
{
    static const luaL_Reg kFunctions[] =
    {
        { "doSomething", doSomething },

        { NULL, NULL }
    };

    // Create "myLibrary"
    // Lua version assumes version and revision default to 1
    int result = CoronaLibraryNew(
        L, "myLibrary", "com.mycompany", 1, 1, kFunctions, NULL );

    return result;
}

Native Java

If you write the module in Java, Corona has set up Lua to load Java code and to let that Java code define the Lua library via JNLua. Here, Lua looks for the Java class LuaLoader and instantiates it.

Corona assumes the following conventions:

  • The LuaLoader class has a default (empty) constructor.
  • The LuaLoader class must implement the JNLua interface com.naef.jnlua.JavaFunction.
  • The namespace of LuaLoader should be the same as the name passed to require().

For example, the Lua library plugin.myLibrary would be implemented by the (fully-qualified) class plugin.myLibrary.LuaLoader and that class would implement the invoke() method of the com.naef.jnlua.JavaFunction interface.

Lua + Native

You can also create plugins in a hybrid fashion. For example, you can create the library via Lua code and then do further initialization in C.

Below is an example for creating the myLibrary library from Lua and then adding additional functions on the native C side. Here, we assume kBuffer stores the Lua bytecodes corresponding to myLibrary.lua.

// The bytecodes for "myLibrary.lua" are contained in kBuffer
static const unsigned char kBuffer[] = { ... }

static int bufferLoader( lua_State *L )
{
    return luaL_loadbuffer( L, (const char*)kBuffer, sizeof( kBuffer ), "myLibrary" );
}

static int somethingElse( lua_State *L )
{
    printf( "I'm doing something else!" );
    return 0;
}

CORONA_EXPORT
int luaopen_myLibrary( lua_State *L )
{
    lua_CFunction factory = Corona::Lua::Open< bufferLoader >;
    int result = CoronaLibraryNewWithFactory( L, factory, NULL, NULL );

    if ( result )
    {
        const luaL_Reg kFunctions[] =
        {
            { "somethingElse", somethingElse },

            { NULL, NULL }
        };

        luaL_register( L, NULL, kFunctions );
    }

    return result;
}