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
325 stars 372 forks source link

In iOS while trying to share some URLs from Safari our app does not appear in the share sheet #253

Closed aakashstha closed 1 year ago

aakashstha commented 1 year ago
  1. Actually we are following two articles mentioned below, with receive_sharing_intent package of flutter and implementing the share extension suggestion feature in our flutter project integrated with swift native code. •https://developer.apple.com/documentation/foundation/app_extension_support/supporting_suggestions_in_your_app_s_share_extensionhttps://medium.com/flutter-community/receive-sharing-files-to-flutter-app-from-another-app-7a84c0c0cd88
  2. we are trying to send a URL from the share extension but our app does not appear in the share sheet for some URLs.
  3. let me clarify while trying to share some URLs like https://flutter.dev/ and https://www.google.com/ the share sheet is showing our app and suggestion of people.
  4. But while trying to share URLs like https://en.wikipedia.org/wiki/Google and https://www.youtube.com/ the share sheet is unable to show our app and the suggestion of people.
  5. I tried adding different NSExtensionActivationRule from Apple documentation but none of them solved the problem. •https://developer.apple.com/documentation/bundleresources/information_property_list/nsextension/nsextensionattributes/nsextensionactivationrule
  6. Some other resources I found but did not help •https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AppExtensionKeys.html#//apple_ref/doc/uid/TP40014212-SW10https://developer.apple.com/forums/thread/662671https://stackoverflow.com/questions/64166595/share-extension-app-not-showing-in-share-menu-on-first-attempt-since-ios-14https://bugzilla.mozilla.org/show_bug.cgi?id=1104805
  7. I know we are missing out something but what is it in a great dilemma 🤔

What I am doing in the code while editing info.plist in our app like below, the app is showing but nothing happens when its clicked.

  1. ShareExtension/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>IntentsSupported</key>
            <array>
                <string>INSendMessageIntent</string>
            </array>
            <key>NSExtensionActivationRule</key>
            <string>TRUEPREDICATE</string>
            <key>PHSupportedMediaTypes</key>
            <array>
                <string>Video</string>
                <string>Image</string>
            </array>
        </dict>
        <key>NSExtensionMainStoryboard</key>
        <string>MainInterface</string>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
    </dict>
</dict>
</plist>

As you can see I have removed value TRUEPREDICATE from the list above and added other supported url links and images supported value down below in the code of Info.plist and this way the app is not showing but feature is working from photos and files but not for the URL

  1. ShareExtension/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>IntentsSupported</key>
            <array>
                <string>INSendMessageIntent</string>
            </array>
            <key>NSExtensionActivationRule</key>
            <dict>
                <key>NSExtensionActivationSupportsAttachmentsWithMaxCount</key>
                <true/>
                <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
                <true/>
                <key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
                <true/>
                <key>NSExtensionActivationSupportsFileWithMaxCount</key>
                <integer>15</integer>
                <key>NSExtensionActivationSupportsImageWithMaxCount</key>
                <integer>15</integer>
                <key>NSExtensionActivationSupportsMovieWithMaxCount</key>
                <integer>15</integer>
                <key>NSExtensionActivationSupportsText</key>
                <true/>
            </dict>
            <key>PHSupportedMediaTypes</key>
            <array>
                <string>Video</string>
                <string>Image</string>
            </array>
        </dict>
        <key>NSExtensionMainStoryboard</key>
        <string>MainInterface</string>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
    </dict>
</dict>
</plist>

This is the main Info.plist from our app and I don't think there is mistake here.

  1. Main Runner /Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>https</string>
                <string>http</string>
                <string>com.googleusercontent.apps.890577051017-11fgqgou5rk8ht59vln934ebv51c2rcg</string>
            </array>
        </dict>
    </array>
    <key>NSUserActivityTypes</key>
    <array>
        <string>sampleConversationIdentifier</string>
        <string>INSendMessageIntent</string>
    </array>
    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <false/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                    <key>UISceneStoryboardFile</key>
                    <string>Main</string>
                </dict>
            </array>
        </dict>
    </dict>
    <key>UIFileSharingEnabled</key>
    <true/>
</dict>
</plist>

We are adding share extension app suggestion feature from this native swift code down below

  1. ShareExtension/ShareViewController.swift
//
//  ShareViewController.swift
//  ShareExtension101
//
//  Created by Aakash Shrestha on 23/03/2023.
//

import UIKit
import Social
import Intents

class ShareViewController: UIViewController {

    private func showSimpleAlert() {

        let alert = UIAlertController(title: "Send to Perr", message: "Sharing with Aakash iOS Ultra Phone", preferredStyle: .alert)

        let action = UIAlertAction(title: "Cancel", style: .cancel) { _ in

//            self.dismiss(animated: true, completion: nil)

        }
       let action2 =  UIAlertAction(title: "Yes", style: UIAlertAction.Style.default,
                                           handler: {(_: UIAlertAction!) in
                                             //Sign out action
             })

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

    override func viewDidLoad() {
        super.viewDidLoad()

        let intent = self.extensionContext?.intent as? INSendMessageIntent
        if intent != nil {

//            showSimpleAlert()

//          Next view Controller
            guard let vc = storyboard?.instantiateViewController(identifier: "IntentShareController") else {
                print("failed to get vc from storyboard")
                return
            }
            present(vc, animated: true)

        } else {

//          Next view Controller
            guard let vc = storyboard?.instantiateViewController(identifier: "AppShareController") else {
                print("failed to get vc from storyboard")
                return
            }
            present(vc, animated: true)
        }

    }
}

//self.dismiss(animated:true, completion: nil)

We are adding share extension people suggestion feature from this native swift code down below

  1. ShareExtension/IntentShareController.swift
    
    //
    //  ShareViewController.swift
    //  ShareExtension101
    //
    //  Created by Aakash Shrestha on 23/03/2023.
    //

import UIKit import Social import Intents import CoreServices import Foundation

import FirebaseCore import FirebaseAuth import GoogleSignIn

class IntentShareController: UIViewController {

let typeURL = String(kUTTypeURL)
let typeText = String(kUTTypeText)
let imageContentType = "public.image"
let videoContentType = kUTTypeMovie as String
let fileURLType = kUTTypeFileURL as String;
var textData:String = ""
var fileNameWithExtension:Array<String> = []
var fileSize:Array<Int> = []
var fileData:Array<Data> = []
var fileExtension:Array<String> = []
var key:Array<String> = []
var countAndStopLoaderAtLast:Int = 0
var isText:Bool = false

let sharedBy:String = "a6ff36f9-0782-44a9-ac63-af28dfb1d58f"
let sharedTo:String = "347d14bc-cd25-415b-9fb1-8ea74e6d067a"
private let token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE2ZGE4NmU4MWJkNTllMGE4Y2YzNTgwNTJiYjUzYjUzYjE4MzA3NzMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiQWFrYXNoIFNocmVzdGhhIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hL0FHTm15eGJJSm03ZzhnVTlLY01kd0p0cjRkSUpDUEhtQWZPbHBKLXQ2RnNvPXM5Ni1jIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3BlcnItYTg0YWEiLCJhdWQiOiJwZXJyLWE4NGFhIiwiYXV0aF90aW1lIjoxNjgxODk2NTU5LCJ1c2VyX2lkIjoiSXR2aXo4UEFyb1kyUVprYURuOTNyQklmNVVLMiIsInN1YiI6Ikl0dml6OFBBcm9ZMlFaa2FEbjkzckJJZjVVSzIiLCJpYXQiOjE2ODE4OTY1NTksImV4cCI6MTY4MTkwMDE1OSwiZW1haWwiOiJhYWthc2hAcmFtYWlsby50ZWNoIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZ29vZ2xlLmNvbSI6WyIxMTAyNzgyNzAzMjIwMDIxMDM3MDAiXSwiZW1haWwiOlsiYWFrYXNoQHJhbWFpbG8udGVjaCJdfSwic2lnbl9pbl9wcm92aWRlciI6Imdvb2dsZS5jb20ifX0.Pi1zyVWCiZg4RUk9IX56Ylx9A01DkAL6gc9GTacgJbyzmbqFgNSUv9PmQPHmZB6lilBJnI3YwqmP55dO1n1iIWwMA8d4rq45JqdNpDIPNn5RQRVXgO4UGzPIQHhh0wZHQU50phjDo6Oi_eYpIzxwZ870Om4_WumwnQKan6z9yTqllLBO88Gj4VaypWxqJZMdrGagRSNP1iwQWa9NHACVWJHDzYIcQfkAaVITcNEUrnzx5F-TCGkneBgBzjghOf-H1Koo8NwfO0EhmNRzvjU3D42QYU3uaKnl-wXm4vwXF-Fjp75kKcdc8OXMmN8xWmV9SA45swpukG3ICwdRvTlTeA"

override func viewDidLoad() {

// let signIn = GIDSignIn.sharedInstance //// signIn?.scopes = ["https://www.googleapis.com/auth/photos"] // // Create Google Sign In configuration object. // let config = GIDConfiguration(clientID: "890577051017-11fgqgou5rk8ht59vln934ebv51c2rcg.apps.googleusercontent.com") // GIDSignIn.sharedInstance.configuration = config // // let currentUser = signIn.currentUser // currentUser?.authentication?.refreshTokens(completion: { (authentication, error) in // if let error = error { // print("Error refreshing token: (error.localizedDescription)") // } else { // // Token refreshed successfully // let accessToken = authentication?.accessToken // // Use the new access token to share the image // } // })

    if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
        if let contents = content.attachments {
            for (_, attachment) in (contents).enumerated() {

                if (attachment.hasItemConformingToTypeIdentifier(fileURLType))  {
                    attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in
                        if error == nil, let url = data as? URL {
                            self!.storeFileData(url: url)
                            print("****************************************************************")
                        }

                    }

                }

               else if (attachment.hasItemConformingToTypeIdentifier(imageContentType) || attachment.hasItemConformingToTypeIdentifier(videoContentType))  {

                   attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in
                       if error == nil, let url = data as? URL {
                           self!.storeFileData(url: url)
                           print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
                       }
                   }

                   attachment.loadItem(forTypeIdentifier: videoContentType, options: nil) { [weak self] data, error in
                       if error == nil, let url = data as? URL {
                           self!.storeFileData(url: url)
                       }
                   }

                }

            }
        }
    }

}

func storeFileData(url:URL) {

// Add File Name let fileName = getFileName(from: url, type: .image) self.fileNameWithExtension.append(fileName) print(self.fileNameWithExtension)

// Add File Data let fileURL = URL(fileURLWithPath: url.path) let totalFileData = try! Data(contentsOf: fileURL) // print("***the contents of file = " + totalFileData.base64EncodedString()) self.fileData.append(totalFileData) print(self.fileData)

// Add File Size if url.startAccessingSecurityScopedResource() {

       guard let url = URL(string: url.absoluteString) else { return }
       do {
           let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
           let fileSizeInBytes = attributes[.size] as! Int
           self.fileSize.append(fileSizeInBytes)
           print(self.fileSize)
       } catch {
           print("Error getting file size: \(error.localizedDescription)")
       }

   }
   url.stopAccessingSecurityScopedResource()

// Add File Extension self.fileExtension.append(fileName.components(separatedBy: ".").last!.lowercased()) print(self.fileExtension)

}

override func viewDidAppear(_ animated: Bool) {
    showSimpleAlert()

    if (!fileData.isEmpty) {

        let date = Date()
        let millisecondsSinceEpoch = Int(date.timeIntervalSince1970 * 1000)

        for i in 0...fileData.count-1 {
            key.append("\(i)\(sharedBy)\(millisecondsSinceEpoch).\(fileExtension[i])")

        }
        print(key)
    }

}

private func showSimpleAlert() {

    let alert = UIAlertController(title: "Send to Perr", message: "Sharing with Aakash iOS Ultra Phone", preferredStyle: .alert)

    let action = UIAlertAction(title: "Cancel", style: .cancel) { _ in
        self.dismiss(animated: true, completion: nil)
        self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)

    }
   let action2 =  UIAlertAction(title: "Yes", style: UIAlertAction.Style.default,
                                       handler: {(_: UIAlertAction!) in

       self.startLoader()

       // Sending action
       // 1
       guard let extensionItem = self.extensionContext?.inputItems.first as? NSExtensionItem,
           let itemProvider = extensionItem.attachments?.first else {
               self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
               return
       }

       if itemProvider.hasItemConformingToTypeIdentifier(self.typeText) {
           self.isText = true
           self.handleIncomingText(itemProvider: itemProvider)
        }
       else if itemProvider.hasItemConformingToTypeIdentifier(self.fileURLType) {
           self.handleFiles(itemProvider: itemProvider)
      }
       else if itemProvider.hasItemConformingToTypeIdentifier(self.typeURL) {
           self.isText = true
           self.handleIncomingURL(itemProvider: itemProvider)
       }

       else if itemProvider.hasItemConformingToTypeIdentifier(self.imageContentType){
           self.handlePngImage(itemProvider: itemProvider)
        }

       else if itemProvider.hasItemConformingToTypeIdentifier(self.videoContentType) {
           self.handleVideos(itemProvider: itemProvider)
       }
       else {
           print("Error: No URL, Text and any proper file found")
           self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
       }

})

    alert.addAction(action)
    alert.addAction(action2)
    present(alert, animated: true, completion: nil)

// extensionContext!.completeRequest(returningItems: [], completionHandler: nil) }

func startLoader()  {
        let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
        let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
        loadingIndicator.hidesWhenStopped = true
        loadingIndicator.style = UIActivityIndicatorView.Style.large
        loadingIndicator.startAnimating()
        alert.view.addSubview(loadingIndicator)
        present(alert, animated: true, completion: nil)

// return alert }

func stopLoader() {
    if(isText) {
        DispatchQueue.main.async {
            self.dismiss(animated: true,completion: nil)
            self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
        }
    } else if(self.countAndStopLoaderAtLast == self.fileData.count) {
        print(self.countAndStopLoaderAtLast);
        DispatchQueue.main.async {

// dismiss remove(pop) the presented view controller from the Screen self.dismiss(animated: true,completion: nil) self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } }

}

private func handleIncomingURL(itemProvider: NSItemProvider) {

    itemProvider.loadItem(forTypeIdentifier: typeURL, options: nil) { (item, error) in
        if let error = error {
            print("URL-Error: \(error.localizedDescription)")
        }
        if let url = item as? NSURL, let urlString = url.absoluteString {

// self.shareTextURLDataToPerr(url:urlString) self.textData = urlString self.shareTextAndFileDataToPerr() }

// this remove the Share Extension from the Screen // self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } }

private func handleIncomingText(itemProvider: NSItemProvider) {
    itemProvider.loadItem(forTypeIdentifier: typeText, options: nil) { (item, error) in
        if let error = error {
            print("Text-Error: \(error.localizedDescription)")
        }

        if let text = item as? String {

// self.shareTextURLDataToPerr(url:text) self.textData = text self.shareTextAndFileDataToPerr() } } }

private func handlePngImage(itemProvider: NSItemProvider) {

    itemProvider.loadItem(forTypeIdentifier: imageContentType) { [weak self] (data, error) in

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

            self!.getSignedUrl()

        } else {
             self?.dismissWithError()
        }

    }
}

private func handleVideos(itemProvider: NSItemProvider) {

    itemProvider.loadItem(forTypeIdentifier: videoContentType) { [weak self] (data, error) in

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

            self!.getSignedUrl()

        } else {
             self?.dismissWithError()
        }

    }
}

private func handleFiles(itemProvider: NSItemProvider) {

    itemProvider.loadItem(forTypeIdentifier: fileURLType) { [weak self] (data, error) in

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

            self!.getSignedUrl()

        } else {
             self?.dismissWithError()
        }

    }
}

func getSignedUrl()   {

// print("9999999999999999999999999") // let a = FileManager.default.fileExists(atPath: localPathUrl.path) // print(a)

    let data: [String: Any] = ["key":key.map({ "inputimg/\($0)" }), "size":fileSize]

    // Create a URLRequest with the URL of the server endpoint
    var request = URLRequest(url: URL(string: "https://perr.myjobladder.com/upload-signed/")!)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = [
        "Content-Type": "application/json"
    ]
    request.httpBody = try? JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)

    // Create a data task with the request and the file data as the request body
    let task = URLSession.shared.dataTask(with: request) { data, response, error in

        guard data != nil else {
                        print("data is nil")
                        return
                    }

        // Handle the response from the server here
        let jsonResponse = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]

        let dictionary = jsonResponse
        if let dataArray = dictionary!["data"] as? [String] {
            print("Size of signed URL Array = \(dataArray.count)")

            self.uploadToSigned(signedUrl:dataArray)

// DispatchQueue.main.async { // self.uploadToSigned(signedUrl:dataArray) // }

        } else {
            print("Error: Data is not an array of strings")
        }

    }

    task.resume()
    return

}

func uploadToSigned(signedUrl:Array<String>)  {

    for i in 0...fileSize.count-1 {

// Specify the URL of the pre-signed URL for uploading the file let urlEndPoint = URL(string: signedUrl[i])! var request = URLRequest(url: urlEndPoint) request.httpMethod = "PUT"

        // Set the request body to the image data
        request.httpBody = fileData[i]

        print("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")
        print(request)
        print(type(of: fileData[i]))

        // Set the Content-Type header to "image/jpeg"
        request.setValue("application/\(fileExtension[i])", forHTTPHeaderField: "Content-Type")
        request.setValue("\(fileSize[i])", forHTTPHeaderField: "Content-Length")

        // Create a URLSession and data task to perform the request
        let session = URLSession.shared
        let task = session.dataTask(with: request) { data, response, error in
            if let error = error {
                print("Error: \(error.localizedDescription)")
            } else if let response = response as? HTTPURLResponse {
                if response.statusCode == 200 {
                    print("File uploaded successfully!")

                    self.countAndStopLoaderAtLast += 1
                    if(self.countAndStopLoaderAtLast == self.fileData.count) {
                        self.shareTextAndFileDataToPerr()
                    }

                } else {
                    print("Error: HTTP status code \(response.statusCode)")
                }
            }
        }

        // Start the data task
        task.resume()
    }

}

func shareTextAndFileDataToPerr(){
    var documentList:Array<Dictionary<String,Any>> = []

    if(isText) {
        documentList.append(["attribute": "text", "key": "","document": textData,"size": 0])
    }
    else {
        for i in 0...fileSize.count-1 {
            documentList.append(["attribute": "\(fileExtension[i])","key": key[i],"document": fileNameWithExtension[i],"size":fileSize[i]])
        }
    }

    let data: [String: Any] = ["documents": documentList, "shared_by": sharedBy, "shared_to": sharedTo]

    // Create a URLRequest with the URL of the server endpoint
    var request = URLRequest(url: URL(string: "https://perr.myjobladder.com/data/")!)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = [
        "Content-Type": "application/json",
        "Authorization":"Bearer \(token)"

    ]
    request.httpBody = try? JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in

        guard data != nil else {
                        print("data is nil")
                        return
                    }

        // Handle the response from the server here
        let jsonResponse = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]

// self.dispatchAtLast.append(jsonResponse!) // self.countAndStopLoaderAtLast += 1 print(jsonResponse as Any);

// Remove Loader at last self.stopLoader()

    }
    task.resume()

}

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 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 = "TXT"
        }
    }
    return ex ?? "Unknown"
}

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)
}

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

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)")
    }

}

} // main class end

aprashantz commented 1 year ago

Solution

        <key>NSExtensionActivationRule</key>
        <string>
            SUBQUERY (
            extensionItems,
            $extensionItem,
            SUBQUERY (
            $extensionItem.attachments,
            $attachment,
            ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" OR
            ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" OR
            ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" OR
            ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text"
            ).@count &gt; 0
            ).@count &gt; 0
        </string>
DavidAriza commented 1 year ago

@aakashstha can you share the complete share extension info.plist please?

aakashstha commented 1 year ago

@DavidAriza In this issue I had already shared all the shared extension key, value pairs naming as ShareExtension/Info.plist above just replace these block of code with the solution code provided by @aprashantz

NSExtensionActivationRule
        <dict>
            <key>NSExtensionActivationSupportsAttachmentsWithMaxCount</key>
            <true/>
            <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
            <true/>
            <key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
            <true/>
            <key>NSExtensionActivationSupportsFileWithMaxCount</key>
            <integer>15</integer>
            <key>NSExtensionActivationSupportsImageWithMaxCount</key>
            <integer>15</integer>
            <key>NSExtensionActivationSupportsMovieWithMaxCount</key>
            <integer>15</integer>
            <key>NSExtensionActivationSupportsText</key>
            <true/>
        </dict>
aprashantz commented 1 year ago

@aakashstha k cha sathi halkhabar