Background/Context
JavaScriptCore allows developers to use a JavaScript library inside their Swift/ObjC application. One such method is to directly call a JavaScript function, as demonstrated in the Swift code sample below:
let firstname = "Mickey"
let lastname = "Mouse"
if let functionFullname = self.jsContext.objectForKeyedSubscript("getFullname") {
// Call the function that composes the fullname.
if let fullname = functionFullname.call(withArguments: [firstname, lastname]) {
print(fullname.toString() ?? "jsDemo1 name not resolved")
}
}
This calls a JavaScript function, getFullname, as follows:
function getFullname(firstname, lastname) {
return firstname + " " + lastname;
}
Another option is to "assign" a function in Swift code when calling a JavaScript function, as demonstrated in the Swift code sample below:
// declare swiftJSFnImpl function (called later via JS object)
let swiftJSFnImpl: @convention(block) (String) -> Void = { value in
print("\nJS Console:", value)
}
// assign swiftJSFnImpl to JS object
let swiftFunctionObject = unsafeBitCast(self.swiftJSFnImpl, to: AnyObject.self)
self.jsContext.setObject(swiftFunctionObject, forKeyedSubscript: "callSwiftFunctionFromJS" as (NSCopying & NSObjectProtocol))
_ = self.jsContext.evaluateScript("callSwiftFunctionFromJS")
// call JS function with
if let fnCallSwiftFromJS = self.jsContext.objectForKeyedSubscript("swiftJSFnImpl") {
// Call the function that calls a Swift function from Javascript
_ = fnCallSwiftFromJS.call(withArguments: nil)
}
function callSwiftFunctionFromJS() {
var value = "hello world";
mySwiftFunction(value);
}
To use a member of the class, such as self.mySecondFunction or self.name inside of an @convention(block) block, treat the block using SOLID principles, although this approach adds more complexity.
Problem
Given a variable jsContext: JSContext! that is a member of the class, BasicsViewController, the error below occurs when attempting to access a class member inside a @convention(block) block:
Cannot use instance member 'self' within property initializer; property initializers run before 'self' is available

Question
Is it possible to capture self or context inside of a Swift block, and if so, how?
Workarounds
There is a workaround to capture self inside of a @convention(block) block by treating the block using SOLID principles, as shown in the following example:
private func getTotalPriceOfProduct(_ product: Product) -> Float? {
let discountedPrice: @convention(block) (Float, Float) -> Float = { price, discount in
price * (1 - discount)
}
jsHandler.setObject(object: discountedPrice, withName: "discountedPrice")
if let value = jsHandler.callFunction(functionName: "getTotalPriceOfProduct", withData: product, type: Product.self) {
if value.isNumber {
return value.toNumber() as? Float
}
else {
print("error while getting total price for \(product.name)")
}
}
return nil
}
For more information on this topic, see the following resources:
- NSHipster
- kodeco.com
- gfrigerio.com
- (dirty workaround) vmanot.com