Swift blocks not working
Asked Answered
T

2

7

I've been trying to figure out how to use JavaScriptCore in swift. I'm running into problems however when I have to deal with blocks as arguments, seems like the block is run immediately and the arguments gets the return value of the block. What am I doing wrong?

Working Objective C code:

JSContext* context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];
context[@"test"] = ^(NSString *string) {
    //code
};

What I've tried:

1:

var ctx = JSContext(virtualMachine:JSVirtualMachine())
var ctx["test"] = {(string:NSString)->() in /*code*/ }

//Gives me "'JSContext' does not have a member named 'subscript'"

2:

var ctx = JSContext(virtualMachine:JSVirtualMachine())
let n: (string: String)->() = {string in /*code*/}

ctx.setObject(n, forKeyedSubscript:"test")

//Gives me "Type '(x: String) -> () does not conform to protocol 'AnyObject'"

3:

var ctx = JSContext(virtualMachine:JSVirtualMachine())
let n: (string: String)->() = {string in /*code*/}

ctx.setObject(n as AnyObject, forKeyedSubscript:"test")

//Gives me "Cannot downcast from '(string: String) -> () to non-@objc protocol type 'AnyObject'"

Am I missing something here, or is this just a bug in Swift?

Edit:

I've now also tried suggestions from Cast closures/blocks

class Block<T> {
    let f : T
    init (_ f: T) { self.f = f }
}

and then

ctx.setObject(Block<()->Void> {
        /*code*/
    }, forKeyedSubscript: "test")

This solution lets me compile but I get a runtime error:

Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
Tracay answered 6/7, 2014 at 12:12 Comment(2)
Seems to be the same problem as this one: https://mcmap.net/q/375938/-cast-closures-blocks.Overstuff
It's similar but different. That question is about dealing with blocks that you receive as return values from Objc methods. This one is about passing Swift closures as blocks to Objc methods.Bible
D
29

It has something to do with how Swift implement closure. You need to use @convention(block) to annotate that the closure is ObjC block. Use unsafeBitCast to force cast it

var block : @convention(block) (NSString!) -> Void = {
   (string : NSString!) -> Void in 
    println("test")
}

ctx.setObject(unsafeBitCast(block, AnyObject.self), forKeyedSubscript: "test")

from REPL

swift
Welcome to Swift!  Type :help for assistance.
  1> import Foundation
  2> var block : @convention(block)(NSString!) -> Void = {(string : NSString!) -> Void in println("test")}
block: @convention(block)(NSString!) -> Void =
  3> var obj: AnyObject = reinterpretCast(block) as AnyObject
obj: __NSMallocBlock__ = {} // familiar block type
Demolition answered 29/7, 2014 at 11:5 Comment(5)
That worked like a charm! Thank you so much. Only thing, apparently ctx["test"] = doesn't work. ctx.setObject(obj, forKeyedSubscript:"test") does however. Once again, Thank you, and here is your bounty in 22 hours when I'm able to.Tracay
On Beta6 reinterpretCast(block) as AnyObject has been deprecated in favor of unsafeBitCast(block, AnyObject)Telmatelo
Actually, that would be unsafeBitCast(block, AnyObject.self)Bible
This line var ctx["test"] = reinterpretCast(block) as AnyObject should be ctx.setObject(unsafeBitCast(block, AnyObject.self), forKeyedSubscript: "test"), tested on Xcode Version 6.1.1 (6A2008a).Satiable
With the latest Swift 2 syntax, the block should be defined as @convention(block) (String) -> VoidVoiceful
G
1

I have a working demo at:

And here is the part that implements block registration:

typealias ID = AnyObject!
extension JSContext {
    func fetch(key:NSString)->JSValue {
        return getJSVinJSC(self, key)
    }
    func store(key:NSString, _ val:ID) {
        setJSVinJSC(self, key, val)
    }
    func store(key:NSString, _ blk:()->ID) {
        setB0JSVinJSC(self, key, blk)
    }
    func store(key:NSString, _ blk:(ID)->ID) {
        setB1JSVinJSC(self, key, blk)
    }
    func store(key:NSString, _ blk:(ID,ID)->ID) {
        setB2JSVinJSC(self, key, blk)
    }
}

You need a very small objc code and bridging header to make it work. See the repository for details.

Glasgo answered 29/7, 2014 at 22:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.