From 12d51d9e22e2c4a96620918eabb32c7a804ad03a Mon Sep 17 00:00:00 2001
From: Michael Kirk <michael.code@endoftheworl.de>
Date: Tue, 27 Feb 2018 13:26:12 -0500
Subject: [PATCH] Fix sharing url when text is also present

This is sort of a stop gap intended to be minimal and safe.

// FREEBIE
---
 SignalShareExtension/Info.plist               |  8 ++---
 .../ShareViewController.swift                 | 34 ++++++++++++++++---
 2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist
index b265dba5e..710d778b8 100644
--- a/SignalShareExtension/Info.plist
+++ b/SignalShareExtension/Info.plist
@@ -61,11 +61,11 @@
                 $extensionItem.attachments,
                 $attachment,
                 (
-                ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.data"
-                || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
+                ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO &quot;public.data&quot;
+                || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO &quot;public.url&quot;
                 )
-                AND NOT (ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.vcard")
-                ).@count == 1
+                AND NOT (ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO &quot;public.vcard&quot;)
+                ).@count &gt;= 1
                 ).@count == 1
             </string>
 		</dict>
diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift
index a188e70ed..6f391160b 100644
--- a/SignalShareExtension/ShareViewController.swift
+++ b/SignalShareExtension/ShareViewController.swift
@@ -523,7 +523,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
                                           utiType:kUTTypeContact as String)
     }
 
-    private class func utiTypeForItem(itemProvider: NSItemProvider) -> String? {
+    private class func utiType(itemProvider: NSItemProvider) -> String? {
         Logger.info("\(self.logTag) utiTypeForItem: \(itemProvider.registeredTypeIdentifiers)")
 
         if isUrlItem(itemProvider:itemProvider) {
@@ -539,6 +539,25 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
         return matchingUtiType
     }
 
+    private class func preferredItemProvider(inputItem: NSExtensionItem) -> NSItemProvider? {
+        guard let attachments = inputItem.attachments else {
+            return nil
+        }
+
+        // Prefer a URL provider if available
+        if let preferredAttachment = attachments.first(where: { (attachment: Any) -> Bool in
+            guard let itemProvider = attachment as? NSItemProvider else {
+                return false
+            }
+            return isUrlItem(itemProvider: itemProvider)
+        }) {
+                return preferredAttachment as? NSItemProvider
+        }
+
+        // else return whatever is available
+        return inputItem.attachments?.first as? NSItemProvider
+    }
+
     private class func createDataSource(utiType: String, url: URL, customFileName: String?) -> DataSource? {
         if utiType == (kUTTypeURL as String) {
             // Share URLs as oversize text messages whose text content is the URL.
@@ -574,9 +593,14 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
             return Promise(error: error)
         }
 
-        // TODO Multiple attachments. In that case I'm unclear if we'll
-        // be given multiple inputItems or a single inputItem with multiple attachments.
-        guard let itemProvider: NSItemProvider = inputItem.attachments?.first as? NSItemProvider else {
+        // A single inputItem can have multiple attachments, e.g. sharing from Firefox gives
+        // one url attachment and another text attachment, where the the url would be https://some-news.com/articles/123-cat-stuck-in-tree
+        // and the text attachment would be something like "Breaking news - cat stuck in tree"
+        //
+        // FIXME: For now, we prefer the URL provider and discard the text provider, since it's more useful to share the URL than the caption
+        // but we *should* include both. This will be a bigger change though since our share extension is currently heavily predicated
+        // on one itemProvider per share.
+        guard let itemProvider: NSItemProvider = type(of: self).preferredItemProvider(inputItem: inputItem) else {
             let error = ShareViewControllerError.assertionError(description: "No item provider in input item attachments")
             return Promise(error: error)
         }
@@ -591,7 +615,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
         // * UTIs aren't very descriptive (there are far more MIME types than UTI types)
         //   so in the case of file attachments we try to refine the attachment type
         //   using the file extension.
-        guard let srcUtiType = ShareViewController.utiTypeForItem(itemProvider: itemProvider) else {
+        guard let srcUtiType = ShareViewController.utiType(itemProvider: itemProvider) else {
             let error = ShareViewControllerError.unsupportedMedia
             return Promise(error: error)
         }