Shalin Shah
Shalin Shah

Reputation: 8183

iOS Share extension not working on images from photo library

I have built a share extension in Swift 4 and I've been trying to get it to be able to accept images of all types. For some reason, it only seems to be accepting only screenshots when I try sharing from Photo Library. No other images end up redirecting to my app.

For content type, I'm currently using kUTTypeData but I've also tried kUTTypeImage, kUTTypeJPEG, kUTTypeJPEG2000, kUTTypePNG, and kUTTypeURL.

The app is appearing on the share list correctly, so it doesn't seem to be a problem with NSExtensionActivationRule.

Here is my code for the share extension:

import UIKit
import Social
import MobileCoreServices

class ShareViewController: SLComposeServiceViewController {
    
    let sharedKey = "ImageSharePhotoKey"
    var imagesData: [Data] = []
                
    override func viewDidLoad() {
        super.viewDidLoad()
        self.manageImages()
    }
    
    func redirectToHostApp(key: String) {
        let url = URL(string: "AppName://dataUrl=\(key)")
        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
        }
        self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
    }
    
    func manageImages() {
        let content = extensionContext!.inputItems[0] as! NSExtensionItem
        let contentType = kUTTypeData as String

        
        for (index, attachment) in (content.attachments!).enumerated() {
            if attachment.hasItemConformingToTypeIdentifier(contentType) {
                
                attachment.loadItem(forTypeIdentifier: contentType, options: nil) { [weak self] data, error in
                    if error == nil, let this = self {
                        var contentData: Data? = nil

                        //data could be raw Data
                        if let data = data as? Data {
                            contentData = data

                        //data could be an URL
                        } else if let url = data as? URL {
                            contentData = try? Data(contentsOf: url)
                        }

                        //data could be an UIImage object (e.g. ios11 screenshot editor)
                        else if let imageData = data as? UIImage {
                            contentData = imageData.pngData()
                        
                        }

                        // proceed here with contentData
                        let rawImage = UIImage(data: contentData ?? Data())
                        
                        // CONVERTED INTO FORMATTED FILE : OVER COME MEMORY WARNING
                        // YOU USE SCALE PROPERTY ALSO TO REDUCE IMAGE SIZE
                        let image = UIImage.resizeImage(image: rawImage!, width: rawImage?.size.width ?? 0, height: rawImage?.size.height ?? 0)
                        let imgData = image.pngData()
                        
                        this.imagesData.append(imgData!)
                        
                        if index == (content.attachments?.count)! - 1 {
                            DispatchQueue.main.async {
                                let userDefaults = UserDefaults(suiteName: "group.com.myName.AppName")
                                userDefaults?.set(this.imagesData, forKey: this.sharedKey)
                                userDefaults?.synchronize()
                                
                                self?.redirectToHostApp(key: this.sharedKey)
                            }
                        }
                    }
                }
            }
        }
    }
        
    override func isContentValid() -> Bool {
        return true
    }

    override func didSelectPost() {
    }

    override func configurationItems() -> [Any]! {
        return []
    }

}

extension UIImage {
    class func resizeImage(image: UIImage, width: CGFloat, height: CGFloat) -> UIImage {
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        image.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}

Here is my current PLIST file for the extension:

<?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>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleDisplayName</key>
    <string>ShareExt</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>$(PRODUCT_NAME)</string>
    <key>CFBundlePackageType</key>
    <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
    <key>CFBundleShortVersionString</key>
    <string>5.0</string>
    <key>CFBundleVersion</key>
    <string>5</string>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>NSExtensionActivationRule</key>
            <string>SUBQUERY (
                  extensionItems,
                  $extensionItem,
                  SUBQUERY (
                      $extensionItem.attachments,
                      $attachment,
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ||
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" ||
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" ||
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.data" ||
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.content" ||
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "com.adobe.pdf" ||
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.png" ||
                       ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "com.compuserve.gif"
                    ).@count == $extensionItem.attachments.@count
                    ).@count == 1
            </string>
        </dict>
        <key>NSExtensionMainStoryboard</key>
        <string>MainInterface</string>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
    </dict>
</dict>
</plist>

Any suggestions or help for how to get this to accept images of all formats would be appreciated!!

Upvotes: 1

Views: 1981

Answers (1)

Manish Punia
Manish Punia

Reputation: 877

Try with kUTTypeFileURL. If your application is not appearing at all in the share sheet then the issue may be related to NSExtensionActivationRule, you can rule out that issue by using NSExtensionActivationRule value as TRUEPREDICATE. By looking at NSExtensionActivationRule value, I don't see any issue with it.

Try with kUTTypeFileURL, if this doesn't work, debug and find attachment.registeredTypeIdentifiers value while share image. and you can try one of those values.

Check attachment.registeredTypeIdentifiers value before loading attachment.loadItem(forTypeIdentifier: contentType, options: nil)

Upvotes: 1

Related Questions