iOS Share Extension is not working in Chrome

2019-04-05 07:12发布

问题:

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;

回答1:

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
        }
    }
}


回答2:

Add NSExtensionActivationSupportsText and NSExtensionActivationSupportsWebPageWithMaxCount under NSExtensionActivationRule with the type as Number and set them to 1.

That was what did it for me.