I need to return a value in Swift by executing JavaScript, but I am having trouble I have searched everywhere, but I can't find anything that works. To execute the JavaScript, I am using
webby.evaluateJavaScript("document.querySelector(\"pre\").innerText;")
(Webby is the name of the web view)
The JavaScript returns a string, which I would like to access in Swift. How would I accomplish this? Thanks.
To access the value you have to provide a completion handler to evaluateJavaScript, see the docs:
webby.evaluateJavaScript("<your code goes here>") { result, error in
if let result = result {
// your result handling goes here
}
}
Related
I'm working on a Meteor project and want to get the return value of Meteor.call in template helpers on client side. At very first, I just set a variable in the call back function and get the variable's value outside the Meteor.call. I found out the code after Meteor.call doesn't execute at all. Then I searched a bit and use Session, it works. But I don't really understand the reason. Here's my original code and modified code. Can anyone explain a bit for me? Thanks!!
Original wrong code: html
<div id="text-result-main">
<h2>{{title}}</h2>
</div>
js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
console.log(titles);
return titles;
}});
Collection text.js
Text = new Mongo.Collection("text");
Meteor.methods({
'getTitle': function(myindex){
return Text.findOne({index: myindex}).title;
}});
The working code: js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
Session.set("titles",result);
});
console.log(Session.get("titles"));
return Session.get("titles");
}});
Notice that I didn't publish Collection Text to the client at all because it's just so huge. Every time when I refresh the page when running the wrong code, I can't see the content of "title" or see it on the console. But when I set the session, it works. I don't really understand how it works here. Thanks
There is two issues Asynchronicity and Reactivity
This affectation
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
inside the meteor call is executed but in a asynch way. So the return of your helper is immediately called, and return a empty value.
Try it out in the console of your browser.
But then, why your template render correctly with {{title}} when you use a Session Variable ?
It's because the Session is a reactive data source, witch means that every change to it trigger a re-computation of all templates involving this piece of data.
Here is a timeline:
Methods is called
Return empty value
Method is executed, setting variable value
If the Variable is a reactive data source, template is re-computed. ( in your case, the session is a reactive data source. )
To go further
I would use a reactive var in that case, it's very close from a session variable, but the scope is limited to a template.
A good read on Reactive data source: http://richsilv.github.io/meteor/meteor-reactive-data-types/
The problem is the fact that Meteor.call() is asynchronous when paired with a callback.
So when title() starts executing, it does not wait for your Meteor.call() invocation to return a result (or possibly an error). It continues execution. This is called asynchronous execution.
In short, you are trying to log the value for the key titles which doesn't exist in Session (since the state of your asynchronous Meteor call is unknown, at this point of time).
Try moving the console log statement into the callback paired with your Meteor.call() and you can see the result once it has successfully been set in Session.
A workaround to your problem is to make your Meteor.call() synchronous like this:
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
var result = Meteor.call('getTitle', index); // <--- this is synchronous code now
Session.set("titles",result);
console.log(Session.get("titles"));
return Session.get("titles");
}});
Removing the callback makes Meteor.call() behave synchronously.
If you do not pass a callback on the server, the method invocation
will block until the method is complete. It will eventually return the
return value of the method, or it will throw an exception if the
method threw an exception.
(from http://docs.meteor.com/api/methods.html#Meteor-call)
Why not use something like this:
title: function(){
var index = Router.current().params.index;
var a = Text.findOne({index: myindex}).title;
console.log(a);
return a;
without methods
Before anyone marks it as duplicate, this post does not actually answer the question but suggests a different way altogether to solve that particular issue.
Mine is a different issue. Please let me explain.
In my case, there are various .js files (plugins) which are being loaded with jquery getscript and stored in variables. Then whenever required they will be executed (more than once)
The code for loading script (this code will only run once at the init of the system for each plugin js file)
var storedFunc;
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
storedFunc = data;
});
All the plugins are in this format
(function(){
//lots of code here
})()
But when I checked the storedFunc variable in console, I found out that it has been stored as String variable. Like this,
"(function(){
//lots of code here
})()"
Now to execute this, I used eval, like this (this code can be executed multiple times based on the need)
eval(storedFunc)
Everything is working fine and i am happy with it, but here comes the problem, I read in somewhere that the usage of eval is kind of like a bad thing to do. So now I am afraid that thought everything is working fine, all these negativity of using eval spread on the internet might scare my client away. :(
So, please tell me how I can run that stored function (which has become a string) without using eval.
Or should I use anything else than $.getScript which does not convert a function into a string ?
Or if there is any other way altogether rewriting this plugin functionality?
Please show me the way. I am in need of this solution badly.
Any help will be appreciated.
Thanks in advance.
Understanding how $.getScript works
Seems there is some confusion on how $.getScript works. If you notice jQuery's documentation on the method, and as #Pointy made mention of in the comments, this is stated:
Load a JavaScript file from the server using a GET HTTP request, then execute it.
Here's an example: Let's pretend the contents of the file being returned is only this:
// Contents of yourExternalFile.js
console.log('Executed!');
Now, when you use $.getScript:
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
// The script you retrieved has already executed at this point, and you will find "Executed!" in the console.
console.log('All Done');
});
Console output:
> Executed!
> All Done
The $.getScript method is not meant to be used to return a string of the content of the file. However, while that data is available in the callback, the contents of the file have already been executed. So by taking the string version of the file, and re-executing it with either new Function, or even eval, you are executing it twice on the page (jQuery does it once, and so do you).
Original Post:
Use the Function constructor instead of using eval.
// Your function as a string stored to a variable
var stringFunction = "(function(){console.log('Executed');})()";
// Use the Function constructor to create a new function:
var executableFunction = new Function(stringFunction);
// Now you can execute it
executableFunction(); // logs "Executed"
This snippet from this SO question/answer addresses the difference between eval and new Function.
eval() evaluates a string as a JavaScript expression within the current execution scope and can access local variables.
new Function() parses the JavaScript code stored in a string into a function object, which can then be called. It cannot access local variables because the code runs in a separate scope.
Additional Information (Based on comments)
Yes, you can just get the string contents of the file and store them to a variable without the contents of the file executing. You can have that function to execute anytime. You just need to use the regular get method using jquery, and set the dataType to text. This way, the script will not execute, and you can execute it as you see fit:
var storedFunction;
$.get({url: pathToPluginJSFile, dataType: 'text'})
.done(function (data) {
// Turn the script into a new function and store it
// The information in the script file has not done anything yet
storedFunction = new Function(data);
})
.fail(function () {
console.log('Failed :(');
});
The only thing you will have to watch out for, is making sure that the function was assigned to the storedFunction variable as you are making an api call, and you have to wait for that to finish before attempting to make the function execute.
// Later on, call that function anytime, and as often as you want:
storedFunction();
I'm attempting to use Blockly to do a "make your own game" sort of thing, and for the most part it works. However, when trying to run code generated (by Blockly's own pre-defined function generators) by declaring a function and calling it, I consistently get told that the function isn't defined, no matter what it is or what it contains.
I'm grabbing and running the code like so:
var code = Blockly.JavaScript.workspaceToCode();
try{
eval(code);
} catch (e){
alert(e);
}
Which is how the demos provide on Blockly generate code. I've also echoed the code out elsewhere in the page and it looks right to me:
function whatINameIt() {
//code I give it
}
//rest of code
Is this something to do with how eval works? The only thing I can think of is that for some reason it's "evaluating" the function code but not adding it as something callable. If that's the case, is there an alternate way I should run the code string Blockly gives me?
Maybe you are creating an infinite loop. To solve it, you will have to add the following lines as the documentation of Blockly says:
window.LoopTrap = 1000;
Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = Blockly.JavaScript.workspaceToCode(workspace);
Also, if you have created custom Blocks, as it seems in your question, make sure that you are returning the code that you are creating in all of them. If you do not return it, the workspace will not know that these Blocks want to do whatever.
It would be great to help you if you provide the Blocks code that you are creating/using and the code that you are retrieving from your other function.
I have a function that calls another function over the server and returns a string back which i want to be printed in the browser's log windows, The function looks like:
function getErrorInfo() {
setTimeout(function () {
$.getJSON('Get/ErrorInfo', function (responseText) {
console.log("Log: "+responseText);
});
}, 5000);
}
getErrorInfo();
The function on the server sides does gets hits and returns a valid string But nothing is being displayed in the browser's windows Moreover the function on the server side must get hit after every 5 secs. but it only gets his on time and not again.
Please explain what am i doing wrong here.
Your basic issue is that you need to have properly formatted JSON in order to get back any result. Your result (per above) is:
3/8/2014 5:27:16 PMSystem.NullReferenceException: Object reference not set to an instance of an object. at movie.Models.Genre.GetPosts(Int32 min)
Not only is this an exception text, but it isn't valid JSON. JSON format is fully described here. Rather than calling a real service, I would recommend starting by just getting a static JSON file from the server. Then you know the data is correct.
Side Note:
The other issue here is how you print the OBJECT result from getJSON. When you try to print an object using "Console.log" it converts it to a string, which isn't probably going to show what you want. You should probably change your log statement to:
console.log(responseText);
In chrome at least, the console window will let you browse the contents of the object which can be really helpful. Between the note and the solution above I think you should have it. Best of luck!
When using $.getJSON(), the return result is required to be a valid JSON string, meaning it needs to be parsable into an object or array. In this situation, you can probably simply use $.get(), which will autodetect the return data type, or use $.ajax() and set the dataType: plain if you want to skip the JSON requirement.
On the second issue of keeping the log running, you can call getErrorInfo() from inside the setTimeout() or the callback, and it will keep running:
function getErrorInfo() {
setTimeout(function () {
$.getJSON('/echo/json/', function (responseText) {
console.log("Log: "+responseText);
getErrorInfo();
});
}, 5000);
}
getErrorInfo();
http://jsfiddle.net/Er5Lg/
In my opinion, in this situation, it is better than setInterval(), since that can get backed up and end up overriding calls, and the errors might display out of order.
Since I'm using this type of call often I wish to make this reusable:
function getJSON(cmd){
$.getJSON(cmd, function(data) {
}).done(function(data) {
return data;
}).fail(function() { console.log('Server failed!') });
}
I hoped to use it like this:
function susbcribe(id){
var cmd = 'subscribe.php?='+id;
var obj = getJSON(cmd);
console.log(obj);
}
But javascript runs console.log before the async json can even return anything to obj.
Just to be clear - i know i can execute code inside of .done(), but because I use this often I wish to forgo rewriting the same function over and over.
So the question is: is there a way to make js stop and wait for getJSON to finish and return something to obj ?
As things currently stand, you will have to at least write the done function every time. You can't escape callback hell by pretending it doesn't exist.
There are ways to avoid some of it by using promises cleverly, but for simple things, this is pretty much as simple as it gets. Until we get support for generators/iterators some time in 2025.
You could set the fail function as a global "ajax event" handler to avoid having to type error handling every time.