Search code examples
iosobjective-cluacocoapodsluasocket

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!


Solution

  • 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.