Cordova: sharing browser URL to my iOS app (Clipper ios share extension)












15















What I want



On an Iphone, when visiting a website inside Safari or Chrome, it is possible to share content to other apps. In this case, you can see I can share the content (basically the URL) to an app called Pocket.



Pocket example



Is it possible to do that? And specifically with Cordova?










share|improve this question

























  • Hey Sebastien, Any success ?

    – Burhan Mughal
    Dec 3 '15 at 7:16











  • @BurhanMughal I've answered my own question! this is possible and it took me a long time to figure it out! hope it will help you

    – Sebastien Lorber
    Dec 3 '15 at 11:32
















15















What I want



On an Iphone, when visiting a website inside Safari or Chrome, it is possible to share content to other apps. In this case, you can see I can share the content (basically the URL) to an app called Pocket.



Pocket example



Is it possible to do that? And specifically with Cordova?










share|improve this question

























  • Hey Sebastien, Any success ?

    – Burhan Mughal
    Dec 3 '15 at 7:16











  • @BurhanMughal I've answered my own question! this is possible and it took me a long time to figure it out! hope it will help you

    – Sebastien Lorber
    Dec 3 '15 at 11:32














15












15








15


14






What I want



On an Iphone, when visiting a website inside Safari or Chrome, it is possible to share content to other apps. In this case, you can see I can share the content (basically the URL) to an app called Pocket.



Pocket example



Is it possible to do that? And specifically with Cordova?










share|improve this question
















What I want



On an Iphone, when visiting a website inside Safari or Chrome, it is possible to share content to other apps. In this case, you can see I can share the content (basically the URL) to an app called Pocket.



Pocket example



Is it possible to do that? And specifically with Cordova?







ios iphone safari cfbundledocumenttypes






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 3 '15 at 10:06







Sebastien Lorber

















asked Oct 13 '15 at 14:44









Sebastien LorberSebastien Lorber

52.3k37206333




52.3k37206333













  • Hey Sebastien, Any success ?

    – Burhan Mughal
    Dec 3 '15 at 7:16











  • @BurhanMughal I've answered my own question! this is possible and it took me a long time to figure it out! hope it will help you

    – Sebastien Lorber
    Dec 3 '15 at 11:32



















  • Hey Sebastien, Any success ?

    – Burhan Mughal
    Dec 3 '15 at 7:16











  • @BurhanMughal I've answered my own question! this is possible and it took me a long time to figure it out! hope it will help you

    – Sebastien Lorber
    Dec 3 '15 at 11:32

















Hey Sebastien, Any success ?

– Burhan Mughal
Dec 3 '15 at 7:16





Hey Sebastien, Any success ?

– Burhan Mughal
Dec 3 '15 at 7:16













@BurhanMughal I've answered my own question! this is possible and it took me a long time to figure it out! hope it will help you

– Sebastien Lorber
Dec 3 '15 at 11:32





@BurhanMughal I've answered my own question! this is possible and it took me a long time to figure it out! hope it will help you

– Sebastien Lorber
Dec 3 '15 at 11:32












5 Answers
5






active

oldest

votes


















22














Edit: sooner or later a simple mobile website will probably be able to receive content shared from native apps. Check the Web Share Target protocol



I'm answering my own question, as we finally succeeded implementing the iOS Share Extension for a Cordova application.



First the Share Extension system is only available for iOS >= 8



However it is kind of painful to integrate it in a Cordova project because there's no special Cordova config to do so. When creating a Share Extension, it is hard for the Cordova team to reverse-engineer the XCode xproj file to add a share extension so it will probably be hard in the future too...



You have 2 options:




  • Version some of your iOS platform files (like the xproj file)

  • Include a manual procedure after generating the iOS platform with cordova


We decided to go with the 2nd option, as our extension is pretty stable and we will not modify it often.



Create the share extension manually



VERY IMPORTANT: create the share extension, and the Action.js THROUGH the XCode interface! They have to be registered in the xproj file or it won't work at all. See



Create the files through XCode



To create a share extension for a Cordova app, you will have to do like any iOS developer would do.




  • Open the ios platform xproj on XCode

  • File > New > Target > Share Extension

  • Select Swift as a language (only because ObjC seems unpleasant to me)


You get a new folder in XCode with some files that you will have to customize.



You will also need an extra Action.js file in that share extension folder. Create a new empty file (through XCode!) Action.js



Handle browser data extraction



Put in Action.js the following code:



var Action = function() {};

Action.prototype = {

run: function(parameters) {
parameters.completionFunction({"url": document.URL, "title": document.title });
},

finalize: function(parameters) {

}

};

var ExtensionPreprocessingJS = new Action


When your share extension is selected on top of a browser (I think it only works for Safari), this JS will be run and will permit you to retrieve the data you want on that page in your Swift controller (here I want the url and the title).



Customize Info.plist



Now you need to customize the Info.plist file to describe what kind of share extension you are creating, and what kind of content you can share to your app. In my case I mostly want to share urls, so here is a config that works for sharing urls from Chrome or Safari.



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>MyClipper</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionJavaScriptPreprocessingFile</key>
<string>Action</string>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>


Notice that we registered the Action.js file in that plist file.



Customize the ShareViewController.swift



Normally you would have to implement by yourself Swift views that will be run on top of the existing app (for me on top of the browser app).



By default, the controller will provide a default view that you can use, and you can perform requests to your backend from there. Here is an example from which I inspired myself that do so.



But in my case, I am not an iOS developer and I want that when the user select my extension, it opens my app instead of displaying iOS views. So I used a custom URL scheme to open my app clipper: myAppScheme://openClipper?url=SomeUrl
This permits me to design my clipper in HTML / JS instead of having to create iOS views.



Notice that I use a hack for that, and Apple may forbid to open your app from a Share Extension in future iOS versions. However this hack works currently for iOS 8.x and 9.0.



Here is the code. It works for both Chrome and Safari on iOS.



//
// ShareViewController.swift
// MyClipper
//
// Created by Sébastien Lorber on 15/10/2015.
//
//

import UIKit
import Social
import MobileCoreServices

@available(iOSApplicationExtension 8.0, *)
class ShareViewController: SLComposeServiceViewController {

let contentTypeList = kUTTypePropertyList as String
let contentTypeTitle = "public.plain-text"
let contentTypeUrl = "public.url"

// We don't want to show the view actually
// as we directly open our app!
override func viewWillAppear(animated: Bool) {
self.view.hidden = true
self.cancel()
self.doClipping()
}

// We directly forward all the values retrieved from Action.js to our app
private func doClipping() {
self.loadJsExtensionValues { dict in
let url = "myAppScheme://mobileclipper?" + self.dictionaryToQueryString(dict)
self.doOpenUrl(url)
}
}

///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

private func dictionaryToQueryString(dict: Dictionary<String,String>) -> String {
return dict.map({ entry in
let value = entry.1
let valueEncoded = value.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
return entry.0 + "=" + valueEncoded!
}).joinWithSeparator("&")
}

// See https://github.com/extendedmind/extendedmind/blob/master/frontend/cordova/app/platforms/ios/extmd-share/ShareViewController.swift
private func loadJsExtensionValues(f: Dictionary<String,String> -> Void) {
let content = extensionContext!.inputItems[0] as! NSExtensionItem
if (self.hasAttachmentOfType(content, contentType: contentTypeList)) {
self.loadJsDictionnary(content) { dict in
f(dict)
}
} else {
self.loadUTIDictionnary(content) { dict in
// 2 Items should be in dict to launch clipper opening : url and title.
if (dict.count==2) { f(dict) }
}
}
}

private func hasAttachmentOfType(content: NSExtensionItem,contentType: String) -> Bool {
for attachment in content.attachments as! [NSItemProvider] {
if attachment.hasItemConformingToTypeIdentifier(contentType) {
return true;
}
}
return false;
}

private func loadJsDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
for attachment in content.attachments as! [NSItemProvider] {
if attachment.hasItemConformingToTypeIdentifier(contentTypeList) {
attachment.loadItemForTypeIdentifier(contentTypeList, options: nil) { data, error in
if ( error == nil && data != nil ) {
let jsDict = data as! NSDictionary
if let jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey] {
let values = jsPreprocessingResults as! Dictionary<String,String>
f(values)
}
}
}
}
}
}


private func loadUTIDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
var dict = Dictionary<String, String>()
loadUTIString(content, utiKey: contentTypeUrl , handler: { url_NSSecureCoding in
let url_NSurl = url_NSSecureCoding as! NSURL
let url_String = url_NSurl.absoluteString as String
dict["url"] = url_String
f(dict)
})
loadUTIString(content, utiKey: contentTypeTitle, handler: { title_NSSecureCoding in
let title = title_NSSecureCoding as! String
dict["title"] = title
f(dict)
})
}


private func loadUTIString(content: NSExtensionItem,utiKey: String,handler: NSSecureCoding -> Void) {
for attachment in content.attachments as! [NSItemProvider] {
if attachment.hasItemConformingToTypeIdentifier(utiKey) {
attachment.loadItemForTypeIdentifier(utiKey, options: nil, completionHandler: { (data, error) -> Void in
if ( error == nil && data != nil ) {
handler(data!)
}
})
}
}
}


// See https://stackoverflow.com/a/28037297/82609
// Works fine for iOS 8.x and 9.0 but may not work anymore in the future :(
private func doOpenUrl(url: String) {
let urlNS = NSURL(string: url)!
var responder = self as UIResponder?
while (responder != nil){
if responder!.respondsToSelector(Selector("openURL:")) == true{
responder!.callSelector(Selector("openURL:"), object: urlNS, delay: 0)
}
responder = responder!.nextResponder()
}
}
}

// See https://stackoverflow.com/a/28037297/82609
extension NSObject {
func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
let delay = delay * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), {
NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
})
}
}


Notice there are 2 ways to load the Dictionary<String,String>. This is because Chrome and Safari seems to provide the url and title of the page in 2 different ways.



Automating the process



You must create the Share Extension files and Action.js file through the XCode interface. However, once they are created (and referenced in XCode), you can replace them with your own files.



So we decided that we will version the above files in a folder (/cordova/ios-share-extension), and override the default share extension files with them.



This is not ideal but the minimum procedure we use is:




  • Build Cordova iOS platform (cordova prepare ios)

  • Open project in XCode

  • Create share extension with (product name="MyClipper", language="Swift", organization name="MyCompany")

  • On the "MyClipper", create an empty file "Action.js"

  • Copy the content of /cordova/ios-share-extension to cordova/platforms/ios/MyClipper


This way the extension is correctly registered in the xproj file but you still have the ability to version control your extension.



Edit 2017: this may become easier to setup all that with cordova-ios@5.0.0, see https://issues.apache.org/jira/browse/CB-10218






share|improve this answer


























  • Thanks! Very helpful!

    – Flock Dawson
    Dec 16 '15 at 7:53






  • 1





    Thank you very much, this was the only post on SO with anything useful related to original question. I required handling URL+text+image in various combinations and created a derivation of this answer to do so. Just to pay it forward, github.com/inshikos/cordova-ios-share-extension/blob/master/… might be of help to others needing a solution for content sharing to a Cordova app in the absence of a proper iOS share extension plugin.

    – Warren Kim
    Jan 20 '16 at 8:34











  • thanks @WarrenKim your code could be helpful to me in the future too :)

    – Sebastien Lorber
    Jan 20 '16 at 9:23











  • Hey @SebastienLorber ! Your post seems really helpful :) Thanks! One thing I'd like to ask: I did everything like you described, but still can't see an icon of my app in sharing screen. Could you please point me where it may stuck ?

    – Stanislav Iegorov
    Apr 8 '16 at 14:10











  • @StanislavIegorov I have no idea. Maybe you did not set target >= 7.0 in XCode, or missed a step

    – Sebastien Lorber
    Apr 8 '16 at 14:20



















3














doOpenUrl() above needs to be updated to work on iOS 10. The following code also works on older versions of iOS.



private func doOpenUrl(url: String) {

let url = NSURL(string:url)
let context = NSExtensionContext()
context.open(url! as URL, completionHandler: nil)

var responder = self as UIResponder?

while (responder != nil){
if responder?.responds(to: Selector("openURL:")) == true{
responder?.perform(Selector("openURL:"), with: url)
}
responder = responder!.next
}
}





share|improve this answer
























  • how to do this "doOpenUrl() above needs to be updated to work on iOS 10."? after paste those codes, i've got many red dot.

    – crapthings
    Mar 2 '17 at 3:53



















2














You should be able to achieve your goal with far less manual work using this cordova plugin. It'll also work on Android.






share|improve this answer































    1














    That's a good and still relevant question.



    I tried to make use of the awesome cordova-plugin-openwith by Jean-Christophe Hoelt but faced several issues. The plugin is meant to receive share items of one type (say, URL, text or image), which is configured during installation. Also, with its current implementation, writing a note to share and selecting a receiver in a Cordova app are two different steps in different (native and Cordova) context, so it didn't look as a good user experience to me.



    I made these and other corrections to this plugin and published it as a separate plugin:
    https://github.com/EternallLight/cordova-plugin-openwith-ios



    Note that it works only for iOS, not for Android.






    share|improve this answer































      0














      Following up on the iOS 10 update remark by Aaron Rosen, here's the process to make it work :





      1. In the code from the original answer by Sebastien Lorber, update the doOpenUrl function as suggested by Aaron. Reposting here for clarity:



        private func doOpenUrl(url: String) {
        let url = NSURL(string:url)
        let context = NSExtensionContext()
        context.open(url! as URL, completionHandler: nil)
        var responder = self as UIResponder?
        while (responder != nil){
        if responder?.responds(to: Selector("openURL:")) == true{
        responder?.perform(Selector("openURL:"), with: url)
        }
        responder = responder!.next
        }
        }


      2. Follow the process outlined in the initial answer to create the extension in Xcode


      3. Select ShareViewController.swift in the extension folder

      4. Go to Edit > Convert > To Current Swift Syntax

      5. In the extension build settings, toggle "Require Only App-Extension-Safe API" to NO.


      Only then will the extension work.






      share|improve this answer
























      • With this solution, it works on simulator but not on real device. Any idea why ?

        – Djiggy
        Sep 15 '17 at 16:24











      • It's impossible to tell you why just based on this comment, but you can try debugging the javascript in Safari : make sure your phone is plugged in and Safari has developer mode enabled (preferences -> advanced). When you launch the app your phone will appear in the developer menu of Safari and you can inspect the webview and check the console there for errors. Hopefully you'll have relevant info. Check the logs in Xcode also they can contain relevant info. Make sure also that the extension is properly signed.

        – Edward Silhol
        Sep 18 '17 at 16:55













      • Thanks for you response. Unfortunaltely all seems to be ok in the app (no errors in safari/xcode logs), moreover, my custom url starts application from safari but not from gallery. I continue my investigation !

        – Djiggy
        Sep 19 '17 at 8:01






      • 1





        Ok, that's no surprise. So far we've only handled opening URLs from Safari, been too busy to work on it more. If you find out how to handle them from other apps, I'd love to hear how !

        – Edward Silhol
        Sep 20 '17 at 10:53











      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f33105698%2fcordova-sharing-browser-url-to-my-ios-app-clipper-ios-share-extension%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      5 Answers
      5






      active

      oldest

      votes








      5 Answers
      5






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      22














      Edit: sooner or later a simple mobile website will probably be able to receive content shared from native apps. Check the Web Share Target protocol



      I'm answering my own question, as we finally succeeded implementing the iOS Share Extension for a Cordova application.



      First the Share Extension system is only available for iOS >= 8



      However it is kind of painful to integrate it in a Cordova project because there's no special Cordova config to do so. When creating a Share Extension, it is hard for the Cordova team to reverse-engineer the XCode xproj file to add a share extension so it will probably be hard in the future too...



      You have 2 options:




      • Version some of your iOS platform files (like the xproj file)

      • Include a manual procedure after generating the iOS platform with cordova


      We decided to go with the 2nd option, as our extension is pretty stable and we will not modify it often.



      Create the share extension manually



      VERY IMPORTANT: create the share extension, and the Action.js THROUGH the XCode interface! They have to be registered in the xproj file or it won't work at all. See



      Create the files through XCode



      To create a share extension for a Cordova app, you will have to do like any iOS developer would do.




      • Open the ios platform xproj on XCode

      • File > New > Target > Share Extension

      • Select Swift as a language (only because ObjC seems unpleasant to me)


      You get a new folder in XCode with some files that you will have to customize.



      You will also need an extra Action.js file in that share extension folder. Create a new empty file (through XCode!) Action.js



      Handle browser data extraction



      Put in Action.js the following code:



      var Action = function() {};

      Action.prototype = {

      run: function(parameters) {
      parameters.completionFunction({"url": document.URL, "title": document.title });
      },

      finalize: function(parameters) {

      }

      };

      var ExtensionPreprocessingJS = new Action


      When your share extension is selected on top of a browser (I think it only works for Safari), this JS will be run and will permit you to retrieve the data you want on that page in your Swift controller (here I want the url and the title).



      Customize Info.plist



      Now you need to customize the Info.plist file to describe what kind of share extension you are creating, and what kind of content you can share to your app. In my case I mostly want to share urls, so here is a config that works for sharing urls from Chrome or Safari.



      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
      <key>CFBundleDevelopmentRegion</key>
      <string>en</string>
      <key>CFBundleDisplayName</key>
      <string>MyClipper</string>
      <key>CFBundleExecutable</key>
      <string>$(EXECUTABLE_NAME)</string>
      <key>CFBundleIdentifier</key>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
      <key>CFBundleInfoDictionaryVersion</key>
      <string>6.0</string>
      <key>CFBundleName</key>
      <string>$(PRODUCT_NAME)</string>
      <key>CFBundlePackageType</key>
      <string>XPC!</string>
      <key>CFBundleShortVersionString</key>
      <string>1.0</string>
      <key>CFBundleSignature</key>
      <string>????</string>
      <key>CFBundleVersion</key>
      <string>1</string>
      <key>NSExtension</key>
      <dict>
      <key>NSExtensionAttributes</key>
      <dict>
      <key>NSExtensionJavaScriptPreprocessingFile</key>
      <string>Action</string>
      <key>NSExtensionActivationRule</key>
      <dict>
      <key>NSExtensionActivationSupportsText</key>
      <true/>
      <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
      <integer>1</integer>
      </dict>
      </dict>
      <key>NSExtensionMainStoryboard</key>
      <string>MainInterface</string>
      <key>NSExtensionPointIdentifier</key>
      <string>com.apple.share-services</string>
      </dict>
      </dict>
      </plist>


      Notice that we registered the Action.js file in that plist file.



      Customize the ShareViewController.swift



      Normally you would have to implement by yourself Swift views that will be run on top of the existing app (for me on top of the browser app).



      By default, the controller will provide a default view that you can use, and you can perform requests to your backend from there. Here is an example from which I inspired myself that do so.



      But in my case, I am not an iOS developer and I want that when the user select my extension, it opens my app instead of displaying iOS views. So I used a custom URL scheme to open my app clipper: myAppScheme://openClipper?url=SomeUrl
      This permits me to design my clipper in HTML / JS instead of having to create iOS views.



      Notice that I use a hack for that, and Apple may forbid to open your app from a Share Extension in future iOS versions. However this hack works currently for iOS 8.x and 9.0.



      Here is the code. It works for both Chrome and Safari on iOS.



      //
      // ShareViewController.swift
      // MyClipper
      //
      // Created by Sébastien Lorber on 15/10/2015.
      //
      //

      import UIKit
      import Social
      import MobileCoreServices

      @available(iOSApplicationExtension 8.0, *)
      class ShareViewController: SLComposeServiceViewController {

      let contentTypeList = kUTTypePropertyList as String
      let contentTypeTitle = "public.plain-text"
      let contentTypeUrl = "public.url"

      // We don't want to show the view actually
      // as we directly open our app!
      override func viewWillAppear(animated: Bool) {
      self.view.hidden = true
      self.cancel()
      self.doClipping()
      }

      // We directly forward all the values retrieved from Action.js to our app
      private func doClipping() {
      self.loadJsExtensionValues { dict in
      let url = "myAppScheme://mobileclipper?" + self.dictionaryToQueryString(dict)
      self.doOpenUrl(url)
      }
      }

      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////

      private func dictionaryToQueryString(dict: Dictionary<String,String>) -> String {
      return dict.map({ entry in
      let value = entry.1
      let valueEncoded = value.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
      return entry.0 + "=" + valueEncoded!
      }).joinWithSeparator("&")
      }

      // See https://github.com/extendedmind/extendedmind/blob/master/frontend/cordova/app/platforms/ios/extmd-share/ShareViewController.swift
      private func loadJsExtensionValues(f: Dictionary<String,String> -> Void) {
      let content = extensionContext!.inputItems[0] as! NSExtensionItem
      if (self.hasAttachmentOfType(content, contentType: contentTypeList)) {
      self.loadJsDictionnary(content) { dict in
      f(dict)
      }
      } else {
      self.loadUTIDictionnary(content) { dict in
      // 2 Items should be in dict to launch clipper opening : url and title.
      if (dict.count==2) { f(dict) }
      }
      }
      }

      private func hasAttachmentOfType(content: NSExtensionItem,contentType: String) -> Bool {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentType) {
      return true;
      }
      }
      return false;
      }

      private func loadJsDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentTypeList) {
      attachment.loadItemForTypeIdentifier(contentTypeList, options: nil) { data, error in
      if ( error == nil && data != nil ) {
      let jsDict = data as! NSDictionary
      if let jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey] {
      let values = jsPreprocessingResults as! Dictionary<String,String>
      f(values)
      }
      }
      }
      }
      }
      }


      private func loadUTIDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      var dict = Dictionary<String, String>()
      loadUTIString(content, utiKey: contentTypeUrl , handler: { url_NSSecureCoding in
      let url_NSurl = url_NSSecureCoding as! NSURL
      let url_String = url_NSurl.absoluteString as String
      dict["url"] = url_String
      f(dict)
      })
      loadUTIString(content, utiKey: contentTypeTitle, handler: { title_NSSecureCoding in
      let title = title_NSSecureCoding as! String
      dict["title"] = title
      f(dict)
      })
      }


      private func loadUTIString(content: NSExtensionItem,utiKey: String,handler: NSSecureCoding -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(utiKey) {
      attachment.loadItemForTypeIdentifier(utiKey, options: nil, completionHandler: { (data, error) -> Void in
      if ( error == nil && data != nil ) {
      handler(data!)
      }
      })
      }
      }
      }


      // See https://stackoverflow.com/a/28037297/82609
      // Works fine for iOS 8.x and 9.0 but may not work anymore in the future :(
      private func doOpenUrl(url: String) {
      let urlNS = NSURL(string: url)!
      var responder = self as UIResponder?
      while (responder != nil){
      if responder!.respondsToSelector(Selector("openURL:")) == true{
      responder!.callSelector(Selector("openURL:"), object: urlNS, delay: 0)
      }
      responder = responder!.nextResponder()
      }
      }
      }

      // See https://stackoverflow.com/a/28037297/82609
      extension NSObject {
      func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
      let delay = delay * Double(NSEC_PER_SEC)
      let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
      dispatch_after(time, dispatch_get_main_queue(), {
      NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
      })
      }
      }


      Notice there are 2 ways to load the Dictionary<String,String>. This is because Chrome and Safari seems to provide the url and title of the page in 2 different ways.



      Automating the process



      You must create the Share Extension files and Action.js file through the XCode interface. However, once they are created (and referenced in XCode), you can replace them with your own files.



      So we decided that we will version the above files in a folder (/cordova/ios-share-extension), and override the default share extension files with them.



      This is not ideal but the minimum procedure we use is:




      • Build Cordova iOS platform (cordova prepare ios)

      • Open project in XCode

      • Create share extension with (product name="MyClipper", language="Swift", organization name="MyCompany")

      • On the "MyClipper", create an empty file "Action.js"

      • Copy the content of /cordova/ios-share-extension to cordova/platforms/ios/MyClipper


      This way the extension is correctly registered in the xproj file but you still have the ability to version control your extension.



      Edit 2017: this may become easier to setup all that with cordova-ios@5.0.0, see https://issues.apache.org/jira/browse/CB-10218






      share|improve this answer


























      • Thanks! Very helpful!

        – Flock Dawson
        Dec 16 '15 at 7:53






      • 1





        Thank you very much, this was the only post on SO with anything useful related to original question. I required handling URL+text+image in various combinations and created a derivation of this answer to do so. Just to pay it forward, github.com/inshikos/cordova-ios-share-extension/blob/master/… might be of help to others needing a solution for content sharing to a Cordova app in the absence of a proper iOS share extension plugin.

        – Warren Kim
        Jan 20 '16 at 8:34











      • thanks @WarrenKim your code could be helpful to me in the future too :)

        – Sebastien Lorber
        Jan 20 '16 at 9:23











      • Hey @SebastienLorber ! Your post seems really helpful :) Thanks! One thing I'd like to ask: I did everything like you described, but still can't see an icon of my app in sharing screen. Could you please point me where it may stuck ?

        – Stanislav Iegorov
        Apr 8 '16 at 14:10











      • @StanislavIegorov I have no idea. Maybe you did not set target >= 7.0 in XCode, or missed a step

        – Sebastien Lorber
        Apr 8 '16 at 14:20
















      22














      Edit: sooner or later a simple mobile website will probably be able to receive content shared from native apps. Check the Web Share Target protocol



      I'm answering my own question, as we finally succeeded implementing the iOS Share Extension for a Cordova application.



      First the Share Extension system is only available for iOS >= 8



      However it is kind of painful to integrate it in a Cordova project because there's no special Cordova config to do so. When creating a Share Extension, it is hard for the Cordova team to reverse-engineer the XCode xproj file to add a share extension so it will probably be hard in the future too...



      You have 2 options:




      • Version some of your iOS platform files (like the xproj file)

      • Include a manual procedure after generating the iOS platform with cordova


      We decided to go with the 2nd option, as our extension is pretty stable and we will not modify it often.



      Create the share extension manually



      VERY IMPORTANT: create the share extension, and the Action.js THROUGH the XCode interface! They have to be registered in the xproj file or it won't work at all. See



      Create the files through XCode



      To create a share extension for a Cordova app, you will have to do like any iOS developer would do.




      • Open the ios platform xproj on XCode

      • File > New > Target > Share Extension

      • Select Swift as a language (only because ObjC seems unpleasant to me)


      You get a new folder in XCode with some files that you will have to customize.



      You will also need an extra Action.js file in that share extension folder. Create a new empty file (through XCode!) Action.js



      Handle browser data extraction



      Put in Action.js the following code:



      var Action = function() {};

      Action.prototype = {

      run: function(parameters) {
      parameters.completionFunction({"url": document.URL, "title": document.title });
      },

      finalize: function(parameters) {

      }

      };

      var ExtensionPreprocessingJS = new Action


      When your share extension is selected on top of a browser (I think it only works for Safari), this JS will be run and will permit you to retrieve the data you want on that page in your Swift controller (here I want the url and the title).



      Customize Info.plist



      Now you need to customize the Info.plist file to describe what kind of share extension you are creating, and what kind of content you can share to your app. In my case I mostly want to share urls, so here is a config that works for sharing urls from Chrome or Safari.



      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
      <key>CFBundleDevelopmentRegion</key>
      <string>en</string>
      <key>CFBundleDisplayName</key>
      <string>MyClipper</string>
      <key>CFBundleExecutable</key>
      <string>$(EXECUTABLE_NAME)</string>
      <key>CFBundleIdentifier</key>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
      <key>CFBundleInfoDictionaryVersion</key>
      <string>6.0</string>
      <key>CFBundleName</key>
      <string>$(PRODUCT_NAME)</string>
      <key>CFBundlePackageType</key>
      <string>XPC!</string>
      <key>CFBundleShortVersionString</key>
      <string>1.0</string>
      <key>CFBundleSignature</key>
      <string>????</string>
      <key>CFBundleVersion</key>
      <string>1</string>
      <key>NSExtension</key>
      <dict>
      <key>NSExtensionAttributes</key>
      <dict>
      <key>NSExtensionJavaScriptPreprocessingFile</key>
      <string>Action</string>
      <key>NSExtensionActivationRule</key>
      <dict>
      <key>NSExtensionActivationSupportsText</key>
      <true/>
      <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
      <integer>1</integer>
      </dict>
      </dict>
      <key>NSExtensionMainStoryboard</key>
      <string>MainInterface</string>
      <key>NSExtensionPointIdentifier</key>
      <string>com.apple.share-services</string>
      </dict>
      </dict>
      </plist>


      Notice that we registered the Action.js file in that plist file.



      Customize the ShareViewController.swift



      Normally you would have to implement by yourself Swift views that will be run on top of the existing app (for me on top of the browser app).



      By default, the controller will provide a default view that you can use, and you can perform requests to your backend from there. Here is an example from which I inspired myself that do so.



      But in my case, I am not an iOS developer and I want that when the user select my extension, it opens my app instead of displaying iOS views. So I used a custom URL scheme to open my app clipper: myAppScheme://openClipper?url=SomeUrl
      This permits me to design my clipper in HTML / JS instead of having to create iOS views.



      Notice that I use a hack for that, and Apple may forbid to open your app from a Share Extension in future iOS versions. However this hack works currently for iOS 8.x and 9.0.



      Here is the code. It works for both Chrome and Safari on iOS.



      //
      // ShareViewController.swift
      // MyClipper
      //
      // Created by Sébastien Lorber on 15/10/2015.
      //
      //

      import UIKit
      import Social
      import MobileCoreServices

      @available(iOSApplicationExtension 8.0, *)
      class ShareViewController: SLComposeServiceViewController {

      let contentTypeList = kUTTypePropertyList as String
      let contentTypeTitle = "public.plain-text"
      let contentTypeUrl = "public.url"

      // We don't want to show the view actually
      // as we directly open our app!
      override func viewWillAppear(animated: Bool) {
      self.view.hidden = true
      self.cancel()
      self.doClipping()
      }

      // We directly forward all the values retrieved from Action.js to our app
      private func doClipping() {
      self.loadJsExtensionValues { dict in
      let url = "myAppScheme://mobileclipper?" + self.dictionaryToQueryString(dict)
      self.doOpenUrl(url)
      }
      }

      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////

      private func dictionaryToQueryString(dict: Dictionary<String,String>) -> String {
      return dict.map({ entry in
      let value = entry.1
      let valueEncoded = value.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
      return entry.0 + "=" + valueEncoded!
      }).joinWithSeparator("&")
      }

      // See https://github.com/extendedmind/extendedmind/blob/master/frontend/cordova/app/platforms/ios/extmd-share/ShareViewController.swift
      private func loadJsExtensionValues(f: Dictionary<String,String> -> Void) {
      let content = extensionContext!.inputItems[0] as! NSExtensionItem
      if (self.hasAttachmentOfType(content, contentType: contentTypeList)) {
      self.loadJsDictionnary(content) { dict in
      f(dict)
      }
      } else {
      self.loadUTIDictionnary(content) { dict in
      // 2 Items should be in dict to launch clipper opening : url and title.
      if (dict.count==2) { f(dict) }
      }
      }
      }

      private func hasAttachmentOfType(content: NSExtensionItem,contentType: String) -> Bool {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentType) {
      return true;
      }
      }
      return false;
      }

      private func loadJsDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentTypeList) {
      attachment.loadItemForTypeIdentifier(contentTypeList, options: nil) { data, error in
      if ( error == nil && data != nil ) {
      let jsDict = data as! NSDictionary
      if let jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey] {
      let values = jsPreprocessingResults as! Dictionary<String,String>
      f(values)
      }
      }
      }
      }
      }
      }


      private func loadUTIDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      var dict = Dictionary<String, String>()
      loadUTIString(content, utiKey: contentTypeUrl , handler: { url_NSSecureCoding in
      let url_NSurl = url_NSSecureCoding as! NSURL
      let url_String = url_NSurl.absoluteString as String
      dict["url"] = url_String
      f(dict)
      })
      loadUTIString(content, utiKey: contentTypeTitle, handler: { title_NSSecureCoding in
      let title = title_NSSecureCoding as! String
      dict["title"] = title
      f(dict)
      })
      }


      private func loadUTIString(content: NSExtensionItem,utiKey: String,handler: NSSecureCoding -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(utiKey) {
      attachment.loadItemForTypeIdentifier(utiKey, options: nil, completionHandler: { (data, error) -> Void in
      if ( error == nil && data != nil ) {
      handler(data!)
      }
      })
      }
      }
      }


      // See https://stackoverflow.com/a/28037297/82609
      // Works fine for iOS 8.x and 9.0 but may not work anymore in the future :(
      private func doOpenUrl(url: String) {
      let urlNS = NSURL(string: url)!
      var responder = self as UIResponder?
      while (responder != nil){
      if responder!.respondsToSelector(Selector("openURL:")) == true{
      responder!.callSelector(Selector("openURL:"), object: urlNS, delay: 0)
      }
      responder = responder!.nextResponder()
      }
      }
      }

      // See https://stackoverflow.com/a/28037297/82609
      extension NSObject {
      func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
      let delay = delay * Double(NSEC_PER_SEC)
      let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
      dispatch_after(time, dispatch_get_main_queue(), {
      NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
      })
      }
      }


      Notice there are 2 ways to load the Dictionary<String,String>. This is because Chrome and Safari seems to provide the url and title of the page in 2 different ways.



      Automating the process



      You must create the Share Extension files and Action.js file through the XCode interface. However, once they are created (and referenced in XCode), you can replace them with your own files.



      So we decided that we will version the above files in a folder (/cordova/ios-share-extension), and override the default share extension files with them.



      This is not ideal but the minimum procedure we use is:




      • Build Cordova iOS platform (cordova prepare ios)

      • Open project in XCode

      • Create share extension with (product name="MyClipper", language="Swift", organization name="MyCompany")

      • On the "MyClipper", create an empty file "Action.js"

      • Copy the content of /cordova/ios-share-extension to cordova/platforms/ios/MyClipper


      This way the extension is correctly registered in the xproj file but you still have the ability to version control your extension.



      Edit 2017: this may become easier to setup all that with cordova-ios@5.0.0, see https://issues.apache.org/jira/browse/CB-10218






      share|improve this answer


























      • Thanks! Very helpful!

        – Flock Dawson
        Dec 16 '15 at 7:53






      • 1





        Thank you very much, this was the only post on SO with anything useful related to original question. I required handling URL+text+image in various combinations and created a derivation of this answer to do so. Just to pay it forward, github.com/inshikos/cordova-ios-share-extension/blob/master/… might be of help to others needing a solution for content sharing to a Cordova app in the absence of a proper iOS share extension plugin.

        – Warren Kim
        Jan 20 '16 at 8:34











      • thanks @WarrenKim your code could be helpful to me in the future too :)

        – Sebastien Lorber
        Jan 20 '16 at 9:23











      • Hey @SebastienLorber ! Your post seems really helpful :) Thanks! One thing I'd like to ask: I did everything like you described, but still can't see an icon of my app in sharing screen. Could you please point me where it may stuck ?

        – Stanislav Iegorov
        Apr 8 '16 at 14:10











      • @StanislavIegorov I have no idea. Maybe you did not set target >= 7.0 in XCode, or missed a step

        – Sebastien Lorber
        Apr 8 '16 at 14:20














      22












      22








      22







      Edit: sooner or later a simple mobile website will probably be able to receive content shared from native apps. Check the Web Share Target protocol



      I'm answering my own question, as we finally succeeded implementing the iOS Share Extension for a Cordova application.



      First the Share Extension system is only available for iOS >= 8



      However it is kind of painful to integrate it in a Cordova project because there's no special Cordova config to do so. When creating a Share Extension, it is hard for the Cordova team to reverse-engineer the XCode xproj file to add a share extension so it will probably be hard in the future too...



      You have 2 options:




      • Version some of your iOS platform files (like the xproj file)

      • Include a manual procedure after generating the iOS platform with cordova


      We decided to go with the 2nd option, as our extension is pretty stable and we will not modify it often.



      Create the share extension manually



      VERY IMPORTANT: create the share extension, and the Action.js THROUGH the XCode interface! They have to be registered in the xproj file or it won't work at all. See



      Create the files through XCode



      To create a share extension for a Cordova app, you will have to do like any iOS developer would do.




      • Open the ios platform xproj on XCode

      • File > New > Target > Share Extension

      • Select Swift as a language (only because ObjC seems unpleasant to me)


      You get a new folder in XCode with some files that you will have to customize.



      You will also need an extra Action.js file in that share extension folder. Create a new empty file (through XCode!) Action.js



      Handle browser data extraction



      Put in Action.js the following code:



      var Action = function() {};

      Action.prototype = {

      run: function(parameters) {
      parameters.completionFunction({"url": document.URL, "title": document.title });
      },

      finalize: function(parameters) {

      }

      };

      var ExtensionPreprocessingJS = new Action


      When your share extension is selected on top of a browser (I think it only works for Safari), this JS will be run and will permit you to retrieve the data you want on that page in your Swift controller (here I want the url and the title).



      Customize Info.plist



      Now you need to customize the Info.plist file to describe what kind of share extension you are creating, and what kind of content you can share to your app. In my case I mostly want to share urls, so here is a config that works for sharing urls from Chrome or Safari.



      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
      <key>CFBundleDevelopmentRegion</key>
      <string>en</string>
      <key>CFBundleDisplayName</key>
      <string>MyClipper</string>
      <key>CFBundleExecutable</key>
      <string>$(EXECUTABLE_NAME)</string>
      <key>CFBundleIdentifier</key>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
      <key>CFBundleInfoDictionaryVersion</key>
      <string>6.0</string>
      <key>CFBundleName</key>
      <string>$(PRODUCT_NAME)</string>
      <key>CFBundlePackageType</key>
      <string>XPC!</string>
      <key>CFBundleShortVersionString</key>
      <string>1.0</string>
      <key>CFBundleSignature</key>
      <string>????</string>
      <key>CFBundleVersion</key>
      <string>1</string>
      <key>NSExtension</key>
      <dict>
      <key>NSExtensionAttributes</key>
      <dict>
      <key>NSExtensionJavaScriptPreprocessingFile</key>
      <string>Action</string>
      <key>NSExtensionActivationRule</key>
      <dict>
      <key>NSExtensionActivationSupportsText</key>
      <true/>
      <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
      <integer>1</integer>
      </dict>
      </dict>
      <key>NSExtensionMainStoryboard</key>
      <string>MainInterface</string>
      <key>NSExtensionPointIdentifier</key>
      <string>com.apple.share-services</string>
      </dict>
      </dict>
      </plist>


      Notice that we registered the Action.js file in that plist file.



      Customize the ShareViewController.swift



      Normally you would have to implement by yourself Swift views that will be run on top of the existing app (for me on top of the browser app).



      By default, the controller will provide a default view that you can use, and you can perform requests to your backend from there. Here is an example from which I inspired myself that do so.



      But in my case, I am not an iOS developer and I want that when the user select my extension, it opens my app instead of displaying iOS views. So I used a custom URL scheme to open my app clipper: myAppScheme://openClipper?url=SomeUrl
      This permits me to design my clipper in HTML / JS instead of having to create iOS views.



      Notice that I use a hack for that, and Apple may forbid to open your app from a Share Extension in future iOS versions. However this hack works currently for iOS 8.x and 9.0.



      Here is the code. It works for both Chrome and Safari on iOS.



      //
      // ShareViewController.swift
      // MyClipper
      //
      // Created by Sébastien Lorber on 15/10/2015.
      //
      //

      import UIKit
      import Social
      import MobileCoreServices

      @available(iOSApplicationExtension 8.0, *)
      class ShareViewController: SLComposeServiceViewController {

      let contentTypeList = kUTTypePropertyList as String
      let contentTypeTitle = "public.plain-text"
      let contentTypeUrl = "public.url"

      // We don't want to show the view actually
      // as we directly open our app!
      override func viewWillAppear(animated: Bool) {
      self.view.hidden = true
      self.cancel()
      self.doClipping()
      }

      // We directly forward all the values retrieved from Action.js to our app
      private func doClipping() {
      self.loadJsExtensionValues { dict in
      let url = "myAppScheme://mobileclipper?" + self.dictionaryToQueryString(dict)
      self.doOpenUrl(url)
      }
      }

      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////

      private func dictionaryToQueryString(dict: Dictionary<String,String>) -> String {
      return dict.map({ entry in
      let value = entry.1
      let valueEncoded = value.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
      return entry.0 + "=" + valueEncoded!
      }).joinWithSeparator("&")
      }

      // See https://github.com/extendedmind/extendedmind/blob/master/frontend/cordova/app/platforms/ios/extmd-share/ShareViewController.swift
      private func loadJsExtensionValues(f: Dictionary<String,String> -> Void) {
      let content = extensionContext!.inputItems[0] as! NSExtensionItem
      if (self.hasAttachmentOfType(content, contentType: contentTypeList)) {
      self.loadJsDictionnary(content) { dict in
      f(dict)
      }
      } else {
      self.loadUTIDictionnary(content) { dict in
      // 2 Items should be in dict to launch clipper opening : url and title.
      if (dict.count==2) { f(dict) }
      }
      }
      }

      private func hasAttachmentOfType(content: NSExtensionItem,contentType: String) -> Bool {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentType) {
      return true;
      }
      }
      return false;
      }

      private func loadJsDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentTypeList) {
      attachment.loadItemForTypeIdentifier(contentTypeList, options: nil) { data, error in
      if ( error == nil && data != nil ) {
      let jsDict = data as! NSDictionary
      if let jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey] {
      let values = jsPreprocessingResults as! Dictionary<String,String>
      f(values)
      }
      }
      }
      }
      }
      }


      private func loadUTIDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      var dict = Dictionary<String, String>()
      loadUTIString(content, utiKey: contentTypeUrl , handler: { url_NSSecureCoding in
      let url_NSurl = url_NSSecureCoding as! NSURL
      let url_String = url_NSurl.absoluteString as String
      dict["url"] = url_String
      f(dict)
      })
      loadUTIString(content, utiKey: contentTypeTitle, handler: { title_NSSecureCoding in
      let title = title_NSSecureCoding as! String
      dict["title"] = title
      f(dict)
      })
      }


      private func loadUTIString(content: NSExtensionItem,utiKey: String,handler: NSSecureCoding -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(utiKey) {
      attachment.loadItemForTypeIdentifier(utiKey, options: nil, completionHandler: { (data, error) -> Void in
      if ( error == nil && data != nil ) {
      handler(data!)
      }
      })
      }
      }
      }


      // See https://stackoverflow.com/a/28037297/82609
      // Works fine for iOS 8.x and 9.0 but may not work anymore in the future :(
      private func doOpenUrl(url: String) {
      let urlNS = NSURL(string: url)!
      var responder = self as UIResponder?
      while (responder != nil){
      if responder!.respondsToSelector(Selector("openURL:")) == true{
      responder!.callSelector(Selector("openURL:"), object: urlNS, delay: 0)
      }
      responder = responder!.nextResponder()
      }
      }
      }

      // See https://stackoverflow.com/a/28037297/82609
      extension NSObject {
      func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
      let delay = delay * Double(NSEC_PER_SEC)
      let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
      dispatch_after(time, dispatch_get_main_queue(), {
      NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
      })
      }
      }


      Notice there are 2 ways to load the Dictionary<String,String>. This is because Chrome and Safari seems to provide the url and title of the page in 2 different ways.



      Automating the process



      You must create the Share Extension files and Action.js file through the XCode interface. However, once they are created (and referenced in XCode), you can replace them with your own files.



      So we decided that we will version the above files in a folder (/cordova/ios-share-extension), and override the default share extension files with them.



      This is not ideal but the minimum procedure we use is:




      • Build Cordova iOS platform (cordova prepare ios)

      • Open project in XCode

      • Create share extension with (product name="MyClipper", language="Swift", organization name="MyCompany")

      • On the "MyClipper", create an empty file "Action.js"

      • Copy the content of /cordova/ios-share-extension to cordova/platforms/ios/MyClipper


      This way the extension is correctly registered in the xproj file but you still have the ability to version control your extension.



      Edit 2017: this may become easier to setup all that with cordova-ios@5.0.0, see https://issues.apache.org/jira/browse/CB-10218






      share|improve this answer















      Edit: sooner or later a simple mobile website will probably be able to receive content shared from native apps. Check the Web Share Target protocol



      I'm answering my own question, as we finally succeeded implementing the iOS Share Extension for a Cordova application.



      First the Share Extension system is only available for iOS >= 8



      However it is kind of painful to integrate it in a Cordova project because there's no special Cordova config to do so. When creating a Share Extension, it is hard for the Cordova team to reverse-engineer the XCode xproj file to add a share extension so it will probably be hard in the future too...



      You have 2 options:




      • Version some of your iOS platform files (like the xproj file)

      • Include a manual procedure after generating the iOS platform with cordova


      We decided to go with the 2nd option, as our extension is pretty stable and we will not modify it often.



      Create the share extension manually



      VERY IMPORTANT: create the share extension, and the Action.js THROUGH the XCode interface! They have to be registered in the xproj file or it won't work at all. See



      Create the files through XCode



      To create a share extension for a Cordova app, you will have to do like any iOS developer would do.




      • Open the ios platform xproj on XCode

      • File > New > Target > Share Extension

      • Select Swift as a language (only because ObjC seems unpleasant to me)


      You get a new folder in XCode with some files that you will have to customize.



      You will also need an extra Action.js file in that share extension folder. Create a new empty file (through XCode!) Action.js



      Handle browser data extraction



      Put in Action.js the following code:



      var Action = function() {};

      Action.prototype = {

      run: function(parameters) {
      parameters.completionFunction({"url": document.URL, "title": document.title });
      },

      finalize: function(parameters) {

      }

      };

      var ExtensionPreprocessingJS = new Action


      When your share extension is selected on top of a browser (I think it only works for Safari), this JS will be run and will permit you to retrieve the data you want on that page in your Swift controller (here I want the url and the title).



      Customize Info.plist



      Now you need to customize the Info.plist file to describe what kind of share extension you are creating, and what kind of content you can share to your app. In my case I mostly want to share urls, so here is a config that works for sharing urls from Chrome or Safari.



      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
      <key>CFBundleDevelopmentRegion</key>
      <string>en</string>
      <key>CFBundleDisplayName</key>
      <string>MyClipper</string>
      <key>CFBundleExecutable</key>
      <string>$(EXECUTABLE_NAME)</string>
      <key>CFBundleIdentifier</key>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
      <key>CFBundleInfoDictionaryVersion</key>
      <string>6.0</string>
      <key>CFBundleName</key>
      <string>$(PRODUCT_NAME)</string>
      <key>CFBundlePackageType</key>
      <string>XPC!</string>
      <key>CFBundleShortVersionString</key>
      <string>1.0</string>
      <key>CFBundleSignature</key>
      <string>????</string>
      <key>CFBundleVersion</key>
      <string>1</string>
      <key>NSExtension</key>
      <dict>
      <key>NSExtensionAttributes</key>
      <dict>
      <key>NSExtensionJavaScriptPreprocessingFile</key>
      <string>Action</string>
      <key>NSExtensionActivationRule</key>
      <dict>
      <key>NSExtensionActivationSupportsText</key>
      <true/>
      <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
      <integer>1</integer>
      </dict>
      </dict>
      <key>NSExtensionMainStoryboard</key>
      <string>MainInterface</string>
      <key>NSExtensionPointIdentifier</key>
      <string>com.apple.share-services</string>
      </dict>
      </dict>
      </plist>


      Notice that we registered the Action.js file in that plist file.



      Customize the ShareViewController.swift



      Normally you would have to implement by yourself Swift views that will be run on top of the existing app (for me on top of the browser app).



      By default, the controller will provide a default view that you can use, and you can perform requests to your backend from there. Here is an example from which I inspired myself that do so.



      But in my case, I am not an iOS developer and I want that when the user select my extension, it opens my app instead of displaying iOS views. So I used a custom URL scheme to open my app clipper: myAppScheme://openClipper?url=SomeUrl
      This permits me to design my clipper in HTML / JS instead of having to create iOS views.



      Notice that I use a hack for that, and Apple may forbid to open your app from a Share Extension in future iOS versions. However this hack works currently for iOS 8.x and 9.0.



      Here is the code. It works for both Chrome and Safari on iOS.



      //
      // ShareViewController.swift
      // MyClipper
      //
      // Created by Sébastien Lorber on 15/10/2015.
      //
      //

      import UIKit
      import Social
      import MobileCoreServices

      @available(iOSApplicationExtension 8.0, *)
      class ShareViewController: SLComposeServiceViewController {

      let contentTypeList = kUTTypePropertyList as String
      let contentTypeTitle = "public.plain-text"
      let contentTypeUrl = "public.url"

      // We don't want to show the view actually
      // as we directly open our app!
      override func viewWillAppear(animated: Bool) {
      self.view.hidden = true
      self.cancel()
      self.doClipping()
      }

      // We directly forward all the values retrieved from Action.js to our app
      private func doClipping() {
      self.loadJsExtensionValues { dict in
      let url = "myAppScheme://mobileclipper?" + self.dictionaryToQueryString(dict)
      self.doOpenUrl(url)
      }
      }

      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////////////////////////////////////////////////////////////////

      private func dictionaryToQueryString(dict: Dictionary<String,String>) -> String {
      return dict.map({ entry in
      let value = entry.1
      let valueEncoded = value.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
      return entry.0 + "=" + valueEncoded!
      }).joinWithSeparator("&")
      }

      // See https://github.com/extendedmind/extendedmind/blob/master/frontend/cordova/app/platforms/ios/extmd-share/ShareViewController.swift
      private func loadJsExtensionValues(f: Dictionary<String,String> -> Void) {
      let content = extensionContext!.inputItems[0] as! NSExtensionItem
      if (self.hasAttachmentOfType(content, contentType: contentTypeList)) {
      self.loadJsDictionnary(content) { dict in
      f(dict)
      }
      } else {
      self.loadUTIDictionnary(content) { dict in
      // 2 Items should be in dict to launch clipper opening : url and title.
      if (dict.count==2) { f(dict) }
      }
      }
      }

      private func hasAttachmentOfType(content: NSExtensionItem,contentType: String) -> Bool {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentType) {
      return true;
      }
      }
      return false;
      }

      private func loadJsDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(contentTypeList) {
      attachment.loadItemForTypeIdentifier(contentTypeList, options: nil) { data, error in
      if ( error == nil && data != nil ) {
      let jsDict = data as! NSDictionary
      if let jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey] {
      let values = jsPreprocessingResults as! Dictionary<String,String>
      f(values)
      }
      }
      }
      }
      }
      }


      private func loadUTIDictionnary(content: NSExtensionItem,f: Dictionary<String,String> -> Void) {
      var dict = Dictionary<String, String>()
      loadUTIString(content, utiKey: contentTypeUrl , handler: { url_NSSecureCoding in
      let url_NSurl = url_NSSecureCoding as! NSURL
      let url_String = url_NSurl.absoluteString as String
      dict["url"] = url_String
      f(dict)
      })
      loadUTIString(content, utiKey: contentTypeTitle, handler: { title_NSSecureCoding in
      let title = title_NSSecureCoding as! String
      dict["title"] = title
      f(dict)
      })
      }


      private func loadUTIString(content: NSExtensionItem,utiKey: String,handler: NSSecureCoding -> Void) {
      for attachment in content.attachments as! [NSItemProvider] {
      if attachment.hasItemConformingToTypeIdentifier(utiKey) {
      attachment.loadItemForTypeIdentifier(utiKey, options: nil, completionHandler: { (data, error) -> Void in
      if ( error == nil && data != nil ) {
      handler(data!)
      }
      })
      }
      }
      }


      // See https://stackoverflow.com/a/28037297/82609
      // Works fine for iOS 8.x and 9.0 but may not work anymore in the future :(
      private func doOpenUrl(url: String) {
      let urlNS = NSURL(string: url)!
      var responder = self as UIResponder?
      while (responder != nil){
      if responder!.respondsToSelector(Selector("openURL:")) == true{
      responder!.callSelector(Selector("openURL:"), object: urlNS, delay: 0)
      }
      responder = responder!.nextResponder()
      }
      }
      }

      // See https://stackoverflow.com/a/28037297/82609
      extension NSObject {
      func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
      let delay = delay * Double(NSEC_PER_SEC)
      let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
      dispatch_after(time, dispatch_get_main_queue(), {
      NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
      })
      }
      }


      Notice there are 2 ways to load the Dictionary<String,String>. This is because Chrome and Safari seems to provide the url and title of the page in 2 different ways.



      Automating the process



      You must create the Share Extension files and Action.js file through the XCode interface. However, once they are created (and referenced in XCode), you can replace them with your own files.



      So we decided that we will version the above files in a folder (/cordova/ios-share-extension), and override the default share extension files with them.



      This is not ideal but the minimum procedure we use is:




      • Build Cordova iOS platform (cordova prepare ios)

      • Open project in XCode

      • Create share extension with (product name="MyClipper", language="Swift", organization name="MyCompany")

      • On the "MyClipper", create an empty file "Action.js"

      • Copy the content of /cordova/ios-share-extension to cordova/platforms/ios/MyClipper


      This way the extension is correctly registered in the xproj file but you still have the ability to version control your extension.



      Edit 2017: this may become easier to setup all that with cordova-ios@5.0.0, see https://issues.apache.org/jira/browse/CB-10218







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Jan 7 at 20:48









      cnmuc

      5,29021724




      5,29021724










      answered Dec 3 '15 at 11:30









      Sebastien LorberSebastien Lorber

      52.3k37206333




      52.3k37206333













      • Thanks! Very helpful!

        – Flock Dawson
        Dec 16 '15 at 7:53






      • 1





        Thank you very much, this was the only post on SO with anything useful related to original question. I required handling URL+text+image in various combinations and created a derivation of this answer to do so. Just to pay it forward, github.com/inshikos/cordova-ios-share-extension/blob/master/… might be of help to others needing a solution for content sharing to a Cordova app in the absence of a proper iOS share extension plugin.

        – Warren Kim
        Jan 20 '16 at 8:34











      • thanks @WarrenKim your code could be helpful to me in the future too :)

        – Sebastien Lorber
        Jan 20 '16 at 9:23











      • Hey @SebastienLorber ! Your post seems really helpful :) Thanks! One thing I'd like to ask: I did everything like you described, but still can't see an icon of my app in sharing screen. Could you please point me where it may stuck ?

        – Stanislav Iegorov
        Apr 8 '16 at 14:10











      • @StanislavIegorov I have no idea. Maybe you did not set target >= 7.0 in XCode, or missed a step

        – Sebastien Lorber
        Apr 8 '16 at 14:20



















      • Thanks! Very helpful!

        – Flock Dawson
        Dec 16 '15 at 7:53






      • 1





        Thank you very much, this was the only post on SO with anything useful related to original question. I required handling URL+text+image in various combinations and created a derivation of this answer to do so. Just to pay it forward, github.com/inshikos/cordova-ios-share-extension/blob/master/… might be of help to others needing a solution for content sharing to a Cordova app in the absence of a proper iOS share extension plugin.

        – Warren Kim
        Jan 20 '16 at 8:34











      • thanks @WarrenKim your code could be helpful to me in the future too :)

        – Sebastien Lorber
        Jan 20 '16 at 9:23











      • Hey @SebastienLorber ! Your post seems really helpful :) Thanks! One thing I'd like to ask: I did everything like you described, but still can't see an icon of my app in sharing screen. Could you please point me where it may stuck ?

        – Stanislav Iegorov
        Apr 8 '16 at 14:10











      • @StanislavIegorov I have no idea. Maybe you did not set target >= 7.0 in XCode, or missed a step

        – Sebastien Lorber
        Apr 8 '16 at 14:20

















      Thanks! Very helpful!

      – Flock Dawson
      Dec 16 '15 at 7:53





      Thanks! Very helpful!

      – Flock Dawson
      Dec 16 '15 at 7:53




      1




      1





      Thank you very much, this was the only post on SO with anything useful related to original question. I required handling URL+text+image in various combinations and created a derivation of this answer to do so. Just to pay it forward, github.com/inshikos/cordova-ios-share-extension/blob/master/… might be of help to others needing a solution for content sharing to a Cordova app in the absence of a proper iOS share extension plugin.

      – Warren Kim
      Jan 20 '16 at 8:34





      Thank you very much, this was the only post on SO with anything useful related to original question. I required handling URL+text+image in various combinations and created a derivation of this answer to do so. Just to pay it forward, github.com/inshikos/cordova-ios-share-extension/blob/master/… might be of help to others needing a solution for content sharing to a Cordova app in the absence of a proper iOS share extension plugin.

      – Warren Kim
      Jan 20 '16 at 8:34













      thanks @WarrenKim your code could be helpful to me in the future too :)

      – Sebastien Lorber
      Jan 20 '16 at 9:23





      thanks @WarrenKim your code could be helpful to me in the future too :)

      – Sebastien Lorber
      Jan 20 '16 at 9:23













      Hey @SebastienLorber ! Your post seems really helpful :) Thanks! One thing I'd like to ask: I did everything like you described, but still can't see an icon of my app in sharing screen. Could you please point me where it may stuck ?

      – Stanislav Iegorov
      Apr 8 '16 at 14:10





      Hey @SebastienLorber ! Your post seems really helpful :) Thanks! One thing I'd like to ask: I did everything like you described, but still can't see an icon of my app in sharing screen. Could you please point me where it may stuck ?

      – Stanislav Iegorov
      Apr 8 '16 at 14:10













      @StanislavIegorov I have no idea. Maybe you did not set target >= 7.0 in XCode, or missed a step

      – Sebastien Lorber
      Apr 8 '16 at 14:20





      @StanislavIegorov I have no idea. Maybe you did not set target >= 7.0 in XCode, or missed a step

      – Sebastien Lorber
      Apr 8 '16 at 14:20













      3














      doOpenUrl() above needs to be updated to work on iOS 10. The following code also works on older versions of iOS.



      private func doOpenUrl(url: String) {

      let url = NSURL(string:url)
      let context = NSExtensionContext()
      context.open(url! as URL, completionHandler: nil)

      var responder = self as UIResponder?

      while (responder != nil){
      if responder?.responds(to: Selector("openURL:")) == true{
      responder?.perform(Selector("openURL:"), with: url)
      }
      responder = responder!.next
      }
      }





      share|improve this answer
























      • how to do this "doOpenUrl() above needs to be updated to work on iOS 10."? after paste those codes, i've got many red dot.

        – crapthings
        Mar 2 '17 at 3:53
















      3














      doOpenUrl() above needs to be updated to work on iOS 10. The following code also works on older versions of iOS.



      private func doOpenUrl(url: String) {

      let url = NSURL(string:url)
      let context = NSExtensionContext()
      context.open(url! as URL, completionHandler: nil)

      var responder = self as UIResponder?

      while (responder != nil){
      if responder?.responds(to: Selector("openURL:")) == true{
      responder?.perform(Selector("openURL:"), with: url)
      }
      responder = responder!.next
      }
      }





      share|improve this answer
























      • how to do this "doOpenUrl() above needs to be updated to work on iOS 10."? after paste those codes, i've got many red dot.

        – crapthings
        Mar 2 '17 at 3:53














      3












      3








      3







      doOpenUrl() above needs to be updated to work on iOS 10. The following code also works on older versions of iOS.



      private func doOpenUrl(url: String) {

      let url = NSURL(string:url)
      let context = NSExtensionContext()
      context.open(url! as URL, completionHandler: nil)

      var responder = self as UIResponder?

      while (responder != nil){
      if responder?.responds(to: Selector("openURL:")) == true{
      responder?.perform(Selector("openURL:"), with: url)
      }
      responder = responder!.next
      }
      }





      share|improve this answer













      doOpenUrl() above needs to be updated to work on iOS 10. The following code also works on older versions of iOS.



      private func doOpenUrl(url: String) {

      let url = NSURL(string:url)
      let context = NSExtensionContext()
      context.open(url! as URL, completionHandler: nil)

      var responder = self as UIResponder?

      while (responder != nil){
      if responder?.responds(to: Selector("openURL:")) == true{
      responder?.perform(Selector("openURL:"), with: url)
      }
      responder = responder!.next
      }
      }






      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Nov 6 '16 at 4:56









      Aaron RosenAaron Rosen

      613




      613













      • how to do this "doOpenUrl() above needs to be updated to work on iOS 10."? after paste those codes, i've got many red dot.

        – crapthings
        Mar 2 '17 at 3:53



















      • how to do this "doOpenUrl() above needs to be updated to work on iOS 10."? after paste those codes, i've got many red dot.

        – crapthings
        Mar 2 '17 at 3:53

















      how to do this "doOpenUrl() above needs to be updated to work on iOS 10."? after paste those codes, i've got many red dot.

      – crapthings
      Mar 2 '17 at 3:53





      how to do this "doOpenUrl() above needs to be updated to work on iOS 10."? after paste those codes, i've got many red dot.

      – crapthings
      Mar 2 '17 at 3:53











      2














      You should be able to achieve your goal with far less manual work using this cordova plugin. It'll also work on Android.






      share|improve this answer




























        2














        You should be able to achieve your goal with far less manual work using this cordova plugin. It'll also work on Android.






        share|improve this answer


























          2












          2








          2







          You should be able to achieve your goal with far less manual work using this cordova plugin. It'll also work on Android.






          share|improve this answer













          You should be able to achieve your goal with far less manual work using this cordova plugin. It'll also work on Android.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Oct 8 '17 at 19:26









          jekojeko

          23724




          23724























              1














              That's a good and still relevant question.



              I tried to make use of the awesome cordova-plugin-openwith by Jean-Christophe Hoelt but faced several issues. The plugin is meant to receive share items of one type (say, URL, text or image), which is configured during installation. Also, with its current implementation, writing a note to share and selecting a receiver in a Cordova app are two different steps in different (native and Cordova) context, so it didn't look as a good user experience to me.



              I made these and other corrections to this plugin and published it as a separate plugin:
              https://github.com/EternallLight/cordova-plugin-openwith-ios



              Note that it works only for iOS, not for Android.






              share|improve this answer




























                1














                That's a good and still relevant question.



                I tried to make use of the awesome cordova-plugin-openwith by Jean-Christophe Hoelt but faced several issues. The plugin is meant to receive share items of one type (say, URL, text or image), which is configured during installation. Also, with its current implementation, writing a note to share and selecting a receiver in a Cordova app are two different steps in different (native and Cordova) context, so it didn't look as a good user experience to me.



                I made these and other corrections to this plugin and published it as a separate plugin:
                https://github.com/EternallLight/cordova-plugin-openwith-ios



                Note that it works only for iOS, not for Android.






                share|improve this answer


























                  1












                  1








                  1







                  That's a good and still relevant question.



                  I tried to make use of the awesome cordova-plugin-openwith by Jean-Christophe Hoelt but faced several issues. The plugin is meant to receive share items of one type (say, URL, text or image), which is configured during installation. Also, with its current implementation, writing a note to share and selecting a receiver in a Cordova app are two different steps in different (native and Cordova) context, so it didn't look as a good user experience to me.



                  I made these and other corrections to this plugin and published it as a separate plugin:
                  https://github.com/EternallLight/cordova-plugin-openwith-ios



                  Note that it works only for iOS, not for Android.






                  share|improve this answer













                  That's a good and still relevant question.



                  I tried to make use of the awesome cordova-plugin-openwith by Jean-Christophe Hoelt but faced several issues. The plugin is meant to receive share items of one type (say, URL, text or image), which is configured during installation. Also, with its current implementation, writing a note to share and selecting a receiver in a Cordova app are two different steps in different (native and Cordova) context, so it didn't look as a good user experience to me.



                  I made these and other corrections to this plugin and published it as a separate plugin:
                  https://github.com/EternallLight/cordova-plugin-openwith-ios



                  Note that it works only for iOS, not for Android.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 8 '17 at 13:32









                  EternalLightEternalLight

                  1,1171816




                  1,1171816























                      0














                      Following up on the iOS 10 update remark by Aaron Rosen, here's the process to make it work :





                      1. In the code from the original answer by Sebastien Lorber, update the doOpenUrl function as suggested by Aaron. Reposting here for clarity:



                        private func doOpenUrl(url: String) {
                        let url = NSURL(string:url)
                        let context = NSExtensionContext()
                        context.open(url! as URL, completionHandler: nil)
                        var responder = self as UIResponder?
                        while (responder != nil){
                        if responder?.responds(to: Selector("openURL:")) == true{
                        responder?.perform(Selector("openURL:"), with: url)
                        }
                        responder = responder!.next
                        }
                        }


                      2. Follow the process outlined in the initial answer to create the extension in Xcode


                      3. Select ShareViewController.swift in the extension folder

                      4. Go to Edit > Convert > To Current Swift Syntax

                      5. In the extension build settings, toggle "Require Only App-Extension-Safe API" to NO.


                      Only then will the extension work.






                      share|improve this answer
























                      • With this solution, it works on simulator but not on real device. Any idea why ?

                        – Djiggy
                        Sep 15 '17 at 16:24











                      • It's impossible to tell you why just based on this comment, but you can try debugging the javascript in Safari : make sure your phone is plugged in and Safari has developer mode enabled (preferences -> advanced). When you launch the app your phone will appear in the developer menu of Safari and you can inspect the webview and check the console there for errors. Hopefully you'll have relevant info. Check the logs in Xcode also they can contain relevant info. Make sure also that the extension is properly signed.

                        – Edward Silhol
                        Sep 18 '17 at 16:55













                      • Thanks for you response. Unfortunaltely all seems to be ok in the app (no errors in safari/xcode logs), moreover, my custom url starts application from safari but not from gallery. I continue my investigation !

                        – Djiggy
                        Sep 19 '17 at 8:01






                      • 1





                        Ok, that's no surprise. So far we've only handled opening URLs from Safari, been too busy to work on it more. If you find out how to handle them from other apps, I'd love to hear how !

                        – Edward Silhol
                        Sep 20 '17 at 10:53
















                      0














                      Following up on the iOS 10 update remark by Aaron Rosen, here's the process to make it work :





                      1. In the code from the original answer by Sebastien Lorber, update the doOpenUrl function as suggested by Aaron. Reposting here for clarity:



                        private func doOpenUrl(url: String) {
                        let url = NSURL(string:url)
                        let context = NSExtensionContext()
                        context.open(url! as URL, completionHandler: nil)
                        var responder = self as UIResponder?
                        while (responder != nil){
                        if responder?.responds(to: Selector("openURL:")) == true{
                        responder?.perform(Selector("openURL:"), with: url)
                        }
                        responder = responder!.next
                        }
                        }


                      2. Follow the process outlined in the initial answer to create the extension in Xcode


                      3. Select ShareViewController.swift in the extension folder

                      4. Go to Edit > Convert > To Current Swift Syntax

                      5. In the extension build settings, toggle "Require Only App-Extension-Safe API" to NO.


                      Only then will the extension work.






                      share|improve this answer
























                      • With this solution, it works on simulator but not on real device. Any idea why ?

                        – Djiggy
                        Sep 15 '17 at 16:24











                      • It's impossible to tell you why just based on this comment, but you can try debugging the javascript in Safari : make sure your phone is plugged in and Safari has developer mode enabled (preferences -> advanced). When you launch the app your phone will appear in the developer menu of Safari and you can inspect the webview and check the console there for errors. Hopefully you'll have relevant info. Check the logs in Xcode also they can contain relevant info. Make sure also that the extension is properly signed.

                        – Edward Silhol
                        Sep 18 '17 at 16:55













                      • Thanks for you response. Unfortunaltely all seems to be ok in the app (no errors in safari/xcode logs), moreover, my custom url starts application from safari but not from gallery. I continue my investigation !

                        – Djiggy
                        Sep 19 '17 at 8:01






                      • 1





                        Ok, that's no surprise. So far we've only handled opening URLs from Safari, been too busy to work on it more. If you find out how to handle them from other apps, I'd love to hear how !

                        – Edward Silhol
                        Sep 20 '17 at 10:53














                      0












                      0








                      0







                      Following up on the iOS 10 update remark by Aaron Rosen, here's the process to make it work :





                      1. In the code from the original answer by Sebastien Lorber, update the doOpenUrl function as suggested by Aaron. Reposting here for clarity:



                        private func doOpenUrl(url: String) {
                        let url = NSURL(string:url)
                        let context = NSExtensionContext()
                        context.open(url! as URL, completionHandler: nil)
                        var responder = self as UIResponder?
                        while (responder != nil){
                        if responder?.responds(to: Selector("openURL:")) == true{
                        responder?.perform(Selector("openURL:"), with: url)
                        }
                        responder = responder!.next
                        }
                        }


                      2. Follow the process outlined in the initial answer to create the extension in Xcode


                      3. Select ShareViewController.swift in the extension folder

                      4. Go to Edit > Convert > To Current Swift Syntax

                      5. In the extension build settings, toggle "Require Only App-Extension-Safe API" to NO.


                      Only then will the extension work.






                      share|improve this answer













                      Following up on the iOS 10 update remark by Aaron Rosen, here's the process to make it work :





                      1. In the code from the original answer by Sebastien Lorber, update the doOpenUrl function as suggested by Aaron. Reposting here for clarity:



                        private func doOpenUrl(url: String) {
                        let url = NSURL(string:url)
                        let context = NSExtensionContext()
                        context.open(url! as URL, completionHandler: nil)
                        var responder = self as UIResponder?
                        while (responder != nil){
                        if responder?.responds(to: Selector("openURL:")) == true{
                        responder?.perform(Selector("openURL:"), with: url)
                        }
                        responder = responder!.next
                        }
                        }


                      2. Follow the process outlined in the initial answer to create the extension in Xcode


                      3. Select ShareViewController.swift in the extension folder

                      4. Go to Edit > Convert > To Current Swift Syntax

                      5. In the extension build settings, toggle "Require Only App-Extension-Safe API" to NO.


                      Only then will the extension work.







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Jun 29 '17 at 17:54









                      Edward SilholEdward Silhol

                      1




                      1













                      • With this solution, it works on simulator but not on real device. Any idea why ?

                        – Djiggy
                        Sep 15 '17 at 16:24











                      • It's impossible to tell you why just based on this comment, but you can try debugging the javascript in Safari : make sure your phone is plugged in and Safari has developer mode enabled (preferences -> advanced). When you launch the app your phone will appear in the developer menu of Safari and you can inspect the webview and check the console there for errors. Hopefully you'll have relevant info. Check the logs in Xcode also they can contain relevant info. Make sure also that the extension is properly signed.

                        – Edward Silhol
                        Sep 18 '17 at 16:55













                      • Thanks for you response. Unfortunaltely all seems to be ok in the app (no errors in safari/xcode logs), moreover, my custom url starts application from safari but not from gallery. I continue my investigation !

                        – Djiggy
                        Sep 19 '17 at 8:01






                      • 1





                        Ok, that's no surprise. So far we've only handled opening URLs from Safari, been too busy to work on it more. If you find out how to handle them from other apps, I'd love to hear how !

                        – Edward Silhol
                        Sep 20 '17 at 10:53



















                      • With this solution, it works on simulator but not on real device. Any idea why ?

                        – Djiggy
                        Sep 15 '17 at 16:24











                      • It's impossible to tell you why just based on this comment, but you can try debugging the javascript in Safari : make sure your phone is plugged in and Safari has developer mode enabled (preferences -> advanced). When you launch the app your phone will appear in the developer menu of Safari and you can inspect the webview and check the console there for errors. Hopefully you'll have relevant info. Check the logs in Xcode also they can contain relevant info. Make sure also that the extension is properly signed.

                        – Edward Silhol
                        Sep 18 '17 at 16:55













                      • Thanks for you response. Unfortunaltely all seems to be ok in the app (no errors in safari/xcode logs), moreover, my custom url starts application from safari but not from gallery. I continue my investigation !

                        – Djiggy
                        Sep 19 '17 at 8:01






                      • 1





                        Ok, that's no surprise. So far we've only handled opening URLs from Safari, been too busy to work on it more. If you find out how to handle them from other apps, I'd love to hear how !

                        – Edward Silhol
                        Sep 20 '17 at 10:53

















                      With this solution, it works on simulator but not on real device. Any idea why ?

                      – Djiggy
                      Sep 15 '17 at 16:24





                      With this solution, it works on simulator but not on real device. Any idea why ?

                      – Djiggy
                      Sep 15 '17 at 16:24













                      It's impossible to tell you why just based on this comment, but you can try debugging the javascript in Safari : make sure your phone is plugged in and Safari has developer mode enabled (preferences -> advanced). When you launch the app your phone will appear in the developer menu of Safari and you can inspect the webview and check the console there for errors. Hopefully you'll have relevant info. Check the logs in Xcode also they can contain relevant info. Make sure also that the extension is properly signed.

                      – Edward Silhol
                      Sep 18 '17 at 16:55







                      It's impossible to tell you why just based on this comment, but you can try debugging the javascript in Safari : make sure your phone is plugged in and Safari has developer mode enabled (preferences -> advanced). When you launch the app your phone will appear in the developer menu of Safari and you can inspect the webview and check the console there for errors. Hopefully you'll have relevant info. Check the logs in Xcode also they can contain relevant info. Make sure also that the extension is properly signed.

                      – Edward Silhol
                      Sep 18 '17 at 16:55















                      Thanks for you response. Unfortunaltely all seems to be ok in the app (no errors in safari/xcode logs), moreover, my custom url starts application from safari but not from gallery. I continue my investigation !

                      – Djiggy
                      Sep 19 '17 at 8:01





                      Thanks for you response. Unfortunaltely all seems to be ok in the app (no errors in safari/xcode logs), moreover, my custom url starts application from safari but not from gallery. I continue my investigation !

                      – Djiggy
                      Sep 19 '17 at 8:01




                      1




                      1





                      Ok, that's no surprise. So far we've only handled opening URLs from Safari, been too busy to work on it more. If you find out how to handle them from other apps, I'd love to hear how !

                      – Edward Silhol
                      Sep 20 '17 at 10:53





                      Ok, that's no surprise. So far we've only handled opening URLs from Safari, been too busy to work on it more. If you find out how to handle them from other apps, I'd love to hear how !

                      – Edward Silhol
                      Sep 20 '17 at 10:53


















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f33105698%2fcordova-sharing-browser-url-to-my-ios-app-clipper-ios-share-extension%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Berounka

                      Sphinx de Gizeh

                      Different font size/position of beamer's navigation symbols template's content depending on regular/plain...