Skip to content
junzhan edited this page May 20, 2016 · 8 revisions

Here is a quick tour of the Wax framework. By the end you should have a better understanding of how Wax interacts with Objective-C.

  • Wax is a Lua bridge to Objective-C. This means you can write Lua that talks to Objective-C and you can write Objective-C that talks with Lua!

  • Accessing a Cocoa class in Lua (via Wax) looks just like Objective-C

    NSString -- Returns the NSString class
    UIView -- Returns the UIView class

    UITableView -- This is just syntactic sugar for the line below.
    wax.class["UITableView"] -- This is the raw Wax call, you probably didn't need to know this
  • When you create an instance of an Objective-C object DON'T call alloc, wax handles this for you.
    -- No need to call alloc, retain or release! ZOMG!
    local view = UIView.initWithFrame(CGRect(0, 0, 100, 100))
  • To create an Objective-C class from Lua, call the function waxClass{CLASS\_NAME, PARENT_CLASS}. All subsequent functions (within the same file) you add to that Lua file will be automatically added to your new class as instance methods.
    waxClass{"MyClass", NSObject}

    -- Add protocols like this
    waxClass{"MyClass", NSObject, protocols = {"UITableViewDelegate", "UITableViewDataSource"}}

    -- REMEMBER! Protocols need to be added as strings (This is because they are not classes)
  • Calling a Lua function with brackets {} instead of parenthesis () (like in the above call to waxClass) is just syntactic sugar for calling a function with a single table as the argument.
    waxClass{"MyClass", NSObject}
    -- is equivalent to...
    waxClass({"MyClass", NSObject})
    -- ...omitting the parenthesis is much more pretty, so that's how we roll.
  • For waxClass functions, the first argument must be self. This is how Wax mimics Objective-C's object orientated model.
    waxClass{"MyClass", NSObject}

    -- Notice the first argument is self... this will hold the instance of `MyClass` 
    function storeWords(self, words)
      self.words = words
      
      -- Wax creates a `super` variable on the self object. 
      -- Use that to call a method on the object's super class.
      self.super:words(self)
    end
  • Use a colon : instead of a dot . when you call any Objective-C method. This passes the calling object in as the first argument to the method.
    -- A method called with a colon : in Lua ...
    UIApplication:sharedApplication()

    -- ... is equivalent to this
    UIApplication.sharedApplication(UIApplication)

    -- So we just use the colon : to keep this DRY and readable!
  • If an Objective-C method takes multiple arguments, the method call uses underscores _ instead of colons like Objective-C does.

    In Objective-C...

    [UIAlertView initWithTitle:@"title" message:@"message" delegate:nil];

With Wax...

    UIAlertView:initWithTitle_message_delegate("title", "message", nil)
  • Wax DOESN'T use Objective-C properties. Wax forces Lua and Objective-C to only communicate with methods
    someView.frame -- This WON'T work. 

    -- You want to use the getter/setter methods instead
    someView:frame()
    someView:setFrame(someFrame)
  • You can dynamically create member variables for any Objective-C object using the dot . operator. Unlike the colon : operator (used to call methods on an Objective-C class/instance) the dot . operator dynamically creates member variables on the Lua side of the object (The Objective-C side of the object has no knowledge of these variables). The member variables are available throughout the lifetime of the object.
    -- A method called with a colon : in Lua ...
    local view = UIView:init()

    view.someRandomVariable = "YEAH!"
    -- You can assign anything to an instance and it will stick around 
  • Wax will try and coerce Objective-C objects into Lua primatives. It also does the reverse, if an Objective-C methods takes an NSString argument, you can just pass it a Lua string.
    local fileManager = NSFileManager:defaultManager()
    local contents = fileManager:directoryContentsAtPath(".")
    -- directoryContentsAtPath returns an NSArray, but Wax turns this into a Lua
    -- table, which is totally cooler.

    print(contents[1]) --> "info.plist"

    -- NSDictionaries become Lua tables
    -- NSArrays become Lua tables
    -- NSStrings become Lua strings
    -- NSNumbers become Lua numbers
  • Sometimes you don't want an Objective-C object to be coerced, for that use the toobjc method.
    -- if you are trying to call an NSString method, this will fail ...
    local string = "a string"
    string:capitalizedString()
    -- ... because string is coerced into a Lua string

    -- Instead use toobjc, it will force string to be a NSString
    toobjc(string):capitalizedString()
    
    local length = toobjc(str):length();
 	if not toobjc(tempDeviceID):isEqualToString(self:deviceID()) then 
 		--xx
	end
  • Enums, like UITableViewStylePlain, are hardcoded. For now the most commonly used ones are declared in APP_ROOT/wax/stdlib/enums.lua. In the future I hope to get BridgeSupport working for the iPhone so all enums & structs can be created automatically.

  • To pass a selector, just use a string. For example in Objective-C selectors are written as @selector(this:is:a:method). In Wax they are written as this:is:a:method.

  • 32/64bit, you can use wax.isArm64 to judge whether current app is running in 64 bit cpu or not.

if wax.isArm64 == true then
	print("is 64 bit")
else
	print("not 64 bit")
  • Structs are also tricky, the most common ones are decalred in APP_ROOT/wax/wax-scripts/structs.lua(you don't need to create again). It is easy to add structs by following the pattern found in the structs.lua file.
    Pay attention to NSInteger,CGFloat, because they are encoded different in 32/64 bit.
    You can use NSLog(@"type=%s", @encode(NSInteger)) to get the right type for 32/64 bit.
if wax.isArm64 == true then
	wax.struct.create("CGRect", "dddd", "x", "y", "width", "height")
else
    wax.struct.create("CGRect", "ffff", "x", "y", "width", "height")
end
    -- The creates a global function called CGRect that takes 4 float arguments,
    -- the second argument "ffff" is what defines the argument types.

    local rect = CGRect(1, 2, 3, 4)
    print(rect.x) --> 1
    rect.x = 200
    print(rect.x) --> 200
  • Hook method of already exist class is easy. Just write the method, then it will replace the method to Lua function. You use ORIG prefix to call the original method.
waxClass{“MyController"}
function viewDidLoad(self)
--before do something
     self:ORIGviewDidLoad()--self is no need
--after do something
end
  • Hook method of extension, is just like class.
--OC extension
@interface MTPServer (Async4j)
- (void)startAsync4jRequest;
@end
--Lua
waxClass{"MTPServer"}
function startAsync4jRequest(self)
--before do something
     self:ORIGstartAsync4jRequest()--self is no need
--after do something
end
  • Since wax use underline to seperate method, how to hook or call method that contains underline? You can use UNDERxLINE instead of _ (a little ugly, but usefull).
--OC - (void)_prefixA:(NSString *)a _b:(NSString *)b
function UNDERxLINEprefixA_UNDERxLINEb(self, a, b)
	self:ORIGUNDERxLINEprefixA_UNDERxLINEb(TEST_VALUE_STRING, TEST_VALUE_STRING)
end

--OC - (id)__aa_bb_:(NSString *)v1 _cc__dd_ :(NSString *)v2 ___ee___f___:(NSString *)v3
function UNDERxLINEUNDERxLINEaaUNDERxLINEbbUNDERxLINE_UNDERxLINEccUNDERxLINEUNDERxLINEddUNDERxLINE_UNDERxLINEUNDERxLINEUNDERxLINEeeUNDERxLINEUNDERxLINEUNDERxLINEfUNDERxLINEUNDERxLINEUNDERxLINE(self, v1, v2, v3) 
	return self:ORIGUNDERxLINEUNDERxLINEaaUNDERxLINEbbUNDERxLINE_UNDERxLINEccUNDERxLINEUNDERxLINEddUNDERxLINE_UNDERxLINEUNDERxLINEUNDERxLINEeeUNDERxLINEUNDERxLINEUNDERxLINEfUNDERxLINEUNDERxLINEUNDERxLINE("abc", "efg", "hij")
end
  • Since $ symbol is invalid in lua function name, you can use DOLLARxMARK instead of $ .
--OC - (NSString *)$testDolorMethod;
--lua
function DOLLARxMARKtestDolorMethod( self )
	print("lua $testDolorMethod")
	self:ORIGDOLLARxMARKtestDolorMethod()
	return "abc"
end
  • Add new properity for class
function myProperity(self)
	return self._myProperity
end
function setMyProperity(self, x)
	self._myProperity = x
end

some tips

  • hook dealloc method
waxClass{"TestDebugVC"}

function dealloc(self)
    print("lua dealloc ")
---do something
    self:ORIGdealloc() --if you don't want call orginal, then you must call self.super:dealloc()
--add this code for dealloc
    waxGCInstance(self)
end
  • hook method called in dealloc
--OC code
- (void)clear{
    
}
- (void)dealloc{
    [self clear];
}
-- Lua code
waxClass{"TestDebugVC"}
function clear(self)
    print("lua clear ")
---do something
    self:ORIGclear() 
-- do something
end

-- hook this also
function dealloc(self)
    self:ORIGdealloc()
    waxGCInstance(self)
end

Attention: self:ORIGdealloc() or self.super:dealloc() must be called, or your instance will leak.

  • call variable method variable method can't be called dynamically. UIAlertView can use like this:
local alert = UIAlertView:init();
alert:setTitle(title);
alert:setMessage("aaaa");
alert:setTitle("bbbb");
alert:setDelegate(self);
alert:addButtonWithTitle("cancel");
alert:addButtonWithTitle("ok");
alert:setTag(20147701);
alert:show();