Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS 18 #324

Open
Nightbl927 opened this issue Sep 18, 2024 · 20 comments
Open

iOS 18 #324

Nightbl927 opened this issue Sep 18, 2024 · 20 comments

Comments

@Nightbl927
Copy link

Is anyone else having issues sharing on an iOS 18 device? I still get the icon to share to the app, and it appears to be fine on the app that is doing the sharing but it seems like the app that is receiving the sharing never opens and receives the shared data.

@Nightbl927
Copy link
Author

Nightbl927 commented Sep 18, 2024

attached what I believe to be the error is:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Share_Extension.ShareViewController openURL:]: unrecognized selector sent to instance 0x10201f400'

FROM

private func redirectToHostApp() {
        // ids may not loaded yet so we need loadIds here too
        loadIds()
        let url = URL(string: "\(kSchemePrefix)-\(hostAppBundleIdentifier):share")
        var responder = self as UIResponder?
        let selectorOpenURL = sel_registerName("openURL:")
        while (responder != nil) {
            if (responder?.responds(to: selectorOpenURL))! {
                _ = responder?.perform(selectorOpenURL, with: url)
            }
            responder = responder!.next
        }
        extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
    }

@zhponng
Copy link

zhponng commented Sep 19, 2024

I have the same issue; file sharing in iOS 18 is unresponsive and doesn't show any error messages, but it works fine in iOS 16 and 17.

@aleynaak
Copy link

aleynaak commented Sep 20, 2024

Any news? I'm having the same issue. @Nightbl927

@Nightbl927
Copy link
Author

Any news? I'm having the same issue. @Nightbl927

No news. I haven't found any way to resolve this. Unfortunately, I am not too familiar with Swift to troubleshoot it myself. I was able to pinpoint the issue to let selectorOpenURL = sel_registerName("openURL:"). I believe this no longer works in iOS 18.

@sstadtl
Copy link

sstadtl commented Sep 22, 2024

I think i found the solution here:
This is an external link ](https://stackoverflow.com/questions/27506413/share-extension-to-open-containing-app/78975759#78975759)
doing more testing right now....

@objc @discardableResult private func openURL(_ url: URL) -> Bool {
    var responder: UIResponder? = self
    while responder != nil {
        if let application = responder as? UIApplication {
            if #available(iOS 18.0, *) {
                application.open(url, options: [:], completionHandler: nil)
                return true
            } else {
                return application.perform(#selector(openURL(_:)), with: url) != nil
            }
        }
        responder = responder?.next
    }
    return false
}

@Nightbl927
Copy link
Author

I think i found the solution here: This is an external link ](https://stackoverflow.com/questions/27506413/share-extension-to-open-containing-app/78975759#78975759) doing more testing right now....

@objc @discardableResult private func openURL(_ url: URL) -> Bool {
    var responder: UIResponder? = self
    while responder != nil {
        if let application = responder as? UIApplication {
            if #available(iOS 18.0, *) {
                application.open(url, options: [:], completionHandler: nil)
                return true
            } else {
                return application.perform(#selector(openURL(_:)), with: url) != nil
            }
        }
        responder = responder?.next
    }
    return false
}

How did your testing go? @sstadtl?

@sstadtl
Copy link

sstadtl commented Sep 23, 2024

it works in ip14 iOS18, Sim ip15 iOS 17.5.1.

this is my new code in ShareViewController.swift:

private func redirectToHostApp(type: RedirectType) {
       let url = URL(string: "ShareMedia-\(hostAppBundleIdentifier)://dataUrl=\(sharedKey)#\(type)")
       var responder = self as UIResponder?
       // https://stackoverflow.com/questions/27506413/share-extension-to-open-containing-app/78975759#78975759
       // https://github.com/KasemJaffer/receive_sharing_intent/issues/324
       if #available(iOS 18.0, *) {            
           while responder != nil {
                  if let application = responder as? UIApplication {                       
                      application.open(url!, options: [:], completionHandler: nil)                       
                  }
                  responder = responder?.next
              }
       } else {
           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)
       }
   }

But for historic reasons i use a very custom "ShareViewController.swift:".
so i am unsure this works by just overwriting this single function for everyone.

as "redirectToHostApp" is private i think there is more to do...

@Nightbl927
Copy link
Author

Thank you @sstadtl , I will see if this works for me.

@Akash-ptl
Copy link

is there any update on this ? @sstadtl

@Nightbl927
Copy link
Author

Nightbl927 commented Sep 24, 2024

Yes application will not be in scope with the code as is. However, changing application.open(url!, options: [:], completionHandler: nil) to UIApplication.shared.open(url!, options: [:], completionHandler: nil) should work

@Akash-ptl
Copy link

Yes application will not be in scope with the code as is. However, changing application.open(url!, options: [:], completionHandler: nil) to UIApplication.shared.open(url!, options: [:], completionHandler: nil) should work

Not working for me

i am still not able to receive files from ios 18 iphone to my flutter app

@Nightbl927
Copy link
Author

Yes application will not be in scope with the code as is. However, changing application.open(url!, options: [:], completionHandler: nil) to UIApplication.shared.open(url!, options: [:], completionHandler: nil) should work

Not working for me

i am still not able to receive files from ios 18 iphone to my flutter app

I am using it to receive shared links and text from Safari and have not tested with files yet. Could you see if that works for you?

@malt03
Copy link

malt03 commented Sep 26, 2024

It works for me! Thank you!
completeRequest may need to be called outside of the if statement.

import receive_sharing_intent

import UIKit
import MobileCoreServices
import Photos

@available(swift, introduced: 5.0)
open class ShareViewController: UIViewController {
    var hostAppBundleIdentifier = ""
    var appGroupId = ""

    open override func viewDidLoad() {
        super.viewDidLoad()
        
        loadIds()
    }
    
    open override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        guard
            let content = extensionContext!.inputItems[0] as? NSExtensionItem,
            let attachments = content.attachments,
            let attachment = attachments.first
            else { return }
    
        if !attachment.hasItemConformingToTypeIdentifier(UTType.fileURL.identifier) { return }
        attachment.loadItem(forTypeIdentifier: UTType.fileURL.identifier) { [weak self] (data, error) in
            guard let s = self, let url = data as? URL else { return }
            s.handleMedia(forFile: url)
        }
    }
    
    private func loadIds() {
        let shareExtensionAppBundleIdentifier = Bundle.main.bundleIdentifier!

        let lastIndexOfPoint = shareExtensionAppBundleIdentifier.lastIndex(of: ".")
        hostAppBundleIdentifier = String(shareExtensionAppBundleIdentifier[..<lastIndexOfPoint!])
        let defaultAppGroupId = "group.\(hostAppBundleIdentifier)"
        
        let customAppGroupId = Bundle.main.object(forInfoDictionaryKey: kAppGroupIdKey) as? String
        
        appGroupId = customAppGroupId ?? defaultAppGroupId
    }
    
    private func handleMedia(forFile url: URL) {
        let fileName = url.lastPathComponent
        let newPath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupId)!.appendingPathComponent(fileName)
        
        if !copyFile(at: url, to: newPath) { return }

        let newPathDecoded = newPath.absoluteString.removingPercentEncoding!;
        saveAndRedirect(data: SharedMediaFile(
            path: newPathDecoded,
            mimeType: url.mimeType(),
            type: .file
        ))
    }
    
    private func saveAndRedirect(data: SharedMediaFile) {
        let userDefaults = UserDefaults(suiteName: appGroupId)
        userDefaults?.set(toData(data: [data]), forKey: kUserDefaultsKey)
        userDefaults?.set(nil, forKey: kUserDefaultsMessageKey)
        userDefaults?.synchronize()
        redirectToHostApp()
    }
    
    private func redirectToHostApp() {
        loadIds()
        let url = URL(string: "\(kSchemePrefix)-\(hostAppBundleIdentifier):share")
        var responder = self as UIResponder?
        
        if #available(iOS 18.0, *) {
            while responder != nil {
                if let application = responder as? UIApplication {
                    application.open(url!, options: [:], completionHandler: nil)
                }
                responder = responder?.next
            }
        } else {
            let selectorOpenURL = sel_registerName("openURL:")
            
            while (responder != nil) {
                if (responder?.responds(to: selectorOpenURL))! {
                    _ = responder?.perform(selectorOpenURL, with: url)
                }
                responder = responder!.next
            }
        }
        extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
    }
    
    private 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 toData(data: [SharedMediaFile]) -> Data {
        let encodedData = try? JSONEncoder().encode(data)
        return encodedData!
    }
}

extension URL {
    public func mimeType() -> String {
        if #available(iOS 14.0, *) {
            if let mimeType = UTType(filenameExtension: self.pathExtension)?.preferredMIMEType {
                return mimeType
            }
        } else {
            if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, self.pathExtension as NSString, nil)?.takeRetainedValue() {
                if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
                    return mimetype as String
                }
            }
        }
        
        return "application/octet-stream"
    }
}

@aleynaak
Copy link

I use the share extension for sharing files and text in my application. I've been struggling with the problem for a few days but still haven't found a solution. I've tried similar suggestions as above but without success. My ShareViewController is as it is currently used in the package. Does anyone have a different update for this situation?

@Nightbl927
Copy link
Author

@aleynaak have you updated the ShareViewController as suggested by @malt03 above? I have not yet updated and forked the code but if someone is willing to do that who has gotten it to work, that could also help.

@dmitry-kotorov
Copy link
Contributor

@Nightbl927 I've updated the controller.
Please merge #328

@Nightbl927
Copy link
Author

@Nightbl927 I've updated the controller. Please merge #328

Unfortunately, I am unable to merge it. I was hoping for someone to fork and have @aleynaak pull from that repo.

@aleynaak
Copy link

Thank you for your support. I solved the problem. It was a problem with my URLShemes. When I fixed it, the problem disappeared. @Nightbl927 @dmitry-kotorov

@Nightbl927
Copy link
Author

Thank you for your support. I solved the problem. It was a problem with my URLShemes. When I fixed it, the problem disappeared. @Nightbl927 @dmitry-kotorov

Good to hear!

@test0terter0n
Copy link

We have tested current master with PR #328 and it seems to work correct ✅. Pls consider releasing new version

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants