react-native-share-menu icon indicating copy to clipboard operation
react-native-share-menu copied to clipboard

ios: sharing system screen snapshot doesn't open the app

Open xavax31 opened this issue 3 years ago • 7 comments
trafficstars

Hi,

When taking a screen snapshot and want to share it, my app appears in share menu. If i choose it, the share view of my app appears, but when i click to publish, the menu closes and my app is not displayed. If i try same thing with example provided in this repository, the app's share menu appears and closes immediately.

Any idea ?

xavax31 avatar Sep 07 '22 10:09 xavax31

Facing Same issue! @xavax31 Did you find any solution?

iamjatttin avatar Oct 26 '22 11:10 iamjatttin

@iamjatttin, a lot of hacks. The main problem found was that snapshot app doesn't give simply a image type, but a public url. I finally resolved for my use by opening public.url attachment type in the info.plist of shared extension, and doing some change in swift code, but without really swift skills ( my fork here ). I needed also to change my react native code, to copy the provided file in my app tmp directory, to avoid security access error. Not perfect because in my case, I only want images or pdf, but the public.url type include every file format. But this resolve many cases where the plugin didn't work correctly, and is sufficient for me.

info.plist:
<key>NSExtensionAttributes</key> <dict> <key>NSExtensionActivationRule</key> <string> SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ).@count == 1 ).@count == 1 </string> </dict>

xavax31 avatar Oct 26 '22 19:10 xavax31

@xavax31 I've had a similar but different problem (sharing screenshots) I've noticed that within your storeImageRawData function you're loading the item again from the provider. Not sure whether this is intended to work in swift, but then again, I'm lacking swift skills myself. Once I've removed this, your solution of handling it as UIImage has worked nicely for me, so thanks! Posting my unverified, single device, limited manual tested, slim patch for others that end up here

diff --git a/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift b/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
index e290cce..7380ee4 100644
--- a/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
+++ b/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift
@@ -147,7 +147,7 @@ public class ShareMenuReactView: NSObject {
                                         // Writing the image to the URL
                                         try imageData.write(to: imageURL)
 
-                                        results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: imageURL.extractMimeType()])
+                                        results.append([DATA_KEY: imageURL.absoluteString, MIME_TYPE_KEY: imageURL.extractMimeType()])
                                     } catch {
                                         callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't load image", userInfo:nil))
                                     }
diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
index f42bce6..ee36062 100644
--- a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
+++ b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
@@ -13,7 +13,7 @@ class ReactShareViewController: ShareViewController, RCTBridgeDelegate, ReactSha
   func sourceURL(for bridge: RCTBridge!) -> URL! {
 #if DEBUG
     return RCTBundleURLProvider.sharedSettings()?
-      .jsBundleURL(forBundleRoot: "index.share", fallbackResource: nil)
+      .jsBundleURL(forBundleRoot: "index.share")
 #else
     return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
 #endif
diff --git a/node_modules/react-native-share-menu/ios/ShareViewController.swift b/node_modules/react-native-share-menu/ios/ShareViewController.swift
index 12d8c92..4653c4c 100644
--- a/node_modules/react-native-share-menu/ios/ShareViewController.swift
+++ b/node_modules/react-native-share-menu/ios/ShareViewController.swift
@@ -166,10 +166,6 @@ class ShareViewController: SLComposeServiceViewController {
         self.exit(withError: error.debugDescription)
         return
       }
-      guard let url = data as? URL else {
-        self.exit(withError: COULD_NOT_FIND_IMG_ERROR)
-        return
-      }
       guard let hostAppId = self.hostAppId else {
         self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR)
         return
@@ -181,18 +177,41 @@ class ShareViewController: SLComposeServiceViewController {
         return
       }
       
-      let mimeType = url.extractMimeType()
-      let fileExtension = url.pathExtension
-      let fileName = UUID().uuidString
-      let filePath = groupFileManagerContainer
-        .appendingPathComponent("\(fileName).\(fileExtension)")
+      if let uiImage = data as? UIImage {
+        let fileExtension = "png"
+        let fileName = UUID().uuidString
+        let filePath = groupFileManagerContainer
+          .appendingPathComponent("\(fileName).\(fileExtension)")
                 
-      guard self.moveFileToDisk(from: url, to: filePath) else {
-        self.exit(withError: COULD_NOT_SAVE_FILE_ERROR)
+        guard let rawData = uiImage.pngData()
+        else {
+          self.exit(withError: "Error while getting raw data")
+          return
+        }
+        
+        guard FileManager.default.createFile(atPath: filePath.path, contents: rawData)
+        else {
+          self.exit(withError: "Error while createFile")
+          return
+        }
+        self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: "image/png"])        
+      } else if let url = data as? URL {
+        let mimeType = url.extractMimeType()
+        let fileExtension = url.pathExtension
+        let fileName = UUID().uuidString
+        let filePath = groupFileManagerContainer
+          .appendingPathComponent("\(fileName).\(fileExtension)")
+        
+        guard self.moveFileToDisk(from: url, to: filePath) else {
+          self.exit(withError: COULD_NOT_SAVE_FILE_ERROR)
+          return
+        }
+        
+        self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType])
+      } else {
+        self.exit(withError: COULD_NOT_FIND_IMG_ERROR)
         return
       }
-      
-      self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType])
       semaphore.signal()
     }
   }

filipgerat avatar Apr 21 '23 00:04 filipgerat

@filipgerat Is this your patch from @xavax31 's fork? Or is that patch from rn-share-menu master branch? I'm currently trying to get the fork running.

jakehasler avatar Apr 28 '23 17:04 jakehasler

@jakehasler the patch is from rn-share-menu.

filipgerat avatar Apr 28 '23 18:04 filipgerat

@filipgerat Your patch is working great! Thank you so much. Been struggling with this for a long while.

jakehasler avatar Apr 28 '23 20:04 jakehasler

In the ShareMenuReactView class, I noticed a variable name inconsistency in the extractDataFromContext function.

The issue is in this block of code:

let imageData: Data! = image.pngData();

// Creating a temporary URL for image data (UIImage)
guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TemporaryScreenshot.png") else {
  return
}

do {
  // Writing the image to the URL
  try imageData.write(to: imageURL)

  results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: imageURL.extractMimeType()])
} catch {
  callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't load image", userInfo:nil))
}

The temporary variable defined for "TemporaryScreenshot.png" is imageURL, but it is incorrectly used as imageUrl here:

results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: imageURL.extractMimeType()])

To fix this issue, you should change it to:

results.append([DATA_KEY: imageURL.absoluteString, MIME_TYPE_KEY: imageURL.extractMimeType()])

Add this in your ShareViewController in the storeFile function ShareViewController

if let uiImage = data as? UIImage {
  let fileExtension = "png"
  let fileName = UUID().uuidString
  let filePath = groupFileManagerContainer
                  .appendingPathComponent("\(fileName).\(fileExtension)")
        
  guard let rawData: Data = uiImage.pngData() else {
     self.exit(withError: "Error while getting raw data")
     return
  }
        
  guard FileManager.default.createFile(atPath: filePath.path, contents: rawData) else {
      self.exit(withError: "Error while createFile")
      return
  }
        
  self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: "image/png"])
  semaphore.signal()
  return
}

JesusTectronic avatar Sep 14 '23 19:09 JesusTectronic