Suge
Suge

Reputation: 2897

How can I use LuaSocket in iOS app?

I'm developing an iOS app can run Lua scripts, I can easily integrate the base lua support with CocoaPods, but how can I add LuaSocket library into it? LuaSocket contains some C and some Lua files, does anyone have ideas?Thank you!

Upvotes: 1

Views: 1090

Answers (2)

Matthew Burke
Matthew Burke

Reputation: 2364

With iOS 8 allowing dynamic frameworks (libraries) there may be a more elegant approach, but the following works with Lua 5.2.3 (since you are using Cocoapods, and 5.2.3 is the version the Cocoapod supplies) and LuaSocket 3.0-rc1.

NOTE that I am actually not using the Cocoapod; including Lua in your iOS project is simple enough that I find it not worth the trouble of using Cocoapods. YMMV. You may need to make a few adjustments to what I describe below due to path differences.

  1. Create a new iOS 'Single View' project
  2. Create a group named Lua in XCode's project navigator
  3. Copy all files (except lua.c, luac.c, lua.hpp and makefile) from the src directory in the Lua download into this group
  4. Create a group named LuaSocket in XCode's project navigator
  5. Copy all files (except makefile, wsocket.c, wsocket.h) from the src directory in the LuaSockets download into this group
  6. Add the line #import "luasocket.h" to the file serial.h in the LuaSocket source

At this point you should be able to build and run the app without any errors. Of course, it doesn't really do anything yet...

First, we're going to modify luaL_openlibs so that it initializes LuaSocket's C code as follows.

In the Lua source, find the file linit.c and change

static const luaL_Reg loadedlibs[] = {
  {"_G", luaopen_base},
  {LUA_LOADLIBNAME, luaopen_package},
  {LUA_COLIBNAME, luaopen_coroutine},
  {LUA_TABLIBNAME, luaopen_table},
  {LUA_IOLIBNAME, luaopen_io},
  {LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_BITLIBNAME, luaopen_bit32},
  {LUA_MATHLIBNAME, luaopen_math},
  {LUA_DBLIBNAME, luaopen_debug},
  {NULL, NULL}
};

to

  {"_G", luaopen_base},
  {LUA_LOADLIBNAME, luaopen_package},
  {LUA_COLIBNAME, luaopen_coroutine},
  {LUA_TABLIBNAME, luaopen_table},
  {LUA_IOLIBNAME, luaopen_io},
  {LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_BITLIBNAME, luaopen_bit32},
  {LUA_MATHLIBNAME, luaopen_math},
  {LUA_DBLIBNAME, luaopen_debug},
  {"socket", luaopen_socket_core},
  {"mime", luaopen_mime_core},
  {NULL, NULL}
};

You'll need to add #include "luasocket.h" and #include "mime.h" at the top of linit.c.

There are a couple of other C functions that you'll want to add to this list, such as luaopen_socket_unix, but I'll leave including them as an exercise for the reader.

Now we'll turn to the various Lua source files that are included in LuaSocket such as socket.lua and mime.lua. Rather than using require to load these in, we're going to execute them with luaL_dofile.

In order to be concrete, suppose we want to use LuaSocket to do some initialization for our view controller. We'll create the Lua state in viewDidLoad, call luaL_openlibs, to initialize the core libraries and LuaSocket's C libraries, then we will get a filepath to the Lua files we want to run using routines from NSBundle.

We need to edit the Lua files to remove any lines that require socket.core, mime.core, etc. because that's simpler than trying to get require to behave correctly. Moreover, socket.core and mime.core have already been initialized by our modified luaL_openlibs, so there is no need to require them.

So viewDidLoad will look something like this:

- (void)viewDidLoad
{
  [super viewDidLoad];

  lua_State *L = luaL_newstate();
  luaL_openlibs(L);

  // Load socket.lua and mime.lua

  NSString *fp = [[NSBundle mainBundle] pathForResource:@"socket" ofType:@"lua"];
  luaL_dofile(L, [fp cStringUsingEncoding:NSUTF8StringEncoding]);

  fp = [[NSBundle mainBundle] pathForResource:@"mime" ofType:@"lua"];
  luaL_dofile(L, [fp cStringUsingEncoding:NSUTF8StringEncoding]);

  lua_settop(L, 0); // ignore return values from the calls to dofile


  // Now do something with the Lua state and LuaSockets

  NSString *script = @"res = mime.b64('LuaSocket', 'works')";
  luaL_dostring(L, [script cStringUsingEncoding:NSUTF8StringEncoding]);
  lua_getglobal(L, "res");
  const char *s  = luaL_checkstring(L, 1);
  NSLog(@"res = %@", [NSString stringWithCString:s encoding:NSUTF8StringEncoding]);
}

There are still a few loose ends, but this should demonstrate the main points. You can look at an example project I've created on Github. Over the next few days, I'll get it cleaned up and demonstrate more of LuaSocket's functionality.

Upvotes: 4

ares777
ares777

Reputation: 3628

I used 2.0.2 version of LuaSocket and 5.1 for Lua. After modify some files [ Unknown type name 'luaL_reg'; did you mean 'luaL_Reg'? ] I was able to compile. Also removed wsocket (.h & .c) files. So it is compiling. After some search, I found also Cocos2d-x source code (3.0.4) use luasocket folder (and has liblua.a). Removed again wsocket files then compile without errors. I used

    -(void)addBundlePathToLuaState:(lua_State*)L
   {
    lua_getglobal(L, "package");
   lua_getfield(L, -1, "path"); // get field "path" from table at top of stack (-1)

   const char* current_path_const = lua_tostring(L, -1); // grab path string from top of stack
   NSString* current_path = [NSString stringWithFormat:@"%s;%@/?.lua", current_path_const, [[NSBundle mainBundle]resourcePath]];

   lua_pop(L, 1); // get rid of the string on the stack we just pushed on line 5
   lua_pushstring(L, [current_path UTF8String]); // push the new one
   NSLog(@"path current %s", [current_path UTF8String]);
   lua_setfield(L, -2, "path"); // set the field "path" in table at -2 with value at top of stack
   lua_pop(L, 1); // get rid of package table from top of stack
     }

and

   int status;
   lua_State *La;
   La = luaL_newstate();
   NSBundle* myBundle = [NSBundle mainBundle];
   NSString* myImage = [myBundle pathForResource:@"a" ofType:@"lua"];
   const char *stringAsChar = [myImage cStringUsingEncoding:[NSString defaultCStringEncoding]];
   NSLog(@"mypath %s", stringAsChar);
   luaL_openlibs(La); /* Load Lua libraries */
/* Load the file containing the script we are going to run */
   status = luaL_loadfile(La, stringAsChar);
   NSString *luaFilePath = [[NSBundle mainBundle] pathForResource:@"a" ofType:@"lua"];
   [self addBundlePathToLuaState:La];

My script use local socket = require("socket") but the problem is it can not find core socket.lua:13: module 'socket.core' not found: no field package.preload['socket.core']

So I don't think you can get success soon :)

Upvotes: 2

Related Questions