3
votes

I have an iOS app with a share extension. The share extension uses the "predicate syntax" NSExtensionActivationRule plist setting. Here is the predicate string that's used.

SUBQUERY (
    extensionItems,
    $extensionItem,
    SUBQUERY (
        $extensionItem.attachments,
        $attachment,
        ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
    ).@count == 1
).@count == 1

The goal is to match content that has one URL, regardless of what other properties it has. Unfortunately there are cases where this seems to match content without any URL's (defined by a type of public.url or anything that conforms to it).

For the purposes of a simple example, I swapped out the view controller with this simplified class.

import UIKit
import Social
import MobileCoreServices

class ShareViewController: SLComposeServiceViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    self.logAttachments()
    // do nothing
    self.extensionContext?.completeRequestReturningItems([]) {(_:Bool) -> Void in}
  }

  func logAttachments() {
    if let items:[NSExtensionItem] = self.extensionContext?.inputItems as? [NSExtensionItem] {
      for item in items {
        self.logItemAttachments(item)
      }
    } else {
      println("No items")
    }
  }

  func logItemAttachments(item:NSExtensionItem) {
    if let attachments = item.attachments as? [NSItemProvider] {
      for a in attachments {
        println(a.registeredTypeIdentifiers)
      }
    } else {
      println("No attachments")
    }
  }
}

All this class does is print the types of each shared item's attachments. Most of the time I get what I'd expect with one attachment having a public.url type. Here's an example from a webpage in the iOS Chrome app.

[public.plain-text]
[public.url]

For content that does not have a URL, usually (as expected) the sharing option is not available. If I change the NSExtensionActivationRule to TRUEPREDICATE I can see an example like this when sharing a note file from Simplenote.

[public.plain-text]

Unfortunately I came across a case in the NYT Now app where my original predicate does match but the content does not have any attachments with a public.url type (or any type that conforms to public.url according to this list). Here is the log output from sharing a NYT Now story.

[public.image]
[public.plain-text]

I can't figure out why my extension shows up for this content, since neither public.image nor public.plain-text conform to public.url. The plain text item does have a URL in it, but there is also a bunch of other non-URL text.

I'd love to figure out why my extension's predicate is matching this content and how to stop it (or even better, if there's actually a URL somewhere that I can't find).

If I use a dictionary for the NSExtensionActivationRule and set NSExtensionActivationSupportsWebURLWithMaxCount=1 the extension (correctly) does not appear for the NYT Now stories, so it seems like the problem is with the predicate rule.

1
I get the same thing, even with variations on the predicate string. I'd call it a bug in the predicate system unfortunately.Tom Harrington
Chiming to say that sharing from the Podcasts app demonstrates this as well.nomad00
Smells like a bug. Have you put in a radar?Matt S.

1 Answers

0
votes

While this may not help your specific situation, I wanted to share my workaround.

Context: The following is a skeleton version of the NSItemProviderCompletionHandler I use when the NSItemProvider conforms to kUTTypePlainText.

lazy var textHandler: NSItemProviderCompletionHandler = { [unowned self]
(result: NSSecureCoding?, error: NSError!) in
    if let text = result as? String {
        print("Text: \(text)")
        // If NSDataDetector thinks we have a link - use it!
        let types: NSTextCheckingType = [.Link]
        let detector = try? NSDataDetector(types: types.rawValue)
        if let match = detector?.firstMatchInString(text, options: [], range: NSMakeRange(0, (text as NSString).length))
            where match.resultType == .Link  {
                print("URL: \(match.URL!.absoluteString)")
        }
    }
}

Worth noting that this is even though the App does not advertise support for public.plain-text (just public.url like in your case).