{"id":15550043,"url":"https://github.com/outdatedguy/receive_sharing_intent_plus","last_synced_at":"2025-10-23T02:10:25.346Z","repository":{"id":211771602,"uuid":"729897763","full_name":"OutdatedGuy/receive_sharing_intent_plus","owner":"OutdatedGuy","description":"Unlock seamless content sharing in your Flutter apps with text, photos, and URLs - one plugin away.","archived":true,"fork":false,"pushed_at":"2024-12-12T21:04:32.000Z","size":87,"stargazers_count":6,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-13T21:11:20.528Z","etag":null,"topics":["flutter-plugin","receive-intent","share-intent"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/receive_sharing_intent_plus","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OutdatedGuy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["OutdatedGuy"]}},"created_at":"2023-12-10T17:32:04.000Z","updated_at":"2024-12-12T21:11:42.000Z","dependencies_parsed_at":"2025-02-17T13:37:11.556Z","dependency_job_id":"397ba89f-f9f2-414c-89a7-2ee1a78dfd41","html_url":"https://github.com/OutdatedGuy/receive_sharing_intent_plus","commit_stats":null,"previous_names":["outdatedguy/receive_sharing_intent_plus"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/OutdatedGuy/receive_sharing_intent_plus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OutdatedGuy%2Freceive_sharing_intent_plus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OutdatedGuy%2Freceive_sharing_intent_plus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OutdatedGuy%2Freceive_sharing_intent_plus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OutdatedGuy%2Freceive_sharing_intent_plus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OutdatedGuy","download_url":"https://codeload.github.com/OutdatedGuy/receive_sharing_intent_plus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OutdatedGuy%2Freceive_sharing_intent_plus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280402323,"owners_count":26324607,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-22T02:00:06.515Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flutter-plugin","receive-intent","share-intent"],"created_at":"2024-10-02T13:48:08.006Z","updated_at":"2025-10-23T02:10:25.033Z","avatar_url":"https://github.com/OutdatedGuy.png","language":"Swift","readme":"\u003e [!CAUTION]\n\u003e\n\u003e This plugin is no longer maintained, please use the [receive_sharing_intent](https://pub.dev/packages/receive_sharing_intent) plugin instead.\n\n# Receive Sharing Intent Plus\n\nA Flutter plugin to Unlock seamless content sharing in your apps with text,\nphotos, and URLs.\n\n[![pub package][package_svg]][package]\n[![GitHub][license_svg]](LICENSE)\n\n[![GitHub issues][issues_svg]][issues]\n[![GitHub issues closed][issues_closed_svg]][issues_closed]\n\n\u003chr /\u003e\n\nThis plugin provides functionality to receive images, videos, files, text and\nurls from other apps.\n\n|    Android     |    iOS     |\n| :------------: | :--------: |\n| ![Android Demo] | ![iOS Demo] |\n\n## Features\n\n- Open app when any data shared from other apps\n- Listen to shared data when app is opened as a stream\n\n## Supported Platforms\n\n| Platform | Open App | Listen for Shared Data |\n| :------: | :------: | :--------------------: |\n| Android  |    ✅    |           ✅           |\n|   iOS    |    ✅    |           ✅           |\n\n## Getting Started\n\n## Setup (Android)\n\n### 1. Get External Storage Permission\n\nAdd the following to your `AndroidManifest.xml` inside `\u003cmanifest\u003e` if you wish\nto access shared files:\n\n```xml\n\u003cuses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" /\u003e\n```\n\n### 2. Add data specific intent filters\n\nAdd the following to your `AndroidManifest.xml` inside **main** `\u003cactivity\u003e`.\n\nEach intent filter mentioned below allows your app to receive data of a specific\ntype.\n\n```xml\n\u003c!-- TODO:  Add this filter, if you want support opening urls into your app --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.VIEW\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.BROWSABLE\" /\u003e\n    \u003cdata\n        android:scheme=\"https\"\n        android:host=\"example.com\"\n        android:pathPrefix=\"/invite\"/\u003e\n\u003c/intent-filter\u003e\n\n\u003c!-- TODO: Add this filter, if you want to support sharing text into your app --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.SEND\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003cdata android:mimeType=\"text/*\" /\u003e\n\u003c/intent-filter\u003e\n\n\u003c!-- TODO: Add this filter, if you want to support sharing single image at once --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.SEND\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003cdata android:mimeType=\"image/*\" /\u003e\n\u003c/intent-filter\u003e\n\n\u003c!-- TODO: Add this filter, if you want to support sharing multiple images at once --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.SEND_MULTIPLE\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003cdata android:mimeType=\"image/*\" /\u003e\n\u003c/intent-filter\u003e\n\n\u003c!-- TODO: Add this filter, if you want to support sharing single video at once --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.SEND\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003cdata android:mimeType=\"video/*\" /\u003e\n\u003c/intent-filter\u003e\n\n\u003c!-- TODO: Add this filter, if you want to support sharing multiple videos at once --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.SEND_MULTIPLE\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003cdata android:mimeType=\"video/*\" /\u003e\n\u003c/intent-filter\u003e\n\n\u003c!-- TODO: Add this filter, if you want to support sharing any single file at once --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.SEND\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003cdata android:mimeType=\"*/*\" /\u003e\n\u003c/intent-filter\u003e\n\n\u003c!-- TODO: Add this filter, if you want to support sharing multiple files at once --\u003e\n\u003cintent-filter\u003e\n    \u003caction android:name=\"android.intent.action.SEND_MULTIPLE\" /\u003e\n    \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n    \u003cdata android:mimeType=\"*/*\" /\u003e\n\u003c/intent-filter\u003e\n```\n\n\u003e If you wish to open urls into your app, see [Android App Links] to know more\n\u003e about opening urls/deep-links into your android app.\n\n### 3. Optional Activity Configuration\n\nUpdate the `android:launchMode` attribute of the **main** `\u003cactivity\u003e` inside\n`AndroidManifest.xml` to `singleTask` if you want to prevent creating new\nactivity instance everytime there is a new data shared.\n\n## Setup (iOS)\n\nThis is long and complicated process. Please follow the steps carefully.\n\n### 1. Update Info.plist\n\nAdd following inside `ios/Runner/info.plist`\n\n```xml\n\u003ckey\u003eAppGroupId\u003c/key\u003e\n\u003cstring\u003e$(CUSTOM_GROUP_ID)\u003c/string\u003e\n\u003ckey\u003eCFBundleURLTypes\u003c/key\u003e\n\u003carray\u003e\n  \u003cdict\u003e\n    \u003ckey\u003eCFBundleTypeRole\u003c/key\u003e\n    \u003cstring\u003eEditor\u003c/string\u003e\n    \u003ckey\u003eCFBundleURLSchemes\u003c/key\u003e\n    \u003carray\u003e\n      \u003cstring\u003eShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)\u003c/string\u003e\n    \u003c/array\u003e\n  \u003c/dict\u003e\n\u003c/array\u003e\n\u003ckey\u003eNSPhotoLibraryUsageDescription\u003c/key\u003e\n\u003cstring\u003eTo upload photos, please allow permission to access your photo library.\u003c/string\u003e\n```\n\n### 2. Create Share Extension Target\n\n1. Using XCode, go to File -\u003e New -\u003e Target and Choose `Share Extension`\n1. Give it a name i.e. \"Share Extension\"\n1. Choose language as `Swift`\n\n**Note:** Make sure the `iOS Deployment Target` is **SAME** for both the\n`Runner` and `Share Extension` targets.\n\n### 3. Add Runner and Share Extension in the same group\n\n1. Select the `Runner` target and go to the `Signing \u0026 Capabilities` tab.\n\n1. Click on the `+ Capability` button and add the `App Groups` capability.\n\n1. Add a new group and name it as you want. For example\n   `group.YOUR_HOST_APP_BUNDLE_IDENTIFIER` in my case\n   `group.rocks.outdatedguy.receiveSharingIntentPlusExample`.\n\n1. Do the same for the `Share Extension` target.\n\nThis will allow both the targets to share data with each other.\n\n### 4. Add User-Defined Settings\n\n1. Select the `Runner` target and go to the `Build Settings` tab.\n\n1. Click on the `+` button and add a new `User-Defined Setting`.\n\n1. Name it as `CUSTOM_GROUP_ID` and set the value defined in **Step 3** (Above Step).\n\n1. Do the same for the `Share Extension` target.\n\n### 5. Configure Share Extension Target Info.plist\n\nUpdate the `ios/Share Extension/info.plist` with the code below.\n\nRead the comments to understand what each key does and what you need to change.\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003c!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\u003e\n\u003cplist version=\"1.0\"\u003e\n\u003cdict\u003e\n  \u003ckey\u003eAppGroupId\u003c/key\u003e\n  \u003cstring\u003e$(CUSTOM_GROUP_ID)\u003c/string\u003e\n  \u003ckey\u003eNSExtension\u003c/key\u003e\n  \u003cdict\u003e\n    \u003ckey\u003eNSExtensionAttributes\u003c/key\u003e\n    \u003cdict\u003e\n      \u003ckey\u003ePHSupportedMediaTypes\u003c/key\u003e\n      \u003carray\u003e\n        \u003c!-- TODO: Add this flag, if you want to support sharing video into your app --\u003e\n        \u003cstring\u003eVideo\u003c/string\u003e\n        \u003c!-- TODO: Add this flag, if you want to support sharing images into your app --\u003e\n        \u003cstring\u003eImage\u003c/string\u003e\n      \u003c/array\u003e\n      \u003ckey\u003eNSExtensionActivationRule\u003c/key\u003e\n      \u003cdict\u003e\n        \u003c!-- TODO: Add this flag, if you want to support sharing text into your app --\u003e\n        \u003ckey\u003eNSExtensionActivationSupportsText\u003c/key\u003e\n        \u003ctrue/\u003e\n        \u003c!-- TODO: Add this tag, if you want to support sharing urls into your app --\u003e\n        \u003ckey\u003eNSExtensionActivationSupportsWebURLWithMaxCount\u003c/key\u003e\n        \u003cinteger\u003e1\u003c/integer\u003e\n        \u003c!-- TODO: Add this flag, if you want to support sharing images into your app --\u003e\n        \u003ckey\u003eNSExtensionActivationSupportsImageWithMaxCount\u003c/key\u003e\n        \u003cinteger\u003e100\u003c/integer\u003e\n        \u003c!-- TODO: Add this flag, if you want to support sharing video into your app --\u003e\n        \u003ckey\u003eNSExtensionActivationSupportsMovieWithMaxCount\u003c/key\u003e\n        \u003cinteger\u003e100\u003c/integer\u003e\n        \u003c!-- TODO: Add this flag, if you want to support sharing other files into your app --\u003e\n        \u003c!-- TODO: Change the integer to however many files you want to be able to share at a time --\u003e\n        \u003ckey\u003eNSExtensionActivationSupportsFileWithMaxCount\u003c/key\u003e\n        \u003cinteger\u003e1\u003c/integer\u003e\n      \u003c/dict\u003e\n    \u003c/dict\u003e\n    \u003ckey\u003eNSExtensionMainStoryboard\u003c/key\u003e\n    \u003cstring\u003eMainInterface\u003c/string\u003e\n    \u003ckey\u003eNSExtensionPointIdentifier\u003c/key\u003e\n    \u003cstring\u003ecom.apple.share-services\u003c/string\u003e\n  \u003c/dict\u003e\n\u003c/dict\u003e\n\u003c/plist\u003e\n\n```\n\nIf you wish to support opening urls into your app, add the following to the\n`ios/Runner/Runner.entitlements` file.\n\nSee [iOS Universal Links] to know more about opening urls/deep-links into your\nios app.\n\n```xml\n\u003c!--TODO:  Add this tag, if you want support opening urls into your app--\u003e\n\u003ckey\u003ecom.apple.developer.associated-domains\u003c/key\u003e\n\u003carray\u003e\n  \u003cstring\u003eapplinks:example.com\u003c/string\u003e\n\u003c/array\u003e\n```\n\n### 6. Configure Share Extension Target Working\n\nUpdate the whole `ios/Share Extension/ShareViewController.swift` file with the\ncode below.\n\n```swift\nimport UIKit\nimport Social\nimport MobileCoreServices\nimport Photos\n\nclass ShareViewController: SLComposeServiceViewController {\n    var hostAppBundleIdentifier = \"\"\n    var appGroupId = \"\"\n    let sharedKey = \"ShareKey\"\n    var sharedMedia: [SharedMediaFile] = []\n    var sharedText: [String] = []\n    let imageContentType = kUTTypeImage as String\n    let videoContentType = kUTTypeMovie as String\n    let textContentType = kUTTypeText as String\n    let urlContentType = kUTTypeURL as String\n    let fileURLType = kUTTypeFileURL as String\n\n    override func isContentValid() -\u003e Bool {\n        return true\n    }\n\n    private func loadIds() {\n        // loading Share extension App Id\n        let shareExtensionAppBundleIdentifier = Bundle.main.bundleIdentifier!\n\n        // convert ShareExtension id to host app id\n        // By default it is removed the last part of id after the last point\n        // For example: com.test.ShareExtension -\u003e com.test\n        if let lastIndexOfPoint = shareExtensionAppBundleIdentifier.lastIndex(of: \".\") {\n            hostAppBundleIdentifier = String(shareExtensionAppBundleIdentifier[..\u003clastIndexOfPoint])\n        }\n\n        // loading custom AppGroupId from Build Settings or use group.\u003chostAppBundleIdentifier\u003e\n        appGroupId = (Bundle.main.object(forInfoDictionaryKey: \"AppGroupId\") as? String) ?? \"group.\\(hostAppBundleIdentifier)\"\n    }\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        // load group and app id from build info\n        loadIds()\n    }\n\n    override func viewDidAppear(_ animated: Bool) {\n        super.viewDidAppear(animated)\n\n        // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.\n        if let content = extensionContext?.inputItems[0] as? NSExtensionItem {\n            if let contents = content.attachments {\n                for (index, attachment) in contents.enumerated() {\n                    if attachment.hasItemConformingToTypeIdentifier(imageContentType) {\n                        handleImages(content: content, attachment: attachment, index: index)\n                    } else if attachment.hasItemConformingToTypeIdentifier(textContentType) {\n                        handleText(content: content, attachment: attachment, index: index)\n                    } else if attachment.hasItemConformingToTypeIdentifier(fileURLType) {\n                        handleFiles(content: content, attachment: attachment, index: index)\n                    } else if attachment.hasItemConformingToTypeIdentifier(urlContentType) {\n                        handleUrl(content: content, attachment: attachment, index: index)\n                    } else if attachment.hasItemConformingToTypeIdentifier(videoContentType) {\n                        handleVideos(content: content, attachment: attachment, index: index)\n                    }\n                }\n            }\n        }\n    }\n\n    override func didSelectPost() {\n        print(\"didSelectPost\")\n    }\n\n    override func configurationItems() -\u003e [Any]! {\n        // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.\n        return []\n    }\n\n    private func handleText(content: NSExtensionItem, attachment: NSItemProvider, index: Int) {\n        attachment.loadItem(forTypeIdentifier: textContentType, options: nil) { [weak self] data, error in\n\n            if error == nil, let item = data as? String, let this = self {\n\n                this.sharedText.append(item)\n\n                // If this is the last item, save imagesData in userDefaults and redirect to the host app\n                if index == (content.attachments?.count)! - 1 {\n                    let userDefaults = UserDefaults(suiteName: this.appGroupId)\n                    userDefaults?.set(this.sharedText, forKey: this.sharedKey)\n                    userDefaults?.synchronize()\n                    this.redirectToHostApp(type: .text)\n                }\n\n            } else {\n                self?.dismissWithError()\n            }\n        }\n    }\n\n    private func handleUrl(content: NSExtensionItem, attachment: NSItemProvider, index: Int) {\n        attachment.loadItem(forTypeIdentifier: urlContentType, options: nil) { [weak self] data, error in\n\n            if error == nil, let item = data as? URL, let this = self {\n\n                this.sharedText.append(item.absoluteString)\n\n                // If this is the last item, save imagesData in userDefaults and redirect to the host app\n                if index == (content.attachments?.count)! - 1 {\n                    let userDefaults = UserDefaults(suiteName: this.appGroupId)\n                    userDefaults?.set(this.sharedText, forKey: this.sharedKey)\n                    userDefaults?.synchronize()\n                    this.redirectToHostApp(type: .text)\n                }\n\n            } else {\n                self?.dismissWithError()\n            }\n        }\n    }\n\n    private func handleImages(content: NSExtensionItem, attachment: NSItemProvider, index: Int) {\n        attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in\n\n            if error == nil, let url = data as? URL, let this = self {\n\n                // Always copy\n                let fileName = this.getFileName(from: url, type: .image)\n                let newPath = FileManager.default\n                    .containerURL(forSecurityApplicationGroupIdentifier: this.appGroupId)!\n                    .appendingPathComponent(fileName)\n                let copied = this.copyFile(at: url, to: newPath)\n                if copied {\n                    this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .image))\n                }\n\n                // If this is the last item, save imagesData in userDefaults and redirect to the host app\n                if index == (content.attachments?.count)! - 1 {\n                    let userDefaults = UserDefaults(suiteName: this.appGroupId)\n                    userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)\n                    userDefaults?.synchronize()\n                    this.redirectToHostApp(type: .media)\n                }\n\n            } else {\n                self?.dismissWithError()\n            }\n        }\n    }\n\n    private func handleVideos(content: NSExtensionItem, attachment: NSItemProvider, index: Int) {\n        attachment.loadItem(forTypeIdentifier: videoContentType, options: nil) { [weak self] data, error in\n\n            if error == nil, let url = data as? URL, let this = self {\n\n                // Always copy\n                let fileName = this.getFileName(from: url, type: .video)\n                let newPath = FileManager.default\n                    .containerURL(forSecurityApplicationGroupIdentifier: this.appGroupId)!\n                    .appendingPathComponent(fileName)\n                let copied = this.copyFile(at: url, to: newPath)\n                if copied {\n                    guard let sharedFile = this.getSharedMediaFile(forVideo: newPath) else {\n                        return\n                    }\n                    this.sharedMedia.append(sharedFile)\n                }\n\n                // If this is the last item, save imagesData in userDefaults and redirect to the host app\n                if index == (content.attachments?.count)! - 1 {\n                    let userDefaults = UserDefaults(suiteName: this.appGroupId)\n                    userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)\n                    userDefaults?.synchronize()\n                    this.redirectToHostApp(type: .media)\n                }\n\n            } else {\n                self?.dismissWithError()\n            }\n        }\n    }\n\n    private func handleFiles(content: NSExtensionItem, attachment: NSItemProvider, index: Int) {\n        attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in\n\n            if error == nil, let url = data as? URL, let this = self {\n\n                // Always copy\n                let fileName = this.getFileName(from: url, type: .file)\n                let newPath = FileManager.default\n                    .containerURL(forSecurityApplicationGroupIdentifier: this.appGroupId)!\n                    .appendingPathComponent(fileName)\n                let copied = this.copyFile(at: url, to: newPath)\n                if copied {\n                    this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file))\n                }\n\n                if index == (content.attachments?.count)! - 1 {\n                    let userDefaults = UserDefaults(suiteName: this.appGroupId)\n                    userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)\n                    userDefaults?.synchronize()\n                    this.redirectToHostApp(type: .file)\n                }\n\n            } else {\n                self?.dismissWithError()\n            }\n        }\n    }\n\n    private func dismissWithError() {\n        print(\"[ERROR] Error loading data!\")\n        let alert = UIAlertController(title: \"Error\", message: \"Error loading data\", preferredStyle: .alert)\n\n        let action = UIAlertAction(title: \"Error\", style: .cancel) { _ in\n            self.dismiss(animated: true, completion: nil)\n        }\n\n        alert.addAction(action)\n        present(alert, animated: true, completion: nil)\n        extensionContext?.completeRequest(returningItems: [], completionHandler: nil)\n    }\n\n    private func redirectToHostApp(type: RedirectType) {\n        // ids may not be loaded yet so we need loadIds here too\n        loadIds()\n        let url = URL(string: \"ShareMedia-\\(hostAppBundleIdentifier)://dataUrl=\\(sharedKey)#\\(type)\")\n        var responder = self as UIResponder?\n        let selectorOpenURL = sel_registerName(\"openURL:\")\n\n        while (responder != nil) {\n            if (responder?.responds(to: selectorOpenURL))! {\n                let _ = responder?.perform(selectorOpenURL, with: url)\n            }\n            responder = responder?.next\n        }\n        extensionContext?.completeRequest(returningItems: [], completionHandler: nil)\n    }\n\n    enum RedirectType {\n        case media\n        case text\n        case file\n    }\n\n    func getExtension(from url: URL, type: SharedMediaType) -\u003e String {\n        let parts = url.lastPathComponent.components(separatedBy: \".\")\n        var ex: String? = nil\n        if (parts.count \u003e 1) {\n            ex = parts.last\n        }\n\n        if (ex == nil) {\n            switch type {\n            case .image:\n                ex = \"PNG\"\n            case .video:\n                ex = \"MP4\"\n            case .file:\n                ex = \"TXT\"\n            }\n        }\n        return ex ?? \"Unknown\"\n    }\n\n    func getFileName(from url: URL, type: SharedMediaType) -\u003e String {\n        var name = url.lastPathComponent\n\n        if (name.isEmpty) {\n            name = UUID().uuidString + \".\" + getExtension(from: url, type: type)\n        }\n\n        return name\n    }\n\n    func copyFile(at srcURL: URL, to dstURL: URL) -\u003e Bool {\n        do {\n            if FileManager.default.fileExists(atPath: dstURL.path) {\n                try FileManager.default.removeItem(at: dstURL)\n            }\n            try FileManager.default.copyItem(at: srcURL, to: dstURL)\n        } catch (let error) {\n            print(\"Cannot copy item at \\(srcURL) to \\(dstURL): \\(error)\")\n            return false\n        }\n        return true\n    }\n\n    private func getSharedMediaFile(forVideo: URL) -\u003e SharedMediaFile? {\n        let asset = AVAsset(url: forVideo)\n        let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()\n        let thumbnailPath = getThumbnailPath(for: forVideo)\n\n        if FileManager.default.fileExists(atPath: thumbnailPath.path) {\n            return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video)\n        }\n\n        var saved = false\n        let assetImgGenerate = AVAssetImageGenerator(asset: asset)\n        assetImgGenerate.appliesPreferredTrackTransform = true\n        assetImgGenerate.maximumSize =  CGSize(width: 360, height: 360)\n        do {\n            let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil)\n            try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath)\n            saved = true\n        } catch {\n            saved = false\n        }\n\n        return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil\n    }\n\n    private func getThumbnailPath(for url: URL) -\u003e URL {\n        let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: \"==\", with: \"\")\n        let path = FileManager.default\n            .containerURL(forSecurityApplicationGroupIdentifier: appGroupId)!\n            .appendingPathComponent(\"\\(fileName).jpg\")\n        return path\n    }\n\n    class SharedMediaFile: Codable {\n        var path: String\n        var thumbnail: String?\n        var duration: Double?\n        var type: SharedMediaType\n\n        init(path: String, thumbnail: String?, duration: Double?, type: SharedMediaType) {\n            self.path = path\n            self.thumbnail = thumbnail\n            self.duration = duration\n            self.type = type\n        }\n\n        func toString() {\n            print(\"[SharedMediaFile] \\n\\tpath: \\(self.path)\\n\\tthumbnail: \\(self.thumbnail)\\n\\tduration: \\(self.duration)\\n\\ttype: \\(self.type)\")\n        }\n    }\n\n    enum SharedMediaType: Int, Codable {\n        case image\n        case video\n        case file\n    }\n\n    func toData(data: [SharedMediaFile]) -\u003e Data {\n        let encodedData = try? JSONEncoder().encode(data)\n        return encodedData ?? Data()\n    }\n}\n\nextension Array {\n    subscript (safe index: UInt) -\u003e Element? {\n        return Int(index) \u003c count ? self[Int(index)] : nil\n    }\n}\n\n```\n\n### 7. Update Build Phases\n\nTo avoid below error, select the `Runner` target and go to the `Build Phases`\ntab. Then drag and move the `Embed Foundation Extensions` phase above the\n`Thin Binary` phase.\n\n**Error:**\n\n```\nError (Xcode): Cycle inside Runner; building could produce unreliable results.\nCycle details:\n→ Target 'Runner': ExtractAppIntentsMetadata\n○ Target 'Runner' has copy command from 'receive_sharing_intent_plus/example/build/ios/Debug-iphonesimulator/Share Extension.appex' to 'receive_sharing_intent_plus/example/build/ios/Debug-iphonesimulator/Runner.app/PlugIns/Share Extension.appex'\n○ That command depends on command in Target 'Runner': script phase “Thin Binary”\n○ Target 'Runner' has process command with output 'receive_sharing_intent_plus/example/build/ios/Debug-iphonesimulator/Runner.app/Info.plist'\n○ Target 'Runner' has copy command from 'receive_sharing_intent_plus/example/build/ios/Debug-iphonesimulator/Share Extension.appex' to 'receive_sharing_intent_plus/example/build/ios/Debug-iphonesimulator/Runner.app/PlugIns/Share Extension.appex'\n```\n\n## Usage\n\n### 1. Add dependency\n\nAdd the `receive_sharing_intent_plus` package to your `pubspec.yaml` file:\n\n```yaml\ndependencies:\n  receive_sharing_intent_plus: ^1.0.1\n```\n\n### 2. Import the package\n\nImport the `receive_sharing_intent_plus` package into your Dart file:\n\n```dart\nimport 'package:receive_sharing_intent_plus/receive_sharing_intent_plus.dart';\n```\n\n### 3. Checking if the app was opened from a shared content\n\n```dart\n// For sharing images coming from outside the app while the app is closed\nReceiveSharingIntentPlus.getInitialMedia().then(\n  (List\u003cSharedMediaFile\u003e value) {\n    setState(() {\n      _sharedFiles = value;\n      debugPrint(\n        'Shared:${_sharedFiles?.map((f) =\u003e f.path).join(',') ?? ''}',\n      );\n    });\n  },\n);\n```\n\nOR\n\n```dart\n// For sharing or opening urls/text coming from outside the app while the app is closed\nReceiveSharingIntentPlus.getInitialText().then((String? value) {\n  setState(() {\n    _sharedText = value;\n    debugPrint('Shared: $_sharedText');\n  });\n});\n```\n\n### 4. Listening for shared content while the app is opened\n\n```dart\n// For shared images coming from outside the app while the app is in the memory\n_intentMediaStreamSubscription = ReceiveSharingIntentPlus.getMediaStream().listen(\n  (List\u003cSharedMediaFile\u003e value) {\n    setState(() {\n      _sharedFiles = value;\n      debugPrint(\n        'Shared:${_sharedFiles?.map((f) =\u003e f.path).join(',') ?? ''}',\n      );\n    });\n  },\n  onError: (err) {\n    debugPrint('getIntentDataStream error: $err');\n  },\n);\n```\n\nOR\n\n```dart\n// For shared text or opening urls coming from outside the app while the app is\n// in the memory\n_intentTextStreamSubscription = ReceiveSharingIntentPlus.getTextStream().listen(\n  (String value) {\n    setState(() {\n      _sharedText = value;\n      debugPrint('Shared: $_sharedText');\n    });\n  },\n  onError: (err) {\n    debugPrint('getLinkStream error: $err');\n  },\n);\n\n```\n\nDon't forget to cancel the subscription when it is no longer needed.\nThis will prevent memory leaks and free up resources:\n\n```dart\n_intentMediaStreamSubscription.cancel();\n_intentTextStreamSubscription.cancel();\n```\n\n## Credits\n\nThis package is a cloned and modified version of the [receive_sharing_intent]\npackage which is no longer maintained.\n\nThe aim of this package is to support the latest version of Flutter and fix\niOS sharing issues with the original package.\n\n\u003c!-- Badges URLs --\u003e\n\n[package_svg]: https://img.shields.io/pub/v/receive_sharing_intent_plus.svg?color=blueviolet\n[license_svg]: https://img.shields.io/github/license/OutdatedGuy/receive_sharing_intent_plus.svg?color=purple\n[issues_svg]: https://img.shields.io/github/issues/OutdatedGuy/receive_sharing_intent_plus.svg\n[issues_closed_svg]: https://img.shields.io/github/issues-closed/OutdatedGuy/receive_sharing_intent_plus.svg?color=green\n\n\u003c!-- Links --\u003e\n\n[Android App Links]: https://docs.flutter.dev/cookbook/navigation/set-up-app-links\n[iOS Universal Links]: https://docs.flutter.dev/cookbook/navigation/set-up-universal-links\n[Android Demo]: https://github.com/OutdatedGuy/receive_sharing_intent_plus/assets/74326345/fe8acb70-b8b9-42a2-9053-15cec38495ab\n[iOS Demo]: https://github.com/OutdatedGuy/receive_sharing_intent_plus/assets/74326345/54f02244-e67f-48e3-bdad-bf61111c0e1a\n[package]: https://pub.dev/packages/receive_sharing_intent_plus\n[issues]: https://github.com/OutdatedGuy/receive_sharing_intent_plus/issues\n[issues_closed]: https://github.com/OutdatedGuy/receive_sharing_intent_plus/issues?q=is%3Aissue+is%3Aclosed\n[receive_sharing_intent]: https://github.com/KasemJaffer/receive_sharing_intent\n","funding_links":["https://github.com/sponsors/OutdatedGuy"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutdatedguy%2Freceive_sharing_intent_plus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutdatedguy%2Freceive_sharing_intent_plus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutdatedguy%2Freceive_sharing_intent_plus/lists"}