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

Sharing photo to app in iOS with capacitor v4 #66

Open
j-d-carmichael opened this issue Dec 16, 2022 · 5 comments
Open

Sharing photo to app in iOS with capacitor v4 #66

j-d-carmichael opened this issue Dec 16, 2022 · 5 comments

Comments

@j-d-carmichael
Copy link
Contributor

I just built an app in capacitor v4 - sharing links, web-urls and photos works flawlessly in Android.

In iOS text and web-urls work fine, but sharing a photo I cannot get to work in v4.

Setup:

  • YOUR_APP_GROUP_ID: In xcode: the App has a URL Type as stated in the readme:
    • Identifier: com.myapp.www
    • URL Schemes: shareintent
    • In the ShareViewController, 1 line changed to "var urlComps = URLComponents(string: "shareintent://")!"
  • YOUR_APP_GROUP_ID - app groups
    • app group added in xcode "group.com.myapp.www.shareextension"
    • 2 lines changes in the ShareViewController
      • ln52: fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.com.myapp.www.shareextension")!
      • ln64: fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.com.myapp.www.shareextension")!

The shareintent plugin info.plist has been updated to:

                <key>NSExtensionAttributes</key>
		<dict>
			<key>NSExtensionActivationRule</key>
			<dict>
   			    <key>NSExtensionActivationSupportsFileWithMaxCount</key>
		            <integer>1</integer>
		            <key>NSExtensionActivationSupportsImageWithMaxCount</key>
		            <integer>1</integer>
		            <key>NSExtensionActivationSupportsMovieWithMaxCount</key>
		            <integer>5</integer>
		            <key>NSExtensionActivationSupportsText</key>
		            <true/>
		            <key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
		            <integer>1</integer>
		            <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
		            <integer>1</integer>
		            <key>NSExtensionActivationUsesStrictMatching</key>
		            <false/>
			</dict>
		</dict>

The result

Sharing links and text to the ios app works perfectly.
Sharing a photo (not a even a live photo) does not work.

  • Load the photos app
  • select share on a photo
  • select my capacitor app to share to

The capacitor app does not load, instead the photos app being share from sort of animates up about 4mm on the screen, then sort of "gives up". In xcode there are no error messages that are consistent... but sometimes (I think it is related) I get this error in xcode:

"2022-12-16 10:06:29.618067+0000 App[15668:493451] [Snapshotting] Snapshotting a view (0x10a00d600, UIKeyboardImpl) that has not been rendered at least once requires afterScreenUpdates:YES."

@carsten-klaffke
Copy link
Owner

I did not experience this behavior so far and I don't see that anything is wrong with your configuration. In Xcode, have you tried running the plugin directly and not your app? This might give better logging output as the share plugin is a little extra app.

@wescarr
Copy link

wescarr commented Feb 22, 2023

@j-d-carmichael I had a similar issue to this previously and after some debugging noticed that I did not the have the App Group entitlement set correctly. You need to add this in the Signing & Capabilities tab. FWIW, I added it both to the main App, and the Share Extension.

@tarangshah19
Copy link

can i get example code base my app listed in share screen but when i try to click on share button nothing happend dont know why

[//
//  ShareViewController.swift
//  mindlib
//
//  Created by Carsten Klaffke on 05.07.20.
//

import MobileCoreServices
import Social
import UIKit

class ShareItem {
    
    public var title: String?
    public var type: String?
    public var url: String?
}

class ShareViewController: UIViewController {
    
    private var shareItems: [ShareItem] = []
    
    override public func viewDidAppear(_ animated: Bool) {
       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) ?? ""),
            ]
        }.flatMap({ $0 })
        print("SomeString", queryItems)
        
        var urlComps = URLComponents(string: "localhost://")!
        urlComps.queryItems = queryItems
        openURL(urlComps.url!)
        
        print("SomeString", urlComps)
    }
    
    fileprivate func createSharedFileUrl(_ url: URL?) -> String {
        let fileManager = FileManager.default
        
        let copyFileUrl =
        fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.com.imeuswe.app")!
            .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 {
        let fileManager = FileManager.default
        
        let copyFileUrl =
        fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.com.imeuswe.app")!
            .absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
        + "/screenshot.png"
        do {
            try image.pngData()?.write(to: URL(string: copyFileUrl)!)
            return copyFileUrl
        } catch {
            print(error.localizedDescription)
            return ""
        }
    }
    
    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)
    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"
            shareItem.type = "image/png"
            shareItem.url = self.saveScreenshot(image)
        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
        Task {
            try await withThrowingTaskGroup(
                of: ShareItem.self,
                body: { taskGroup in
                    
                    for attachment in extensionItem.attachments! {
                        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)
                            }
                        }
                    }
                    
                    for try await item in taskGroup {
                        self.shareItems.append(item)
                    }
                })
            
            self.sendData()
            
        }
    }
    
    @objc func openURL(_ url: URL) -> Bool {
        var responder: UIResponder? = self
        while responder != nil {
            if let application = responder as? UIApplication {
                return application.perform(#selector(openURL(_:)), with: url) != nil
            }
            responder = responder?.next
        }
        return false
    }
    
}](url)

@alessio-libardi-zupit
Copy link

Thanks, @wescarr, you saved me, adding the App Group to the Signing & Capabilities tab of both my app and of the share extension fixed the issue for me! 🚀

@carsten-klaffke
Copy link
Owner

Hey guys, I recently added an example project for Android and iOS (/Example/SendIntentExample). Maybe this is of help for you.

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

5 participants