diff --git a/ios/Constants.swift b/ios/Constants.swift index 08385b7..4e17644 100644 --- a/ios/Constants.swift +++ b/ios/Constants.swift @@ -13,8 +13,8 @@ public let DISMISS_SHARE_EXTENSION_WITH_ERROR_CODE = 1 public let NO_URL_TYPES_ERROR_MESSAGE = "You have not defined CFBundleURLTypes in your Info.plist" public let NO_URL_SCHEMES_ERROR_MESSAGE = "You have not defined CFBundleURLSchemes in your Info.plist" public let NO_SCHEME_ERROR_MESSAGE = "You have not defined a scheme under CFBundleURLSchemes in your Info.plist" +public let NO_APP_GROUP_PLIST_ERROR = "Failed to get App Group. Did you set up App Group ID in your Share Extension Info.plist?" public let NO_APP_GROUP_ERROR = "Failed to get App Group User Defaults. Did you set up an App Group on your App and Share Extension?" -public let NO_INFO_PLIST_INDENTIFIER_ERROR = "You haven't defined \(HOST_APP_IDENTIFIER_INFO_PLIST_KEY) in your Share Extension's Info.plist" public let NO_INFO_PLIST_URL_SCHEME_ERROR = "You haven't defined \(HOST_URL_SCHEME_INFO_PLIST_KEY) in your Share Extension's Info.plist" public let COULD_NOT_FIND_STRING_ERROR = "Couldn't find string" public let COULD_NOT_FIND_URL_ERROR = "Couldn't find url" @@ -24,14 +24,18 @@ 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_ITEMS_ERROR = "Couldn't find items attached to this share" +public let COULD_NOT_GET_IMAGE_FILE_LOCATION = "Couldn't get image url file location to write to" +public let COULD_NOT_WRITE_IMAGE_DATA = +"Couldn't write image data" +public let COULD_NOT_TURN_DATA_TO_URL = "Couldn't turn data into url" // MARK: Keys public let USER_DEFAULTS_KEY = "ShareMenuUserDefaults" public let USER_DEFAULTS_EXTRA_DATA_KEY = "ShareMenuUserDefaultsExtraData" public let URL_SCHEME_INFO_PLIST_KEY = "AppURLScheme" -public let HOST_APP_IDENTIFIER_INFO_PLIST_KEY = "HostAppBundleIdentifier" public let HOST_URL_SCHEME_INFO_PLIST_KEY = "HostAppURLScheme" +public let GROUP_INFO_PLIST_KEY = "GroupID" public let REACT_SHARE_VIEW_BACKGROUND_COLOR_KEY = "ReactShareViewBackgroundColor" public let COLOR_RED_KEY = "Red" diff --git a/ios/Modules/ShareMenu.swift b/ios/Modules/ShareMenu.swift index 74badda..f1f7388 100644 --- a/ios/Modules/ShareMenu.swift +++ b/ios/Modules/ShareMenu.swift @@ -83,8 +83,11 @@ class ShareMenu: RCTEventEmitter { } guard let scheme = url.scheme, scheme == targetUrlScheme else { return } - guard let bundleId = Bundle.main.bundleIdentifier else { return } - guard let userDefaults = UserDefaults(suiteName: "group.\(bundleId)") else { + guard let groupId = Bundle.main.object(forInfoDictionaryKey: GROUP_INFO_PLIST_KEY) as? String else { + print("Error: \(NO_APP_GROUP_PLIST_ERROR)") + return + } + guard let userDefaults = UserDefaults(suiteName: groupId) else { print("Error: \(NO_APP_GROUP_ERROR)") return } @@ -102,7 +105,12 @@ class ShareMenu: RCTEventEmitter { func getSharedText(callback: RCTResponseSenderBlock) { var data = [DATA_KEY: sharedData] as [String: Any] - if let bundleId = Bundle.main.bundleIdentifier, let userDefaults = UserDefaults(suiteName: "group.\(bundleId)") { + guard let groupId = Bundle.main.object(forInfoDictionaryKey: GROUP_INFO_PLIST_KEY) as? String else { + print("Error: \(NO_APP_GROUP_PLIST_ERROR)") + return + } + + if let userDefaults = UserDefaults(suiteName: groupId) { data[EXTRA_DATA_KEY] = userDefaults.object(forKey: USER_DEFAULTS_EXTRA_DATA_KEY) as? [String: Any] } else { print("Error: \(NO_APP_GROUP_ERROR)") diff --git a/ios/Modules/ShareMenuReactView.swift b/ios/Modules/ShareMenuReactView.swift index e290cce..ed79170 100644 --- a/ios/Modules/ShareMenuReactView.swift +++ b/ios/Modules/ShareMenuReactView.swift @@ -3,7 +3,7 @@ // RNShareMenu // // Created by Gustavo Parreira on 28/07/2020. -// Modified by Veselin Stoyanov on 17/04/2021. +// Modified by Veselin Stoyanov on 03/07/2021. import Foundation import MobileCoreServices @@ -126,40 +126,16 @@ public class ShareMenuReactView: NSObject { 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)) - } - } - } + let url: URL! = self.getFileUrl(from: item as Any, withCallback: callback) as? URL + + results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: url)]) 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 + let url: URL! = self.getFileUrl(from: item as Any, withCallback: callback) as? URL results.append([DATA_KEY: url.absoluteString, MIME_TYPE_KEY: self.extractMimeType(from: url)]) @@ -176,6 +152,46 @@ public class ShareMenuReactView: NSObject { } } + func getFileUrl(from data: Any, withCallback callback: @escaping ([Any]?, NSException?) -> Void) -> Any { + var url: URL! = nil + var image: UIImage! = nil + + if data is UIImage { + image = data as? UIImage + } + + if data is Data { + image = UIImage(data: data as! Data)! + } + + if (image != nil) { + let imageData: Data! = image.pngData() + + // Creating temporary URL for image data (UIImage) + guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("TemporaryImage.png") else { + callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't save image in temp file", userInfo:nil)) + return "" + } + + do { + // Writing the image to the URL + try imageData.write(to: imageURL) + url = imageURL + } catch { + callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't write image to URL", userInfo:nil)) + } + } else { + guard let toUrl = data as? URL else { + callback(nil, NSException(name: NSExceptionName(rawValue: "Error"), reason:"Can't turn data into URL", userInfo:nil)) + return "" + } + + url = toUrl; + } + + return url! + } + func extractMimeType(from url: URL) -> String { let fileExtension: CFString = url.pathExtension as CFString guard let extUTI = UTTypeCreatePreferredIdentifierForTag( diff --git a/ios/ShareViewController.swift b/ios/ShareViewController.swift index 12d8c92..1c2c564 100644 --- a/ios/ShareViewController.swift +++ b/ios/ShareViewController.swift @@ -15,24 +15,24 @@ import Social import RNShareMenu class ShareViewController: SLComposeServiceViewController { - var hostAppId: String? var hostAppUrlScheme: String? + var groupId: String? var sharedItems: [Any] = [] override func viewDidLoad() { super.viewDidLoad() - if let hostAppId = Bundle.main.object(forInfoDictionaryKey: HOST_APP_IDENTIFIER_INFO_PLIST_KEY) as? String { - self.hostAppId = hostAppId - } else { - print("Error: \(NO_INFO_PLIST_INDENTIFIER_ERROR)") - } - if let hostAppUrlScheme = Bundle.main.object(forInfoDictionaryKey: HOST_URL_SCHEME_INFO_PLIST_KEY) as? String { self.hostAppUrlScheme = hostAppUrlScheme } else { print("Error: \(NO_INFO_PLIST_URL_SCHEME_ERROR)") } + + if let groupId = Bundle.main.object(forInfoDictionaryKey: GROUP_INFO_PLIST_KEY) as? String { + self.groupId = groupId + } else { + print("Error: \(NO_APP_GROUP_PLIST_ERROR)") + } } override func isContentValid() -> Bool { @@ -57,11 +57,11 @@ class ShareViewController: SLComposeServiceViewController { 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) + guard let groupId = self.groupId else { + self.exit(withError: NO_APP_GROUP_PLIST_ERROR) return } - guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else { + guard let userDefaults = UserDefaults(suiteName: groupId) else { self.exit(withError: NO_APP_GROUP_ERROR) return } @@ -73,7 +73,6 @@ class ShareViewController: SLComposeServiceViewController { } let semaphore = DispatchSemaphore(value: 0) - var results: [Any] = [] for item in items { guard let attachments = item.attachments else { @@ -103,11 +102,11 @@ class ShareViewController: SLComposeServiceViewController { } func storeExtraData(_ data: [String:Any]) { - guard let hostAppId = self.hostAppId else { - print("Error: \(NO_INFO_PLIST_INDENTIFIER_ERROR)") + guard let groupId = self.groupId else { + print("Error: \(NO_APP_GROUP_PLIST_ERROR)") return } - guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else { + guard let userDefaults = UserDefaults(suiteName: groupId) else { print("Error: \(NO_APP_GROUP_ERROR)") return } @@ -116,11 +115,11 @@ class ShareViewController: SLComposeServiceViewController { } func removeExtraData() { - guard let hostAppId = self.hostAppId else { - print("Error: \(NO_INFO_PLIST_INDENTIFIER_ERROR)") + guard let groupId = self.groupId else { + print("Error: \(NO_APP_GROUP_PLIST_ERROR)") return } - guard let userDefaults = UserDefaults(suiteName: "group.\(hostAppId)") else { + guard let userDefaults = UserDefaults(suiteName: groupId) else { print("Error: \(NO_APP_GROUP_ERROR)") return } @@ -166,16 +165,48 @@ 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 + + var url: URL! = nil + var image: UIImage! = nil + + if data is UIImage { + image = data as? UIImage + } + + if data is Data { + image = UIImage(data: data as! Data)! } - guard let hostAppId = self.hostAppId else { - self.exit(withError: NO_INFO_PLIST_INDENTIFIER_ERROR) + + 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 { + self.exit(withError: COULD_NOT_GET_IMAGE_FILE_LOCATION) + return + } + + do { + // Writing the image to the URL + try imageData.write(to: imageURL) + url = imageURL + } catch { + self.exit(withError: COULD_NOT_WRITE_IMAGE_DATA) + } + } else { + guard let toUrl = data as? URL else { + self.exit(withError: COULD_NOT_TURN_DATA_TO_URL) + return + } + url = toUrl; + } + + guard let groupId = self.groupId else { + self.exit(withError: NO_APP_GROUP_PLIST_ERROR) return } guard let groupFileManagerContainer = FileManager.default - .containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppId)") + .containerURL(forSecurityApplicationGroupIdentifier: groupId) else { self.exit(withError: NO_APP_GROUP_ERROR) return @@ -193,6 +224,7 @@ class ShareViewController: SLComposeServiceViewController { } self.sharedItems.append([DATA_KEY: filePath.absoluteString, MIME_TYPE_KEY: mimeType]) + semaphore.signal() } }