Could either of you tell me how Lua callbacks are called from an ObjC binding?
In this example, I’m executing some javascript but the print in the js bound function aren’t showing up until after the exec returns.
-- JavaScript
function setup()
local js = javascript.context()
js:set("get10", function()
print("I'm incorrectly printed after the results!")
return 10
end)
-- Both these 2 cases should print 10 as the result
local _, result = js:eval([[
var x = 10
x
]])
print("Result 1: ", result)
-- But this case doesn't.
-- Nor does the print() appear until js:eval returns...
local _, result = js:eval([[
var x = get10()
x
]])
print("Result 2: ", result)
end
One thing I noticed as I’m working on adding support for delegates is that our Lua callbacks are called asynchronously, which might explain why you are only seeing both results at the end.
I’m currently testing doing the callbacks synchronously as I need this to return a value (e.g. UITextViewDelegate’s textViewShouldBeginEditing).
Once I get that working, I’ll see if your code works as expected. I just hope that the synchronous calls won’t end up causing deadlocks
@RonJeffries The ObjC bindings already allow passing Lua defined callbacks into ObjC functions. I expect it turns them into a native representation of sorts.
This just utilises that to set a global value in the JavaScript context which we can call in JS. It already works, just not when I’d expect it to.
The “set” method actually defines a JavaScript method which results in a call to the specified Lua function, so print is the right thing to use. There is no “conversion into a native representation”. On the native side, we pass the native method an “Objective-C block” which, when called, will simply call the Lua function.
I’m wildly guessing here but does the Lua function perhaps need to create and return a Core Foundation number rather than a Lua number? That might explain the nil.
The nil is because the current version runs callbacks asynchronously. So when the second eval is called, the following print statement actually evaluates before the callback could even return a value.
Executing callbacks synchronously did introduce deadlocks so I had to change a few things. I now have the above sample working fine locally but I want to do a bit more testing to make sure I’m not introducing other instabilities
Hey everyone, just wanted to give a quick update on this.
Implementing callback support and making sure things are evaluated in the right order did open a huge can of worms with deadlocks. I’ve been thinking about how to solve this properly and I believe I have a good solution in mind, but it will still take some time to put in place properly.
I’ll keep you posted as soon as I make progress on it!
Callbacks are now available in the latest beta, and this includes the fix for the JavaScript timing issue.
Here’s the updated JavaScript sample:
function setup()
local js = javascript.context()
js:set("get10", function()
print("I'm correctly printed between the results!")
return 10
end)
local _, result = js:eval([[
var x = 10
x
]])
print("Result 1: ", result)
local _, result = js:eval([[
var x = get10()
x
]])
print("Result 2: ", result)
end
function draw()
background(0, 0, 0)
end
And here’s a basic example of the new objc.callback with a text view:
Delegate = objc.delegate("UITextViewDelegate")
function Delegate:init(test)
self.test = test
end
function Delegate:textViewShouldBeginEditing_(objTextView)
return AllowBeginEditing
end
function Delegate:textViewDidChange_(objTextView)
print(self.test .. ": " .. objTextView.text)
end
function endEditing()
uiTextView:endEditing_(true)
end
function setup()
parameter.boolean("AllowBeginEditing", false, endEditing)
parameter.action("EndEditing", endEditing)
local vc = objc.viewer
uiTextView = objc.cls.UITextView()
vc.view:addSubview_(uiTextView)
uiTextView.translatesAutoresizingMaskIntoConstraints = false
uiTextView.trailingAnchor:constraintEqualToAnchor_constant_(vc.view.trailingAnchor, -20).active = true
uiTextView.topAnchor:constraintEqualToAnchor_constant_(vc.view.topAnchor, 20).active = true
uiTextView.widthAnchor:constraintEqualToConstant_(400).active = true
uiTextView.heightAnchor:constraintEqualToConstant_(200).active = true
uiTextView.text = ""
uiTextView.layer.cornerRadius = 8
uiTextView.delegate = Delegate("uiTextView")
end
function draw()
background(40, 40, 50)
end
I fixed as many deadlocks as I could find, but there might still be some left for use cases and timings yet to be encountered. If Codea ever becomes unresponsive for you (a common use case for me was when leaving play), please let me know with any details which might help reproduce the problem.
Can we load a JS library that doesn’t rely too much on JS accessors (like “console”) ? I’m thinking of a math library that can load in SVGs or calculate SVG path data.
@jfperusse Just want to check I’m using this correctly?
This is currently causing Codea to crash entirely…
function MessageHandler(func)
local Handler = objc.delegate("WKScriptMessageHandler")
function Handler:userContentController_didReceiveScriptMessage_(objUserContentController, objMessage)
print("log!")
--func(objMessage.body)
end
return Handler()
end
local logHandler = MessageHandler(function(msg)
print(msg)
end)
function setup()
local controller = objc.cls.WKUserContentController()
controller:addScriptMessageHandler_name_(logHandler, "log")
local config = objc.cls.WKWebViewConfiguration()
config.userContentController = controller
-- New view
local wk = objc.cls.WKWebView:alloc()
wk:initWithFrame_configuration_(objc.viewer.view.bounds, config)
-- Load page
wk:loadHTMLString_baseURL_([[
Hello World!
<script>
window.webkit.messageHandlers.log.postMessage("Hello World!");
</script>
]], objc.cls.NSURL:URLWithString_(""))
-- Display the web view above the GL view
objc.viewer.view:insertSubview_atIndex_(wk, 1)
objc.viewer.view.subviews[1]:removeFromSuperview()
end
If I comment out window.webkit.messageHandlers.log.postMessage("Hello World!"); then it at least runs so it’s definitely related to the delegate.
To be clear, I’d expect to see the message ‘log!’ appear in Codea’s log panel.
Hi @Steppers! Seems like I missed this way of setting protocols. The only supported way at the moment is through a “.delegate” member. I will add support for delegate as function arguments as soon as possible.