Forum Archive

Help with invoking a block

mikael

I am trying to implement a Pythonista API for WKWebView. All credit for all ObjC code so far goes to @mithrendal.

For the load policy decision, I would need to invoke the decision handler with a single integer parameter. Repurposing @JonB code from the multipeer project I arrived at the following, but get an exception stating that the handler was never called.

Any ideas? Thanks.

class _block_descriptor (Structure):
  _fields_ = [('reserved', c_ulong), ('size', c_ulong), ('copy_helper', c_void_p), ('dispose_helper', c_void_p), ('signature', c_char_p)]
InvokeFuncType = ctypes.CFUNCTYPE(None, *[c_void_p, ctypes.c_int])
class _block_literal(Structure):
  _fields_ = [('isa', c_void_p), ('flags', c_int), ('reserved', c_int), ('invoke', InvokeFuncType), ('descriptor', _block_descriptor)]

def webView_decidePolicyForNavigationAction_decisionHandler_(_self, _cmd, _webview, _navigation_action, _decision_handler):
  decision_handler = ObjCInstance(_decision_handler)
  retain_global(decision_handler)
  blk = _block_literal.from_address(_decision_handler)
  blk.invoke(decision_handler, 1)
dgelessus

~~IIRC block invoke functions take the block itself as an implicit first argument (basically like self). So you probably need to add an extra c_void_p in your InvokeFuncType's argument list, and add blk as the first argument in your blk.invoke call.~~ Nevermind, decision_handler and blk both refer to the block.

JonB

Old versions of objc_util required you to redefine the block constructor.

From something I did recently, this was a block that took an argument (NSDictionary)

def info_cb(_blk,info):
    now_playing_dict.clear()
    if info:
        infodict=ObjCInstance(info)
        for k in infodict.allKeys():
            now_playing_dict[str(k)]= infodict[k]
    e.set()

cb=ObjCBlock(info_cb,argtypes=[c_void_p,c_void_p], restype=None)

As @dgelessus notes, you need to provide the "hidden" block argument, and then speciffy argtypes (which must include the hidden arg c_void_p) and restype.

JonB

are you sure your callback is getting called? maybe an logging line to check. objc_util has trouble parsing type encodings for things like blocks, or complicated structures, so the protocol type encoding is probably getting munged, in which case, your method might not be getting created correctly.

Your block looks mostly okay -- you have the secret blk parameter, and the integer, though you might try c_void_p instead of None for restype.. i feel like i had to do that recently and couldnt explain why. You might also try c_long instead of c_int, as WKNavigationActionPolicy is defines as NSInteger, which an alias for c_long. if you run on 64 bit, there is a difference, though on 32 bit they are the same.

mikael

Looks like the callback is indeed not getting called, and that is likely because I missed the bit below. I am having a hard time finding the documentation about how these should be changed — I tried *3, and then also removing one @, but no luck.

f = webView_decidePolicyForNavigationAction_decisionHandler_
f.argtypes = [c_void_p]*4
f.restype = None
f.encoding = b'v@:@@@@?'
JonB

the runction prototype (just searvh for method name in google, and choose the apple docs) is:

- (void)webView:(WKWebView *)webView 
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction 
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

so, we have return type void, argtypes are 3 pointers, the last of which is a block. However, you also have the hidden self and selector args.

So encoding would be b'v@:@@@?'
argtypes would then be [c_void_p]*6 (though maybe 4.. i forget if the hidden args get added during the call.
Im not 100% sure about the last @?, you might try plain @.

mikael

@JonB, thanks. Turns out my earlier attempt actually worked, but I did not notice it because of another error in the code.

So, to conclude, these were the right values, and the policy decision is working as expected:

f = webView_decidePolicyForNavigationAction_decisionHandler_
f.argtypes = [c_void_p]*3
f.restype = None
f.encoding = b'v@:@@@?'

I also got to learn about the crazy fun argtype encoding values Apple uses.