Skip to content

Instantly share code, notes, and snippets.

@acegreen
Last active October 27, 2015 03:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acegreen/05282d20eb45de8d30d5 to your computer and use it in GitHub Desktop.
Save acegreen/05282d20eb45de8d30d5 to your computer and use it in GitHub Desktop.
let script: JSValue = jsContext.objectForKeyedSubscript("getImageURL")
[jsContext callWithArguments:@["FirstParam",^(NSString* callbackValue) {
NSLog(@"Got a value: %@",callbackValue)
}]
basically I have a javascript function that looks like this:
function getImageURL(object, callback) {
object.executeActionById("takeScreenshot");
object.onScreenshotReady(function(imageName) {
alert(imageName);
callback(imageName);
});
};
And all I really want is imageName on the iOS side. So I'm trying to call the callback part of it on iOS and get imageName.
onScreenshotReady is async so I need to wait for it to finish before I get my imageName. So I created a callback within it
@acegreen
Copy link
Author

I tried the following:

        script.callWithArguments(["widget", callbackHandler: { (imageName: NSString!) -> NSString in
        }])

@jackwu95
Copy link

Should be just

script.callWithArguments(["widget", { imageName: String in
// Got image
        }])

@acegreen
Copy link
Author

@jackwu95 first off thanks for the reply.

I tried that and still getting an issue.

I also tried

script.callWithArguments(["widget", { (imageName: String) in
// Got image
}])

screen shot 2015-09-18 at 1 00 16 pm

brackets around (imageName: String) but its still too ambiguous.

@jackwu95
Copy link

@acegreen
Copy link
Author

Yea I saw that link but can't get it to work. .callWithArguments wants an array and I'm trying to add a callback block to it.

@acegreen
Copy link
Author

This is as far as I have come now. Swift needs to be converted to @objc_block (now called convention(block). Doesn't execute getImageURL though because no image or alert is generated. Also doesn't print on iOS

        let script: JSValue = jsContext.objectForKeyedSubscript("getImageURL")
        print(script)

        let simplifyString: @convention(block) String -> String = { imageName in

            print(imageName)

            return imageName
        }

        let unsafeString = unsafeBitCast(simplifyString, AnyObject.self)

        script.callWithArguments(["widget", unsafeString])

print(script) shows the correct function:

function getImageURL(object, callback) {
object.executeActionById("takeScreenshot");
object.onScreenshotReady(function(imageName) {
alert(imageName);
callback(imageName);
});
}

@natecook1000
Copy link

Looks like any remaining problems may be in your Javascript -- this is working properly for me:

// set up context
let context = JSContext()
context.exceptionHandler = { context, exception in
    print("JS Error: \(exception)")
}

// two functions: 
// immediately calls the callback with the first argument
context.evaluateScript("var test = function(object, callback) { callback('test ' + object); }")
// calls the first function with a function literal that calls the callback
// this one better emulates the flow of getImageTest
context.evaluateScript("var nestedTest = function(object, callback) { test(object, function(nestedObject) { callback('nestedTest ' + nestedObject); }); }")

// create the callback and cast to AnyObject
let callback: @convention(block) String -> () = { imageName in
    print(imageName)
}
let args = ["Hello from JS!", unsafeBitCast(callback, AnyObject.self)]

context.objectForKeyedSubscript("test").callWithArguments(args)
// prints "test Hello from JS!"
context.objectForKeyedSubscript("nestedTest").callWithArguments(args)
// prints "nestedTest test Hello from JS!"

@acegreen
Copy link
Author

Hi @natecook1000 first off thanks for the reply.

With the code in my last reply. I can print(script) and it prints the right function. But the callWithArguements doesn't do anything.

I can execute the getImageURL with the following and it actually alerts the imageName

let script = "getImageURL(widget, function(imageName) { })"
jsContext.evaluateScript(script)

but on iOS this doesn't wait for a return before executing the rest. so I don't think my function is wrong is it?

By the way my getImageURL isn't in a variable like var test = its just in index.html while I'm loading in my webView and passing it to my jsContext. and it prints it correctly.

@acegreen
Copy link
Author

I GOT IT!!!!!!!! :)

@natecook1000
Copy link

🎉 👍 🎈 👏

What did the trick?

@acegreen
Copy link
Author

I added the exceptionHandler to see if I get any errors:

And turns out I did. "widget" was not being passed along so the function was executing

object.executeActionById("takeScreenshot");
object.onScreenshotReady(function(imageName) ....

instead of
widget.executeActionById("takeScreenshot");
widget.onScreenshotReady(function(imageName) ....

So since I actually don't need to pass that parameter from iOS. I just hardcoded it directly. so my function became:

        function getImageURL(callback) {
            widget.executeActionById("takeScreenshot");
            widget.onScreenshotReady(function(imageName) {
              callback(imageName);
            });
        };

and on iOS side i did:

        let args = [unsafeBitCast(callback, AnyObject.self)]

        jsContext.objectForKeyedSubscript("getImageURL").callWithArguments(args)

And works exactly as expected.

Many many thanks for feedback. Spent two days on this pesky issue. Now onto the next

@tv-webdev
Copy link

HI @acegreen,

How can I save an image to my local("Downloads") without using the snapshot url from tradingview?

@tv-webdev
Copy link

Hi @acegreen,
In addition to my first question, how can I create a method that will not generate an image on tradingview, but generating a base64 image and saving it to my local directory.

Thanks in advance..=

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment