Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

returning variables from objective c to javascript

I have a phonegap application and I want to run a very simple "does file exist" command in the Documents folder. And I've got it mostly working. In js, I have:

fileDownloadMgr.fileexists("logo.png");
......
PixFileDownload.prototype.fileexists = function(filename) {   
    PhoneGap.exec("PixFileDownload.fileExists", filename);
};

Then in Objective C, I have:

-(BOOL) fileExists:(NSMutableArray*)paramArray withDict:(NSMutableDictionary*)options;{
  NSString * fileName = [paramArray objectAtIndex:0];

  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *documentsDirectory = [paths objectAtIndex:0];   
  NSString *newFilePath = [documentsDirectory stringByAppendingString:[NSString stringWithFormat: @"/%@", fileName]];

  BOOL isMyFileThere = [[NSFileManager defaultManager] fileExistsAtPath:newFilePath];

  //i'm stuck here  
}

I can use NSLog to print this to the console to see that the logic does work and the BOOL is set correctly. But I need that variable back in javascript world. I'm aware of stringByEvaluatingJavaScriptFromString, but that will just execute javascript, ie call a callback function. That's not what I need here, I need (in javascript):

var bool = fileDownloadMgr.fileexists("logo.png");
if(bool) alert('The file is there!!!!!!');

What do I need to do to return that bool from objective c back into javascript?

like image 421
Landon Avatar asked Feb 23 '26 07:02

Landon


1 Answers

Since the call to PhoneGap.exec is asynchronous, you need to pass a function that is invoked when the called Objective-C method succeeds. Make the success handler an argument to fileexists (the reason why is explained later):

PixFileDownload.prototype.fileexists = function(filename, success) {   
    PhoneGap.exec(success, null, "PixFileDownload", "fileExists", filename);
};

The second argument to PhoneGap.exec is an error handler, unused here.

Within the Obj-C method, use a PluginResult to pass the result to the success function via the -resultWithStatus:messageAsInt: method.

-(BOOL) fileExists:(NSMutableArray*)paramArray withDict:(NSMutableDictionary*)options;{
    ...
    //i'm stuck here
    /* Create the result */
    PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK 
                                                messageAsInt:isMyFileThere];
    /* Create JS to call the success function with the result */
    NSString *successScript = [pluginResult toSuccessCallbackString:self.callbackID];
    /* Output the script */
    [self writeJavascript:successScript];

    /* The last two lines can be combined; they were separated to illustrate each
     * step.
     */
    //[self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]];
}

If the Obj-C method may result in error conditions, use PluginResult's toErrorCallbackString: to create a script that calls the error function. Make sure you also pass an error handler as the second argument to PhoneGap.exec.

Coordination & Continuations

Now, the promised explanation for adding the success parameter to fileexists. "Coordination" is a feature of computation meaning that code doesn't run until any computation it relies on has completed. Synchronous calls give you coordination for free because functions don't return until their computation completes. With asynchronous calls, you need to take care of coordination. You do this by bundling the dependent code in a function called a "continuation" (which means "the rest of the computation from a given point forward") and passing this continuation to the asynchronous function. This is called (unsurprisingly) continuation passing style (CPS). Note that you can use CPS with synchronous calls, it's just not quite as common.

PhoneGap.exec is asynchronous, so it accepts continuations, one to invoke on success and one on failure. fileexists depends on an asynchronous function so it is itself asynchronous and needs to be passed a continuation. The code after fileDownloadMgr.fileexists("logo.png"); should be wrapped in a function passed to fileexists. For example, if you originally had:

if (fileDownloadMgr.fileexists("logo.png")) {
    ...
} else {
    ...
}

Creating a continuation is straightforward, though it can get a bit hairy when you have multiple continuations. Rewrite the if statement as a function, replacing the call to the asynchronous function with a variable:

function (x) {
    if (x) {
        ...
    } else {
        ...
    }
}

Then pass this continuation to fileexists:

fileDownloadMgr.fileexists("logo.png", function (exists) {
    if (exists) {
        ...
    } else {
        ...
    }
});

Further Reading

I couldn't find PluginResult's -resultWithStatus:messageAsInt:, but there is an example that shows how to return a value from an Obj-C method back to JS in "How to Create a PhoneGap Plugin for iOS". The documentation for PhoneGap.exec in the API docs is currently rather poor. Since both are Wiki pages, perhaps I or someone else will find the time to improve them. There are also the header and implementation files for PluginResult.

like image 161
outis Avatar answered Feb 25 '26 21:02

outis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!