diff --git a/README.md b/README.md index 2539a77..0d98fd7 100644 --- a/README.md +++ b/README.md @@ -123,9 +123,9 @@ Code for the ShareViewController: // Created by Carsten Klaffke on 05.07.20. // -import UIKit -import Social import MobileCoreServices +import Social +import UIKit class ShareItem { @@ -139,34 +139,49 @@ class ShareViewController: UIViewController { private var shareItems: [ShareItem] = [] override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) + super.viewDidAppear(animated) + self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) } private func sendData() { - let queryItems = shareItems.map { - [URLQueryItem(name: "title", value: $0.title?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""), - URLQueryItem(name: "description", value: ""), - URLQueryItem(name: "type", value: $0.type?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""), - URLQueryItem(name: "url", value: $0.url?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")] + let queryItems = shareItems.map { + [ + URLQueryItem( + name: "title", + value: $0.title?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""), + URLQueryItem(name: "description", value: ""), + URLQueryItem( + name: "type", + value: $0.type?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""), + URLQueryItem( + name: "url", + value: $0.url?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""), + ] }.flatMap({ $0 }) - - var urlComps = URLComponents(string: "mindlib://")! + var urlComps = URLComponents(string: "YOUR_APP_URL_SCHEME://")! urlComps.queryItems = queryItems openURL(urlComps.url!) } fileprivate func createSharedFileUrl(_ url: URL?) -> String { let fileManager = FileManager.default - let copyFileUrl = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.de.mindlib")!.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + "/" + url!.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + + let copyFileUrl = + fileManager.containerURL(forSecurityApplicationGroupIdentifier: "YOUR_APP_GROUP_ID")! + .absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + url! + .lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! try? Data(contentsOf: url!).write(to: URL(string: copyFileUrl)!) return copyFileUrl } - func saveScreenshot(_ image: UIImage) -> String { + func saveScreenshot(_ image: UIImage, _ index: Int) -> String { let fileManager = FileManager.default - let copyFileUrl = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.de.mindlib")!.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + "/screenshot.png" + + let copyFileUrl = + fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.SendIntentExample")! + .absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + + "/screenshot_\(index).png" do { try image.pngData()?.write(to: URL(string: copyFileUrl)!) return copyFileUrl @@ -176,88 +191,109 @@ class ShareViewController: UIViewController { } } + fileprivate func handleTypeUrl(_ attachment: NSItemProvider) + async throws -> ShareItem + { + let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) + let url = results as! URL? + let shareItem: ShareItem = ShareItem() + + if url!.isFileURL { + shareItem.title = url!.lastPathComponent + shareItem.type = "application/" + url!.pathExtension.lowercased() + shareItem.url = createSharedFileUrl(url) + } else { + shareItem.title = url!.absoluteString + shareItem.url = url!.absoluteString + shareItem.type = "text/plain" + } + + return shareItem + } + + fileprivate func handleTypeText(_ attachment: NSItemProvider) + async throws -> ShareItem + { + let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil) + let shareItem: ShareItem = ShareItem() + let text = results as! String + shareItem.title = text + shareItem.type = "text/plain" + return shareItem + } + + fileprivate func handleTypeMovie(_ attachment: NSItemProvider) + async throws -> ShareItem + { + let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeMovie as String, options: nil) + let shareItem: ShareItem = ShareItem() + + let url = results as! URL? + shareItem.title = url!.lastPathComponent + shareItem.type = "video/" + url!.pathExtension.lowercased() + shareItem.url = createSharedFileUrl(url) + return shareItem + } + + fileprivate func handleTypeImage(_ attachment: NSItemProvider, _ index: Int) + async throws -> ShareItem + { + let data = try await attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) + + let shareItem: ShareItem = ShareItem() + switch data { + case let image as UIImage: + shareItem.title = "screenshot_\(index)" + shareItem.type = "image/png" + shareItem.url = self.saveScreenshot(image, index) + case let url as URL: + shareItem.title = url.lastPathComponent + shareItem.type = "image/" + url.pathExtension.lowercased() + shareItem.url = self.createSharedFileUrl(url) + default: + print("Unexpected image data:", type(of: data)) + } + return shareItem + } + override public func viewDidLoad() { super.viewDidLoad() shareItems.removeAll() let extensionItem = extensionContext?.inputItems[0] as! NSExtensionItem - let contentTypeURL = kUTTypeURL as String - let contentTypeText = kUTTypeText as String - let contentTypeMovie = kUTTypeMovie as String - let contentTypeImage = kUTTypeImage as String - - for attachment in extensionItem.attachments as! [NSItemProvider] { - - if attachment.hasItemConformingToTypeIdentifier(contentTypeURL) { - attachment.loadItem(forTypeIdentifier: contentTypeURL, options: nil, completionHandler: { [self] (results, error) in - guard let url = results as? URL else { - print("Expected URL but received: \(String(describing: results))") - return - } - let shareItem = ShareItem() + Task { + try await withThrowingTaskGroup( + of: ShareItem.self, + body: { taskGroup in - if url.isFileURL { - shareItem.title = url.lastPathComponent - shareItem.type = "application/" + url.pathExtension.lowercased() - shareItem.url = createSharedFileUrl(url) - } else { - shareItem.title = url.absoluteString - shareItem.url = url.absoluteString - shareItem.type = "text/plain" + for (index, attachment) in extensionItem.attachments!.enumerated() { + if attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String) { + taskGroup.addTask { + return try await self.handleTypeUrl(attachment) + } + } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeText as String) { + taskGroup.addTask { + return try await self.handleTypeText(attachment) + } + } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) { + taskGroup.addTask { + return try await self.handleTypeMovie(attachment) + } + } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) { + taskGroup.addTask { + return try await self.handleTypeImage(attachment, index) + } + } } - self.shareItems.append(shareItem) - self.sendData() - }) - } else if attachment.hasItemConformingToTypeIdentifier(contentTypeText) { - attachment.loadItem(forTypeIdentifier: contentTypeText, options: nil, completionHandler: { (results, error) in - guard let text = results as? String else { - print("Expected String but received: \(String(describing: results))") - return + for try await item in taskGroup { + self.shareItems.append(item) } - let shareItem = ShareItem() - shareItem.title = text - shareItem.type = "text/plain" - self.shareItems.append(shareItem) - self.sendData() }) - } else if attachment.hasItemConformingToTypeIdentifier(contentTypeMovie) { - attachment.loadItem(forTypeIdentifier: contentTypeMovie, options: nil, completionHandler: { [self] (results, error) in - guard let url = results as? URL else { - print("Expected URL for Movie but received: \(String(describing: results))") - return - } - let shareItem = ShareItem() - - shareItem.title = url.lastPathComponent - shareItem.type = "video/" + url.pathExtension.lowercased() - shareItem.url = createSharedFileUrl(url) - - self.shareItems.append(shareItem) - self.sendData() - }) - } else if attachment.hasItemConformingToTypeIdentifier(contentTypeImage) { - attachment.loadItem(forTypeIdentifier: contentTypeImage, options: nil) { (data, error) in - if let image = data as? UIImage { - let shareItem = ShareItem() - shareItem.title = "screenshot" - shareItem.type = "image/png" - shareItem.url = self.saveScreenshot(image) - self.shareItems.append(shareItem) - self.sendData() - } else if let url = data as? URL { - let shareItem = ShareItem() - shareItem.title = url.lastPathComponent - shareItem.type = "image/" + url.pathExtension.lowercased() - shareItem.url = self.createSharedFileUrl(url) - self.shareItems.append(shareItem) - self.sendData() - } else { - print("Unexpected image data: \(type(of: data))") - } - } - } + + self.sendData() + } } @@ -271,9 +307,9 @@ class ShareViewController: UIViewController { } return false } + } - ``` The share extension is like a little standalone program, so to get to your app the extension has to make an openURL call. In order to make your app reachable by a URL, you have to define a URL scheme ([Register Your URL Scheme](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app)). The code above calls a URL scheme named "YOUR_APP_URL_SCHEME" (first line in "didSelectPost"), so just replace this with your scheme!