diff --git a/node_modules/react-native-share-menu/ios/Constants.swift b/node_modules/react-native-share-menu/ios/Constants.swift index 2811008..08385b7 100644 --- a/node_modules/react-native-share-menu/ios/Constants.swift +++ b/node_modules/react-native-share-menu/ios/Constants.swift @@ -23,7 +23,7 @@ public let COULD_NOT_PARSE_IMG_ERROR = "Couldn't parse image" public let COULD_NOT_SAVE_FILE_ERROR = "Couldn't save file on disk" public let NO_EXTENSION_CONTEXT_ERROR = "No extension context attached" public let NO_DELEGATE_ERROR = "No ReactShareViewDelegate attached" -public let COULD_NOT_FIND_ITEM_ERROR = "Couldn't find item attached to this share" +public let COULD_NOT_FIND_ITEMS_ERROR = "Couldn't find items attached to this share" // MARK: Keys diff --git a/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift b/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift index 6c4922a..74badda 100644 --- a/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift +++ b/node_modules/react-native-share-menu/ios/Modules/ShareMenu.swift @@ -9,7 +9,7 @@ class ShareMenu: RCTEventEmitter { } } - var sharedData: [String:String]? + var sharedData: [[String:String]?]? static var initialShare: (UIApplication, URL, [UIApplication.OpenURLOptionsKey : Any])? @@ -91,7 +91,7 @@ class ShareMenu: RCTEventEmitter { let extraData = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String:Any] - if let data = userDefaults.object(forKey: USER_DEFAULTS_KEY) as? [String:String] { + if let data = userDefaults.object(forKey: USER_DEFAULTS_KEY) as? [[String:String]] { sharedData = data dispatchEvent(with: data, and: extraData) userDefaults.removeObject(forKey: USER_DEFAULTS_KEY) @@ -100,25 +100,22 @@ class ShareMenu: RCTEventEmitter { @objc(getSharedText:) func getSharedText(callback: RCTResponseSenderBlock) { - guard var data: [String:Any] = sharedData else { - callback([]) - return - } + var data = [DATA_KEY: sharedData] as [String: Any] if let bundleId = Bundle.main.bundleIdentifier, let userDefaults = UserDefaults(suiteName: "group.\(bundleId)") { - data[EXTRA_DATA_KEY] = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String:Any] + data[EXTRA_DATA_KEY] = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String: Any] } else { print("Error: \(NO_APP_GROUP_ERROR)") } callback([data as Any]) - sharedData = nil + sharedData = [] } - func dispatchEvent(with data: [String:String], and extraData: [String:Any]?) { + func dispatchEvent(with data: [[String:String]], and extraData: [String:Any]?) { guard hasListeners else { return } - var finalData = data as [String:Any] + var finalData = [DATA_KEY: data] as [String: Any] if (extraData != nil) { finalData[EXTRA_DATA_KEY] = extraData } 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 5d21773..0c7eaa7 100644 --- a/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift +++ b/node_modules/react-native-share-menu/ios/Modules/ShareMenuReactView.swift @@ -3,8 +3,9 @@ // RNShareMenu // // Created by Gustavo Parreira on 28/07/2020. -// +// Modified by Veselin Stoyanov on 17/04/2021. +import Foundation import MobileCoreServices @objc(ShareMenuReactView) @@ -17,8 +18,6 @@ public class ShareMenuReactView: NSObject { } public static func attachViewDelegate(_ delegate: ReactShareViewDelegate!) { - guard (ShareMenuReactView.viewDelegate == nil) else { return } - ShareMenuReactView.viewDelegate = delegate } @@ -65,12 +64,12 @@ public class ShareMenuReactView: NSObject { let extensionContext = viewDelegate.loadExtensionContext() - guard let item = extensionContext.inputItems.first as? NSExtensionItem else { - print("Error: \(COULD_NOT_FIND_ITEM_ERROR)") + guard let items = extensionContext.inputItems as? [NSExtensionItem] else { + print("Error: \(COULD_NOT_FIND_ITEMS_ERROR)") return } - viewDelegate.continueInApp(with: item, and: extraData) + viewDelegate.continueInApp(with: items, and: extraData) } @objc(data:reject:) @@ -82,91 +81,96 @@ public class ShareMenuReactView: NSObject { return } - extractDataFromContext(context: extensionContext) { (data, mimeType, error) in + extractDataFromContext(context: extensionContext) { (data, error) in guard (error == nil) else { reject("error", error?.description, nil) return } - resolve([MIME_TYPE_KEY: mimeType, DATA_KEY: data]) + resolve([DATA_KEY: data]) } } - func extractDataFromContext(context: NSExtensionContext, withCallback callback: @escaping (String?, String?, NSException?) -> Void) { - let item:NSExtensionItem! = context.inputItems.first as? NSExtensionItem - let attachments:[AnyObject]! = item.attachments - - var urlProvider:NSItemProvider! = nil - var imageProvider:NSItemProvider! = nil - var textProvider:NSItemProvider! = nil - var dataProvider:NSItemProvider! = nil - - for provider in attachments { - if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) { - urlProvider = provider as? NSItemProvider - break - } else if provider.hasItemConformingToTypeIdentifier(kUTTypeText as String) { - textProvider = provider as? NSItemProvider - break - } else if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) { - imageProvider = provider as? NSItemProvider - break - } else if provider.hasItemConformingToTypeIdentifier(kUTTypeData as String) { - dataProvider = provider as? NSItemProvider - break - } - } + func extractDataFromContext(context: NSExtensionContext, withCallback callback: @escaping ([Any]?, NSException?) -> Void) { + DispatchQueue.global().async { + let semaphore = DispatchSemaphore(value: 0) + let items:[NSExtensionItem]! = context.inputItems as? [NSExtensionItem] + var results: [[String: String]] = [] - if (urlProvider != nil) { - urlProvider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (item, error) in - let url: URL! = item as? URL + for item in items { + guard let attachments = item.attachments else { + callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find attachments", userInfo:nil)) + return + } - callback(url.absoluteString, "text/plain", nil) - } - } else if (imageProvider != nil) { - imageProvider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { (item, error) in - let imageUrl: URL! = item as? URL + for provider in attachments { + if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) { + provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (item, error) in + let url: URL! = item as? URL - if (imageUrl != nil) { - if let imageData = try? Data(contentsOf: imageUrl) { - callback(imageUrl.absoluteString, self.extractMimeType(from: imageUrl), nil) - } - } else { - let image: UIImage! = item as? UIImage + results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"]) - if (image != nil) { - let imageData: Data! = image.pngData(); + semaphore.signal() + } + semaphore.wait() + } else if provider.hasItemConformingToTypeIdentifier(kUTTypeText as String) { + provider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (item, error) in + let text:String! = item as? String + + results.append([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"]) - // Creating temporary URL for image data (UIImage) - guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TemporaryScreenshot.png") else { - return + semaphore.signal() + } + semaphore.wait() + } else if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) { + provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { (item, error) in + let imageUrl: URL! = item as? URL + + if (imageUrl != nil) { + if let imageData = try? Data(contentsOf: imageUrl) { + results.append([DATA_KEY: imageUrl.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: imageUrl)]) + } + } else { + let image: UIImage! = item as? UIImage + + if (image != nil) { + let imageData: Data! = image.pngData(); + + // Creating 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)) + } + } + } + + semaphore.signal() } + semaphore.wait() + } else if provider.hasItemConformingToTypeIdentifier(kUTTypeData as String) { + provider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (item, error) in + let url: URL! = item as? URL - do { - // Writing the image to the URL - try imageData.write(to: imageURL) + results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: url)]) - callback(imageURL.absoluteString, imageURL.extractMimeType(), nil) - } catch { - callback(nil, nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't load image", userInfo:nil)) + semaphore.signal() } + semaphore.wait() + } else { + callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find provider", userInfo:nil)) } } } - } else if (textProvider != nil) { - textProvider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (item, error) in - let text:String! = item as? String - callback(text, "text/plain", nil) - } - } else if (dataProvider != nil) { - dataProvider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (item, error) in - let url: URL! = item as? URL - - callback(url.absoluteString, self.extractMimeType(from: url), nil) - } - } else { - callback(nil, nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"couldn't find provider", userInfo:nil)) + callback(results, nil) } } diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift index 0189ef6..f42bce6 100644 --- a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift +++ b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift @@ -62,7 +62,7 @@ class ReactShareViewController: ShareViewController, RCTBridgeDelegate, ReactSha self.openHostApp() } - func continueInApp(with item: NSExtensionItem, and extraData: [String:Any]?) { - handlePost(item, extraData: extraData) + func continueInApp(with items: [NSExtensionItem], and extraData: [String:Any]?) { + handlePost(items, extraData: extraData) } } diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift b/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift index 0aa4c58..ad0812c 100644 --- a/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift +++ b/node_modules/react-native-share-menu/ios/ReactShareViewDelegate.swift @@ -10,5 +10,5 @@ public protocol ReactShareViewDelegate { func openApp() - func continueInApp(with item: NSExtensionItem, and extraData: [String:Any]?) + func continueInApp(with items: [NSExtensionItem], and extraData: [String:Any]?) } diff --git a/node_modules/react-native-share-menu/ios/ShareViewController.swift b/node_modules/react-native-share-menu/ios/ShareViewController.swift index 7faf6e4..f02bde5 100644 --- a/node_modules/react-native-share-menu/ios/ShareViewController.swift +++ b/node_modules/react-native-share-menu/ios/ShareViewController.swift @@ -6,15 +6,18 @@ // // Created by Gustavo Parreira on 26/07/2020. // +// Modified by Veselin Stoyanov on 17/04/2021. +import Foundation import MobileCoreServices import UIKit import Social import RNShareMenu -class ShareViewController: SLComposeServiceViewController { +class ShareViewController: UIViewController { var hostAppId: String? var hostAppUrlScheme: String? + var sharedItems: [Any] = [] override func viewDidLoad() { super.viewDidLoad() @@ -30,46 +33,64 @@ class ShareViewController: SLComposeServiceViewController { } else { print("Error: \(NO_INFO_PLIST_URL_SCHEME_ERROR)") } - } - override func isContentValid() -> Bool { - // Do validation of contentText and/or NSExtensionContext attachments here - return true + guard let items = extensionContext?.inputItems as? [NSExtensionItem] else { + cancelRequest() + return } - override func didSelectPost() { - // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments. - guard let item = extensionContext?.inputItems.first as? NSExtensionItem else { - cancelRequest() + handlePost(items) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + completeRequest() + } + + func handlePost(_ items: [NSExtensionItem], extraData: [String:Any]? = nil) { + DispatchQueue.global().async { + guard let hostAppId = self.hostAppId else { + self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR) + return + } + guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else { + self.exit(withError: NO_APP_GROUP_ERROR) return } - handlePost(item) - } + if let data = extraData { + self.storeExtraData(data) + } else { + self.removeExtraData() + } - override func configurationItems() -> [Any]! { - // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. - return [] - } + let semaphore = DispatchSemaphore(value: 0) - func handlePost(_ item: NSExtensionItem, extraData: [String:Any]? = nil) { - guard let provider = item.attachments?.first else { - cancelRequest() - return - } + for item in items { + guard let attachments = item.attachments else { + self.cancelRequest() + return + } - if let data = extraData { - storeExtraData(data) - } else { - removeExtraData() - } + for provider in attachments { + if provider.isText { + self.storeText(withProvider: provider, semaphore) + } else if provider.isURL { + self.storeUrl(withProvider: provider, semaphore) + } else { + self.storeFile(withProvider: provider, semaphore) + } - if provider.isText { - storeText(withProvider: provider) - } else if provider.isURL { - storeUrl(withProvider: provider) - } else { - storeFile(withProvider: provider) + semaphore.wait() + } + } + + userDefaults.set(self.sharedItems, + forKey: USER_DEFAULTS_KEY) + userDefaults.synchronize() + + self.openHostApp() } } @@ -99,7 +120,7 @@ class ShareViewController: SLComposeServiceViewController { userDefaults.synchronize() } - func storeText(withProvider provider: NSItemProvider) { + func storeText(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) { provider.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) { (data, error) in guard (error == nil) else { self.exit(withError: error.debugDescription) @@ -109,24 +130,13 @@ class ShareViewController: SLComposeServiceViewController { self.exit(withError: COULD_NOT_FIND_STRING_ERROR) return } - guard let hostAppId = self.hostAppId else { - self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR) - return - } - guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else { - self.exit(withError: NO_APP_GROUP_ERROR) - return - } - - userDefaults.set([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"], - forKey: USER_DEFAULTS_KEY) - userDefaults.synchronize() - self.openHostApp() + self.sharedItems.append([DATA_KEY: text, MIME_TYPE_KEY: "text/plain"]) + semaphore.signal() } } - func storeUrl(withProvider provider: NSItemProvider) { + func storeUrl(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) { provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (data, error) in guard (error == nil) else { self.exit(withError: error.debugDescription) @@ -136,24 +146,13 @@ class ShareViewController: SLComposeServiceViewController { self.exit(withError: COULD_NOT_FIND_URL_ERROR) return } - guard let hostAppId = self.hostAppId else { - self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR) - return - } - guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else { - self.exit(withError: NO_APP_GROUP_ERROR) - return - } - - userDefaults.set([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"], - forKey: USER_DEFAULTS_KEY) - userDefaults.synchronize() - self.openHostApp() + self.sharedItems.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: "text/plain"]) + semaphore.signal() } } - func storeFile(withProvider provider: NSItemProvider) { + func storeFile(withProvider provider: NSItemProvider, _ semaphore: DispatchSemaphore) { provider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { (data, error) in guard (error == nil) else { self.exit(withError: error.debugDescription) @@ -167,10 +166,6 @@ class ShareViewController: SLComposeServiceViewController { self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR) return } - guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else { - self.exit(withError: NO_APP_GROUP_ERROR) - return - } guard let groupFileManagerContainer = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppId)") else { @@ -189,11 +184,8 @@ class ShareViewController: SLComposeServiceViewController { return } - userDefaults.set([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType], - forKey: USER_DEFAULTS_KEY) - userDefaults.synchronize() - - self.openHostApp() + self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType]) + semaphore.signal() } }