A common method of simplification is to divide a problem into subproblems of the same type. – Wikipedia: recursive
In this third blog post (see also blog post 1, blog post 2) we will register a Swift function within Lua so that it can be called from Lua. The Swift function can basically call anything. This allows Lua to execute certain things or access values that the embedded Lua can’t, like the gyroscope of the smartphone. But we will just call the Lua factorial function again.
There are a some more steps necessary, because we add an additional wrapper:
- registerFunction
- FactorialWrapper
- Factorial Swift Class
- Lua Code
You can find the Source Code on Github.
registerFunction
We define the registerFunction method within the LuaWrapper files. The method calls the lua_register
method and passes the function which should be called and the name under which the function is registered.
This allows Objective-C functions to be used within Lua and Lua to access information that is otherwise only available 🔒 within a native iOS. Swift is accessible from the Objective-C function.
LuaWrapper.h
Add the following code to the LuaWrapper.h file before @end
:
- (void) registerFunction: (lua_CFunction)function
withName: (const char *)name;
The first parameter is the C function, the second is the name of the function that Lua registers the function with.
LuaWrapper.m
Add the implementation of the registerFunction
to the LuaWrapper.m file before the @end
:
- (void) registerFunction: (lua_CFunction)function
withName: (const char *)name {
lua_register(luaState, name, function);
}
The registerFunction
only calls the lua_register
function with the luaState
and passes the parameters.
FactorialWrapper
The factorialExternal
function within the wrapper file has two tasks:
- It creates a (singleton) instance of the Swift Factorial class (we will implement the class below).
- It calls the callFactorial method of the Factorial class.
FactorialWrapper.h
Create the FactorialWrapper.h file with following steps:
- File / New / File …
- Header File
- Dialog
- FactorialWrapper.h
- select Targets checkbox
- Create
Add the following code after #define
:
#include "lua.h"
int factorialExternal(lua_State *luaState);
FactorialWrapper.m
For the implementation of the factorialExternal
function create the FactorialWrapper.m file with following steps:
- File / New / File …
- Objective-C
- Dialog
- File: FactorialWrapper.m
- File Type: Empty File
Add the following code to the FactorialWrapper.m file and customize the second import. Xcode generates an .h file that gives Objective-C access to Swift classes and functions. Therefore, you must adjust the import according to the project name: project-name-Swift.h
#import "FactorialWrapper.h"
#import "LuaSwiftRecursiveSwiftLua-Swift.h" // << ADJUST ME
Factorial * factorial;
int factorialExternal(lua_State *luaState) {
if (factorial == Nil) {
factorial = [[Factorial alloc] initWithScript: @""];
}
UInt64 n = lua_tointeger(luaState, -1L);
lua_pop(luaState, 1);
lua_Number res = [factorial
callFactorialWithState:luaState
value:n];
lua_pushnumber(luaState, res);
return 1;
}
The Factorial alloc
calls the function initWithScript
. This function is the init(script: String)
function in the Swift Factorial
class. For the Objective-C and Swift bridging the Swift function names are translated into ‘function-name With first-parameter’.
The same pattern is used for the callFactorialWithState
function name which calls the Swift function callFactorial(state: ...
.
The factorialExternal
function
- creates a
Factorial
instance (as singleton) - reads the value from the stack
- calls the Swift
callFactorial
function - push the result on the stack
Bridging.h
The FactorialWrapper.h file must be added to the project-name-Bridging.h file. This allows Swift to access the function ‘factorialExternal’ to register the function within Lua:
#import "FactorialWrapper.h"
Factorial Class
I moved the initialization and also the call of the Lua function to a separate class. So this functionality is encapsulated and can be used from Objective-C.
Factorial.swift
The Factorial class has an init
and a callFactorial
method. The init
method gets the Lua script during initialization. This script is loaded when Lua is initialized. Additionally, the init
method registers the factorialExternal
method in Lua. The factorialExternal
method is implemented in the FactorialWrapper files.
The callFactorial
method calls the Lua function factorial
. The ViewController
and the factorialExternal
method call the callFactorial
method.
For the Objective-C Swift bridging the Swift functions init
and callFactorial
are named initWithScript
and callFactorialWithState
within Objective-C.
@objc(Factorial)
class Factorial : NSObject {
var lua : Lua
let ptrFname = strdup("factorial")
@objc
init(script: String) { // Objective-C: initWithScript
lua = Lua()
lua.setup()
let ptrScript = strdup(script)
lua.script(ptrScript)
free(ptrScript)
let funcName = strdup("factorialExternal")
lua.register(factorialExternal, withName: funcName)
free(funcName)
}
@objc
func callFactorial(state: OpaquePointer? = nil,
value: lua_Number) -> lua_Number {
// Objective-C: callFactorialWithState
return lua.call(state,
methode: ptrFname,
value: value)
}
deinit {
free(ptrFname)
lua.destruct()
}
}
ViewController.swift
This changes the ViewController.swift file. Replace the existing code for the Lua call with this one:
let filename = Bundle.main.path(forResource: "script",
ofType: "lua")!
do {
let luaScript = try String(contentsOfFile: filename)
let fac = Factorial(script: luaScript)
let result = fac.callFactorial(value: lua_Number(100))
print(result)
} catch let error {
print("can not read file", filename, error)
}
As before, the script.lua file is read in. The file content is passed to the factorial instance when it is created. Afterwards the function callFactorial
is called and the result is printed. Therefore this process has not changed.
script.lua
Last but not least you have to change the script.lua file so that the call can be done via Objective-C and Swift:
-- factorial
function factorial(n)
if (n == 0) then
return 1
else
return n * factorialExternal(n - 1)
end
end
There is no change in the overall result. The 100! gives the same result as in the previous blog post. But the execution is done by an Objective-C function, which again calls a Swift function. Up to this point it is a normal scenario to access external functionalities from the embedded Lua code.
The direct call of the Lua function factorial is experimental. However, it might be possible to call a Swift function from within Lua, which then executes other Lua functions depending on a state.
Summary
In these three blog posts you have seen how to embed Lua within Swift and how to call a Lua function. You also saw how to register a Swift function with Objective-C in Lua.