Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Share Extension is not working in Chrome

Works for Safari, Doesn't work for Chrome

Maybe the question is simple and stupid but I am new to iOS Development and I cannot find any right solution to solve this issue.

I need to get:
1) page url
2) page name

Extension

Info.plist

<dict>
    <key>NSExtensionAttributes</key>
    <dict>
        <key>NSExtensionActivationRule</key>
        <dict>
            <key>NSExrensionActivationSupportsText</key>
            <true/>
            <key>NSExtensionActivationSupportsFileWithMaxCount</key>
            <integer>1</integer>
            <key>NSExtensionActivationSupportsImageWithMaxCount</key>
            <integer>1</integer>
            <key>NSExtensionActivationSupportsMovieWithMaxCount</key>
            <integer>20</integer>
            <key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
            <integer>1</integer>
            <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
            <integer>0</integer>
        </dict>
        <key>NSExtensionJavaScriptPreprocessingFile</key>
        <string>DemoPreprocessor</string>
    </dict>
    <key>NSExtensionMainStoryboard</key>
    <string>MainInterface</string>
    <key>NSExtensionPointIdentifier</key>
    <string>com.apple.share-services</string>
</dict>

I've added more to Chrome but it still does not work. For Safari, this is enough:

NSExtensionActivationSupportsWebPageWithMaxCount, NSExtensionActivationSupportsWebURLWithMaxCount, NSExrensionActivationSupportsText, NSExtensionJavaScriptPreprocessingFile

I try three approaches, with and without DemoPreprocessor.js. But all don't work for Chrome:

1. ShareViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()

    let items = extensionContext?.inputItems
    var itemProvider: NSItemProvider?

    if items != nil && items!.isEmpty == false {
        let item = items![0] as! NSExtensionItem
        if let attachments = item.attachments {
            if !attachments.isEmpty {
                itemProvider = (attachments[0] as? NSItemProvider)!
            }
        }
    }

    let urlType = kUTTypePropertyList as String
    if ((itemProvider?.hasItemConformingToTypeIdentifier(urlType)) != nil) {
        itemProvider?.loadItemForTypeIdentifier(urlType, options: nil, completionHandler: {
            (item: NSSecureCoding?, error: NSError!) -> Void in
            if let resultDict = item as? NSDictionary {
                self.linkName = resultDict[NSExtensionJavaScriptPreprocessingResultsKey]!["URL"] as! String
                self.linkUrl = resultDict[NSExtensionJavaScriptPreprocessingResultsKey]!["title"] as! String
            }

        })
    }

}

2 ShareViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()

    let items = extensionContext?.inputItems
    var itemProvider: NSItemProvider?

    if items != nil && items!.isEmpty == false {
        let item = items![0] as! NSExtensionItem
        if let attachments = item.attachments {
            if !attachments.isEmpty {
                itemProvider = (attachments[0] as? NSItemProvider)!
            }
        }
    }

    let urlType = kUTTypeURL as NSString as String

    if ((itemProvider?.hasItemConformingToTypeIdentifier(urlType)) != nil) {
       itemProvider?.loadItemForTypeIdentifier(urlType, options: nil, completionHandler: {
           item, error in
           self.linkName = self.contentText
           self.linkUrl = "\(item!)"
       })
    }

}

3 ShareViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()

    let items = extensionContext?.inputItems
    var itemProvider: NSItemProvider?

    if items != nil && items!.isEmpty == false {
        let item = items![0] as! NSExtensionItem
        if let attachments = item.attachments {
            if !attachments.isEmpty {
                itemProvider = (attachments[0] as? NSItemProvider)!
            }
        }
    }

    if self.linkUrl.characters.count < 1 {
        if ((itemProvider?.hasItemConformingToTypeIdentifier("public.url")) != nil) {
            itemProvider?.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: {
                item, error in self.linkUrl = "\(item!)"
            })
        }
    }

}

DemoPreprocessor.js

var MyPreprocessor = function() {};

MyPreprocessor.prototype = {
    run: function(arguments) {
        arguments.completionFunction({"URL": document.URL, "pageSource": document.documentElement.outerHTML, "title": document.title, "selection": window.getSelection().toString()});
    }
};

var ExtensionPreprocessingJS = new MyPreprocessor;
like image 662
Denis Avatar asked Jul 28 '16 13:07

Denis


People also ask

Do Chrome extensions work on iOS?

You can use Chrome extensions on iPhone via the Chrome Remote Desktop app for iPhone and iPad. For the uninitiated, it is an app that allows you to securely access your computer from your iOS device.

Can you use browser extensions on iOS?

View and add Safari extensionsGo to Settings > Safari, then tap Extensions. Tap More Extensions to browse extensions from the App Store. When you find one you want, tap the price, or if the app is free, tap Get, then follow the onscreen instructions.

What is Share extension in iOS?

Share Extension is an easy way that Apple provides to share contents (images, audio, files, etc.) from one app to another, even made by different developers.


1 Answers

In my app I've made it work, however it was some time ago and I don't remember which exact steps are necessary, so I'll post my setup in hopes it will serve as a drop in solution.

The first issue I see in your plist is a 0 count for WebURLs.

This is a part of my extension's .plist. I think that allowing arbitrary loads has nothing to do with sharing and it was needed for accessing my server, nevertheless I'll put it here for reference.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
<key>NSExtension</key>
<dict>
    <key>NSExtensionAttributes</key>
    <dict>
        <key>NSExtensionActivationRule</key>
        <dict>
            <key>NSExtensionActivationSupportsImageWithMaxCount</key>
            <integer>1</integer>
            <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
            <integer>1</integer>
        </dict>
    </dict>
    <key>NSExtensionPointIdentifier</key>
    <string>com.apple.share-services</string>
    <key>NSExtensionPrincipalClass</key>
    <string>ShareViewController</string>
</dict>

My controller's part of code responsible for getting a URL:

// MARK: - NSExtensionRequestHandling
extension ShareViewController {
    override func beginRequestWithExtensionContext(context: NSExtensionContext) {
        super.beginRequestWithExtensionContext(context)

        guard let provider = (context.inputItems.first?.attachments as? [NSItemProvider])?.first else { return cancel() }

        if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
            provider.loadItemForTypeIdentifier(kUTTypeURL as String, options: nil) { url, error in
                guard let url = url as? NSURL else { return self.cancel() }
                // do what you want with the url
            }
        } else {
            cancel()
        }
    }
}

As for getting the site's name, well there are a few ways I think. One is to use the JS script that you already have in your project, another is to see whether the provider has an item with a text type (containing the site's name) but they are browser specific (some work only for Safari, some for browsers like Chrome). I chose to go about it in a different way, I use a fake WKWebView (which is never added to view hierarchy, it's just a property) and make a request with the url that I got from the provider. Then I'm able to intercept the site's name from it's JS code like so:

func setupHiddenWebView() {
    hiddenWebView = WKWebView()
    hiddenWebView?.navigationDelegate = self
}

func setupForURL(url: NSURL) {
    (...)
    hiddenWebView?.loadRequest(NSURLRequest(URL: url))
}

// MARK: - WKNavigationDelegate
extension ShareView: WKNavigationDelegate {
    func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
        webView.evaluateJavaScript("document.title") { result, error in
            guard let title = result as? String else { return }
            // do what you want with the page's title
        }
    }
}
like image 130
Andrzej Filipowicz Avatar answered Sep 27 '22 23:09

Andrzej Filipowicz