“Open With” in iOS PhoneGap Apps

Let’s say that you have a PhoneGap app that has access to documents, but you want to open those documents in a different application outside of your PhoneGap application.   For example, let’s say you have a DRM-protected PDF document that gets authenticated by an Adobe LiveCycle server.   On iOS devices, the DRM can only be authenticated if you are using Adobe Reader.  So, you need to delegate handling of that file to Adobe Reader.

You have two ways of handling this type of scenario: Either the target app opens the document via a custom URL scheme, or your app delegates to another application that is registered to handle a specific file format.   If the target app has a custom URL scheme, you just make a request following that scheme’s conventions… Simple, right?   If the target app does not have a custom URL scheme, then what do you do?

iOS supports the ability to use an “open with” menu through the UIDocumentInteractionController class, but by default this is not available to PhoneGap applications.  Luckily for us, PhoneGap is extensible, and we can create native plugins for whatever we want.

Since Adobe Reader does not yet support a custom URL scheme, you could use the UIDocumentInteractionController to suggest that the user opens the document with Adobe Reader, and this is exactly what I did…

I created a PhoneGap native plugin that allows you to specify a file URL and UTI, and it will use the UIDocumentInteractionController class to ask the user to select another program to use for handling the file.  Check out the video below to see it in action:

Using this within your PhoneGap app is dirt simple:

ExternalFileUtil.openWith(
     "http://www.tricedesigns.com/temp/drm.pdf",
     "com.adobe.pdf" );

The plugin workflow is as follows:

  1. PhoneGap app requests ExternalFileUtil.openWith action, specifying a file URL and UTI.
  2. Plugin downloads the file and saves as a local temp file
  3. Plugin uses UIDocumentInteractionController to launch an “open with” dialog
  4. User selects appropriate app to “preview” the content
  5. The appropriate reader app is opened and UI/input is handed over to the target app
  6. Plugin deletes the temp file

Even though I’ve demonstrated this with a PDF and Adobe Reader, this is not limited to just PDF handling.  This plugin should work with any file type that has registered handlers (although I’ve only tested with PDFs & Adobe Reader).  You just need to specify the UTI that is supported by the target application(s).  You can view a list of predefined UTI values from Apple, or create your own applications that support custom UTIs.

If you’re interested, YES, you can use this today.  I’ve already committed it to GitHub and sent a pull request to the master phonegap-plugins repo.  Go download it now!

Enjoy!

  • http://none Mahendra

    Hi,

    This seems to be a great work in direction of opening documents especially PDFs. Although, I haven’t yet tried it – but any insights on whether the plugin will work with PDFs within the ‘www’ folder itself or it needs to be an online document?

    Regards

    Mahendra

    • http://www.tricedesigns.com Andrew

      You could definitely use this technique. You would have to change the native plugin some, b/c it expects remote files. However, this technique would definitely work. As long as you can access the raw file, you can send it to “preview” in other apps.

  • http://n/a Mahendra

    Hi,

    I just tried the plugin – It doesn’t work with v1.4; so I upgraded to v2.0.

    I added ‘ExternalFileUtil’ as the key & value to Cordova.plist and used the code:

    ExternalFileUtil.openWith(
    “http://www.tricedesigns.com/temp/drm.pdf”,
    “com.adobe.pdf” );

    However, it gives me the plugin error:

    2012-10-15 23:44:30.222 TestApp[2923:13403] ERROR: Plugin ‘ExternalFileUtil’ not found, or is not a CDVPlugin. Check your plugin mapping in Cordova.plist.
    2012-10-15 23:44:30.223 TestApp[2923:13403] FAILED pluginJSON = {“className”:”ExternalFileUtil”,”methodName”:”openWith”,”arguments”:["INVALID","http://www.tricedesigns.com/temp/drm.pdf","com.adobe.pdf"]}

    May the plugin mapping is wrong.. Can you post what is the key/value pair to be used in Cordova.plist.

    It would be great if you can upload a working sample project using which anybody can understand what needs to be done and how it is implement.

    Regards,

    Mahendra

    • http://www.tricedesigns.com Andrew

      The key “ExternalFileUtil” maps to the CDVExternalFileUtil class. Make sure that the class (both .h and .m files) has been added to your Xcode project, and that you can see it in the sidebar on the left in Xcode.

  • Paul

    Hi,

    I tried your plugin.

    It stops after downloading the file to the temp-dir (log-outputs).

    Is your plugin compatible with cordova 2.1.0?

    br,
    Paul

    • http://www.tricedesigns.com Andrew

      Yes, it should work. I wrote it with 2.0, but I see no glaring reason that it wouldn’t work in 2.1, although PhoneGap 2.1 supports ARC – did you (or Xcode) make changes to support ARC? If so, that actually changes your Objective-C code. If you un-comment my log statements in CDVExternalFileUtil.m, you can get more insight into what is happening at runtime. The PDF download is a blocking operation, so if the UI just hangs, it’s still working, but downloading. This is especially apparent if it’s a big PDF file. If this is the case, you may want to make the download non-blocking, and make the download/open-with sequence asynchronus.

  • Paul

    Thanks for your answer.

    I found a solution by changing the view argument from “cont.view” to “cont.webView” for iPad Development.

    br,
    Paul

  • manish

    Hello sir, I am using externalutil plugin to open pdf and doc file, but plugin is not calling in cordova2.2.0 …I have added the .h and .m files , with plugin name in Cordova plist file .also added the js file in www folder and calling from index .html on click button..
    Please I want to know how to use this plugin in application .help me
    If you provide proper document to add and use this plugin it will be very helpful for us ..
    Thanks and regards
    Manish

  • laziel

    Hi.
    I’ve tried and failed to use your awesome plugin, for png(public.png) image.
    It doesn’t appears external handler list. File has written in local and presentOpenInMenuRect, successCallback(writeJavascript) processed also in log outputs. ARC doesn’t matter on this problem.

    I need you help.
    laziel.

  • Helmut

    Hi,I tried your example on github,copying the whole content of “sample/www” folder on my “www” folder,but it doesn’t work.
    Have I also to include the obj-c file?
    If yes, in which folder have I to include them?
    Thank you (sorry for my english)

  • kinszhang

    Hi, thanks for the brilliant plugins. However, I tried this plugin but it didn’t work. The debugger shows PluginResult toSuccessCallbackString:cordova.callbackSuccess(‘INVALID’,{“status”:1,”message”:”",”keepCallback”:false}); and the Openwith dialogue didn’t pop up.

    I have copy the .h and .m file into the project plugins folder, in Cordova.plist, I add the mapping, key is ExternalFileUtil, value is CDVExternalFileUtil. and I use your example file but it still didn’t work. Any ideas for that? Thanks

    • http://www.tricedesigns.com Andrew

      Is this happening on an iPad? For some reason, people have been telling me that this line doesn’t work on the iPad:

      CGRect rect = CGRectMake(0, 0, cont.view.bounds.size.width, cont.view.bounds.size.height);

      Instead, change it to a static size, and it should work:

      CGRect rect = CGRectMake(0, 0, 1024, 768);

      • kinszhang

        Thanks, I was figured out later I post my comment, I think it might be relate to the difference between ipad and iphone handle the Open With dialogue. In iphone the Open With menu is displayed like a toast, in iPad it doesn’t.

  • utsu

    thanks for the great plugin. in iOS6.1 iPhone5 works great well.
    but unfortunately iOS6.1 iPad (3rd generation retina display) didn’t work well with:
    CGRect rect = CGRectMake(0, 0, 1024, 768); or changing “cont.view” to “cont.webView”.
    in Cordove(Phonegap)2.2.

    does anyone have solution?

    • Bill

      This does not work for me either. I am getting an error ‘Unable to get data for URL: The operation couldn’t be completed. (Cocoa error 260.)’ with the above solutions for iPad 2 running iOS 6.1. When tested on iphone simulator I was able to get the dialog to open using Laziel’s above comment (changing it to presentOpenInMenuFromRect), but the results were not expected, only options to print, copy or email. It is a shame the latest software will not allow this plugin to work properly :(

      • http://www.tricedesigns.com Andrew

        You will only get options like “Open With Reader” for apps that you actually have installed. If you don’t have reader installed on your device, you will not get the option to open the file with it. If you are able to see the prompt with “print”, “copy” and “email”, then it is working properly.

  • KL

    does it work with data uri? like this:

    ExternalFileUtil.openWith( “data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
    … 9TXL0Y4OHwAAAABJRU5ErkJggg==”, “com.adobe.pdf” );

    • http://www.tricedesigns.com Andrew

      You could probably modify the plugin to work with a data uri like that, but in its current form, no I do not think it will work. It expects a remote URL, then attempts to download the contents locally. Once downloaded, it attempts too preview the file/content using the OS’s preview capability.

      • KL

        I added the plugin and it worked, but after it opened another viewer, the phone gap app crashed, do you have any idea?

        • http://www.tricedesigns.com Andrew

          Do you have any console output, error messages, or additional detail? Without some additional information, it’s pretty much impossible to say.

          • KL

            no error message, but when I switched back to the phonegap, instead of stayed on the same page, the phonegap app restarted. I looked into the code in your plugin and played with it, and it worked when I replaced presentOpenInMenuFromRect with presentOptionsMenuFromRect.

  • Kas

    Hi Andrew, thanks for the great plugin. can you update to cordova2.4 ?
    Thanks.

    • http://www.tricedesigns.com Andrew

      The deprecated API signature should still work. I have plans to update all my plugins, but unfortunately just haven’t had the time to update all of them.

  • jarek

    how do i insert the plugin?

  • kapil kumawat

    Hi Andrew,

    I want to use your plugin in my project, is you plugin is assciated with any LICENSE.

    Regards,
    -Kapil Kumawat

  • Danial

    Hi, thanks for this, but so far I cannot get it to work on my iPad?

    I have the project setup and all appears to be working well but I do not get any popup to choose a reader from?

    I have tried this:
    CGRect rect = CGRectMake(0, 0, cont.view.bounds.size.width, cont.view.bounds.size.height);
    [controller presentOpenInMenuFromRect:rect inView:cont.view animated:YES];

    ans this:
    CGRect rect = CGRectMake(0, 0, 1024, 768);
    [controller presentOptionsMenuFromRect:rect inView:cont.webView animated:YES];

    and test for the result of the presenting of the documentinteractioncontroller that returns true but neither appears or calls the event to signal did pass file to reader

    ???

    • http://www.tricedesigns.com Andrew

      Do you have any additional detail, OS version, device type, or error messages? I’ve heard other people have issues, all of which are discussed in the blog comments on this post.

  • Marc

    Hi Andrew, thank you for your plugin, it is exactly what I want for my application.

    Unfortunately, it doesn’t seem to work on my project. Even your simple PDF example doesn’t bring up any “Open with” menu. I have installed your plugin and I know it’s called because I uncommented some NSLog lines from your code.

    It downloads the file (I can open the downloaded file by commenting your cleanup code) but the menu doesn’t open. The log does not show any error at all. Even I set two callbacks (success, failure) and the success callback is ALWAYS executed (but it does not receive any argument).

    With this information, do you have a clue on what’s wrong in my app?

    Thank you.

    • http://www.tricedesigns.com Andrew

      Check the previous comments on this blog. People have had issues in certain cases, on certain devices. Otherwise, do you have any additional information that could be used to debug the issue?

  • Stefan

    Hi,

    I’m using Codova 2.5, Xcode 4.6.1 and can’t get this running.

    In iPAd Simulator I get a Success callback but the the app seems to hang ,meaning no opening of the menu as in your video.

    On my physical Ipad I get following error:
    2013-04-04 13:25:14.684 eidApp[4074:907] The file name is a_file_com_adobe.pdf
    2013-04-04 13:25:16.301 eidApp[4074:907] Resource file ‘a_file_com_adobe.pdf’ has been written to the Documents directory from online
    2013-04-04 13:25:18.493 eidApp[4074:907] *** Terminating app due to uncaught exception ‘NSGenericException’, reason: ‘-[UIPopoverController dealloc] reached while popover is still visible.’
    *** First throw call stack:
    (0x31a452a3 0x398ae97f 0x31a451c5 0x33c35a91 0x33b87e3d 0x398ab489 0×31988441 0x3235c185 0x31a1a683 0x31a19ee9 0x31a18cb7 0x3198bebd 0x3198bd49 0x355402eb 0x338a1301 0x662e7 0x662a8)
    libc++abi.dylib: terminate called throwing an exception

    I tried with cont.view. and cont.Webview as mentioned in anoher mail but no success.

    Regards,

    Stefan

    • http://www.tricedesigns.com Andrew

      Hi Stefan,
      Do you have any sample code that you can share? It’s hard to say exactly what the error might be.

  • kapil

    Can you provide me the license information to use this plugin.

  • http://www.jcesarmobile.com jcesarmobile

    Hi,
    Do you know if adobe reader support url schemes now? or if adobe is going to support it soon?

    • http://www.tricedesigns.com Andrew

      To my knowledge, it does not support URL schemes. Unfortunately, I do not know whether that is in the roadmap.

  • ben

    Any suggestions on how to change the plugin to work with a data URI ? I’m attempting to use this with another great library jsPDF. Just trying to figure out how I can use this to open the document in something other than inappbrowser, so the user can print/email the pdf file that gets generated.

    • http://www.tricedesigns.com Andrew

      You can’t use a data URL in the preview implementation. Instead, you can save the generated pdf file to your local file system, then use the preview to open the local file. I have done this in the past… I’ll post code to the blog later if I have time to dig it up.