KasemJaffer / receive_sharing_intent

A Flutter plugin that enables flutter apps to receive sharing photos, text and url from other apps.
Apache License 2.0
321 stars 369 forks source link

DON'T WORK FOR IOS #283

Open lutsenko92 opened 4 months ago

lutsenko92 commented 4 months ago

works well for android. In iOS, it opens a menu, there is my application, when you click on it, it displays a window in which nothing happens after clicking send. Configured as written in the documentation

lutsenko92 commented 4 months ago

log when open menu: [default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] Attempt to map database failed: permission was denied. This attempt will not be retried. [db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] Attempt to map database failed: permission was denied. This attempt will not be retried. [db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] Attempt to map database failed: permission was denied. This attempt will not be retried. [db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] Attempt to map database failed: permission was denied. This attempt will not be retried. [db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] Attempt to map database failed: permission was denied. This attempt will not be retried. [db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] -imageForImageDescriptor: can do IO please adopt -imageForDescriptor: for IO free drawing or -prepareImageForDescriptor: if IO is allowed. (This will become a fault soon.) [default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] Attempt to map database failed: permission was denied. This attempt will not be retried. [db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] LaunchServices: store (null) or url (null) was nil: Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] Attempt to map database failed: permission was denied. This attempt will not be retried. [db] Failed to initialize client context with error Error Domain=NSOSStatusErrorDomain Code=-54 "process may not map database" UserInfo={NSDebugDescription=process may not map database, _LSLine=66, _LSFunction=_LSServer_GetServerStoreForConnectionWithCompletionHandler} [default] -imageForImageDescriptor: can do IO please adopt -imageForDescriptor: for IO free drawing or -prepareImageForDescriptor: if IO is allowed. (This will become a fault soon.) [LayoutConstraints] Changing the translatesAutoresizingMaskIntoConstraints property of a UICollectionReusableView that is managed by a UICollectionView is not supported, and will result in incorrect self-sizing. View: <_UIActivityContentFooterView: 0x1413757f0; baseClass = UICollectionReusableView; frame = (16 457.667; 343 52); layer = <CALayer: 0x281b4eb80>> flutter: 2024-02-27 21:46:15.973937 ℹ️ 🔌 Sending Event: health.check flutter: 2024-02-27 21:46:15.975463 ℹ️ 🔌 Needs to reconnect : false [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (

log after click on my app:

[NSExtension] Extension request contains input items but the extension point does not specify a set of allowed payload classes. The extension point's NSExtensionContext subclass must implement +_allowedItemPayloadClasses. This must return the set of allowed NSExtensionItem payload classes. In future, this request will fail with an error.

and log after click post:

[core] SLRemoteComposeViewController: (this may be harmless) viewServiceDidTerminateWithError: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method} [ShareSheet] connection invalidated

Nathanael-Rayapin commented 4 months ago

Same problem here with the same configurations I can see my application from the gallery share but when I click on it it pretends to want to do something but does nothing in the end.

SergeySor commented 4 months ago

I think I found the problem.

The failure occurs in func decode(data: Data) , in the string representation of this data there is a field "type" with the value int, but in the SharedMediaFile class there is a field "type" with String typed enum, the decode error is "Expected to decode String but found number instead.".

lutsenko92 commented 4 months ago

How fix this?

ivanchaukn commented 4 months ago

It does not work for me too! @KasemJaffer any plans on fixing it?

Screenshot 2024-03-11 at 10 40 53 PM

gbcyasar commented 4 months ago

solution ?

abul-kalam-mari commented 4 months ago

My App doesn't show up in the Share bottom sheet anyone can help me out please it works on Android.

aymanattieh commented 3 months ago

in iOS not working when press share not receiving any thing

erighet commented 3 months ago

Same issue here, error on decode(data) function, this is very frustrating...

YashSavsani commented 3 months ago

works well for android. In iOS, it opens a menu, there is my application, when you click on it, it displays a window in which nothing happens after clicking send. Configured as written in the documentation

I have the same issue. Is it solved ?

erighet commented 3 months ago

works well for android. In iOS, it opens a menu, there is my application, when you click on it, it displays a window in which nothing happens after clicking send. Configured as written in the documentation

I have the same issue. Is it solved ?

i've forked the repo and fixed for my specific needs, and now it works. I'm trying to implement a general working patch and i'll make a PR

https://github.com/erighet/receive_sharing_intent

YashSavsani commented 3 months ago

works well for android. In iOS, it opens a menu, there is my application, when you click on it, it displays a window in which nothing happens after clicking send. Configured as written in the documentation

I have the same issue. Is it solved ?

i've forked the repo and fixed for my specific needs, and now it works. I'm trying to implement a general working patch and i'll make a PR

https://github.com/erighet/receive_sharing_intent

Thanks for the reply. It didn't work for me. I tested using your forked branch.

YashSavsani commented 3 months ago

@KasemJaffer receive_sharing_intent version above 1.4.5 , this package doesn't work for IOS.

erighet commented 3 months ago

works well for android. In iOS, it opens a menu, there is my application, when you click on it, it displays a window in which nothing happens after clicking send. Configured as written in the documentation

I have the same issue. Is it solved ?

i've forked the repo and fixed for my specific needs, and now it works. I'm trying to implement a general working patch and i'll make a PR https://github.com/erighet/receive_sharing_intent

Thanks for the reply. It didn't work for me. I tested using your forked branch.

Should work only for receiving text files, not other types, just double checked

Does it opens your app when you click on the icon in the share center?

fpatron commented 3 months ago

Hi, I have the same issue on ios, it works well on android. I only want to receive text or url, but app isn't opened despite your patch @erighet.

erighet commented 3 months ago

If the app doesn't open, then i think it's a controller problem, no plugin fault. I'm using this code as ShareViewController:

import UIKit
import Social
import MobileCoreServices
import Photos

class ShareViewController: SLComposeServiceViewController {

    var hostAppBundleIdentifier = ""
    var appGroupId = ""
    let sharedKey = "ShareKey"
    var sharedMedia: [SharedMediaFile] = []
    var sharedText: [String] = []
    let fileURLType = kUTTypeFileURL as String;

    override func isContentValid() -> Bool {
        // Do validation of contentText and/or NSExtensionContext attachments here
        return true
    }

    private func loadIds() {
        // loading Share extension App Id
        let shareExtensionAppBundleIdentifier = Bundle.main.bundleIdentifier!;

        // convert ShareExtension id to host app id
        // By default it is remove last part of id after last point
        // For example: com.test.ShareExtension -> com.test
        let lastIndexOfPoint = shareExtensionAppBundleIdentifier.lastIndex(of: ".");
        hostAppBundleIdentifier = String(shareExtensionAppBundleIdentifier[..<lastIndexOfPoint!]);

        // loading custom AppGroupId from Build Settings or use group.<hostAppBundleIdentifier>
        appGroupId = (Bundle.main.object(forInfoDictionaryKey: "AppGroupId") as? String) ?? "group.\(hostAppBundleIdentifier)";
    }

    override func viewDidLoad() {
        super.viewDidLoad();

        //load group and app id from build info
        loadIds();
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
        if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
            if let contents = content.attachments {
                print(content.attachments);
                for (index, attachment) in (contents).enumerated() {
                    if attachment.hasItemConformingToTypeIdentifier(fileURLType) {
                        handleFiles(content: content, attachment: attachment, index: index)
                    }
                }
            }
        }
    }

    private func handleFiles (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
        attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in

            if error == nil, let url = data as? URL, let this = self {

                // Always copy
                let fileName = this.getFileName(from :url, type: .file)
                let newPath = FileManager.default
                    .containerURL(forSecurityApplicationGroupIdentifier: this.appGroupId)!
                    .appendingPathComponent(fileName)
                let copied = this.copyFile(at: url, to: newPath)
                if (copied) {
                    this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file))
                }

                if index == (content.attachments?.count)! - 1 {
                    let userDefaults = UserDefaults(suiteName: this.appGroupId)
                    userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)
                    userDefaults?.synchronize()
                    this.redirectToHostApp(type: .file)
                }

            } else {
                self?.dismissWithError()
            }
        }
    }

    override func configurationItems() -> [Any]! {
        // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
        return []
    }

    override func didSelectPost() {
        // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.

        // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
        //self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
        print("didSelectPost");
    }

    private func dismissWithError() {
        print("[ERROR] Error loading data!")
        let alert = UIAlertController(title: "Error", message: "Error loading data", preferredStyle: .alert)

        let action = UIAlertAction(title: "Error", style: .cancel) { _ in
            self.dismiss(animated: true, completion: nil)
        }

        alert.addAction(action)
        present(alert, animated: true, completion: nil)
        extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
    }

    private func redirectToHostApp(type: RedirectType) {
        //ids may not loaded yet so we need loadIds here too
        loadIds();
        let url = URL(string: "ShareMedia-\(hostAppBundleIdentifier)://dataUrl=\(sharedKey)#\(type)")
        var responder = self as UIResponder?
        let selectorOpenURL = sel_registerName("openURL:")

        while (responder != nil) {
            if (responder?.responds(to: selectorOpenURL))! {
                let _ = responder?.perform(selectorOpenURL, with: url)
            }
            responder = responder!.next
        }
        extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
    }

    enum RedirectType {
        case media
        case text
        case file
    }

    func getExtension(from url: URL, type: SharedMediaType) -> String {
        let parts = url.lastPathComponent.components(separatedBy: ".")
        var ex: String? = nil
        if (parts.count > 1) {
            ex = parts.last
        }

        if (ex == nil) {
            switch type {
                case .image:
                    ex = "PNG"
                case .video:
                    ex = "MP4"
                case .file:
                    ex = "GPX"
            }
        }
        return ex ?? "Unknown"
    }

    func getFileName(from url: URL, type: SharedMediaType) -> String {
        var name = url.lastPathComponent

        if (name.isEmpty) {
            name = UUID().uuidString + "." + getExtension(from: url, type: type)
        }
        return name
    }

    func copyFile(at srcURL: URL, to dstURL: URL) -> Bool {
        do {
            if FileManager.default.fileExists(atPath: dstURL.path) {
                try FileManager.default.removeItem(at: dstURL)
            }
            try FileManager.default.copyItem(at: srcURL, to: dstURL)
        } catch (let error) {
            print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
            return false
        }
        return true
    }

    private func getSharedMediaFile(forVideo: URL) -> SharedMediaFile? {
        let asset = AVAsset(url: forVideo)
        let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()
        let thumbnailPath = getThumbnailPath(for: forVideo)

        if FileManager.default.fileExists(atPath: thumbnailPath.path) {
            return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video)
        }

        var saved = false
        let assetImgGenerate = AVAssetImageGenerator(asset: asset)
        assetImgGenerate.appliesPreferredTrackTransform = true
        //        let scale = UIScreen.main.scale
        assetImgGenerate.maximumSize =  CGSize(width: 360, height: 360)
        do {
            let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil)
            try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath)
            saved = true
        } catch {
            saved = false
        }

        return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil

    }

    private func getThumbnailPath(for url: URL) -> URL {
        let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "==", with: "")
        let path = FileManager.default
            .containerURL(forSecurityApplicationGroupIdentifier: appGroupId)!
            .appendingPathComponent("\(fileName).jpg")
        return path
    }

    class SharedMediaFile: Codable {
        var path: String; // can be image, video or url path. It can also be text content
        var thumbnail: String?; // video thumbnail
        var duration: Double?; // video duration in milliseconds
        var type: SharedMediaType;

        init(path: String, thumbnail: String?, duration: Double?, type: SharedMediaType) {
            self.path = path
            self.thumbnail = thumbnail
            self.duration = duration
            self.type = type
        }

        // Debug method to print out SharedMediaFile details in the console
        func toString() {
            print("[SharedMediaFile] \n\tpath: \(self.path)\n\tthumbnail: \(self.thumbnail)\n\tduration: \(self.duration)\n\ttype: \(self.type)")
        }
    }

    enum SharedMediaType: Int, Codable {
        case image
        case video
        case file
    }

    func toData(data: [SharedMediaFile]) -> Data {
        let encodedData = try? JSONEncoder().encode(data)
        return encodedData!
    }

}

extension Array {
    subscript (safe index: UInt) -> Element? {
        return Int(index) < count ? self[Int(index)] : nil
    }
}
fpatron commented 3 months ago

Hi @erighet I just tried using your controller and I've encountered the same issue. When I share a URL or text, a popup is displayed before sending the data, but when I click on send, nothing happens. I can't debug it with a Mac as I don't have it with me.

fpatron commented 3 months ago

Ok, my bad... I missed the instruction ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) in the info.plist file. Now it works without your controller or your fix, @erighet. I only use text and url. Thank you for the support!

YashSavsani commented 3 months ago

Ok, my bad... I missed the instruction ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) in the info.plist file. Now it works without your controller or your fix, @erighet. I only use text and url. Thank you for the support!

@fpatron It is working for me. Made the same mistake. Thanks for the help.

Orlyga01 commented 1 month ago

Hi erighet Which version of the package are you working on? I tried your code in the swift file, I see a dialog box opened - and when I click Post, the application from which Im trying to share, is freezing :-(