General Middleware

How to create a Share Extension in Swift

When other apps want to share data to your app, you need a Share Extension. The ViewController that pops while sharing is called a Share Sheet, as shown below.

In order to get your app to show up in the ShareSheet, follow below steps, once your project is open

  1. In Xcode, File -> New -> Target
  2. Select “Share Extension” and create.

Once the Share Extension is created, you will see your app populating in the Share Sheet, while sharing anything from other apps, as highlighted with a square outline.

Next Step is to prepare the Share Extension to receive and process the incoming data. It may be a text, an Image or a Video, in most cases. Follow the below steps for the same.

  1. Open the ShareViewController.swift, that is generated automatically by Xcode
  2. Insert the below import
import MobileCoreServices

3. Update the didSelectPost() method as below:

   override func didSelectPost() {
        //Get the data to post
        if let content = extensionContext!.inputItems.first as? NSExtensionItem {
            //Defining multiple contenTypes to enable sharing of both photos and videos
            
//Here we are checking the content Types. kUTTypeMovie and kUTTypeImage are the standard types for video and image respectively
let contentTypes = [kUTTypeMovie as? String, kUTTypeImage as? String]
            if let contents = content.attachments as? [NSItemProvider] {
                for attachment in contents {
                    for contentType in contentTypes {
                        if attachment.hasItemConformingToTypeIdentifier(contentType!) {
                            attachment.loadItem(forTypeIdentifier: contentType!, options: nil) { (data, error) in
                                if error  != nil {
                                    print(error)
                                    return
                                }
                                guard let url = data as? NSURL else {
                                    return
                                }
                                guard let url1 = url as? URL else {
                                    return
                                }
                                guard let mediaData = NSData(contentsOf: url1) else {
                                    return
                                }
                                //Now get the path where you will write the data
                                guard let def = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.path_of_media") else {
                                    return
                                }
                                
                                var url2 = URL(string: "")
                                
                                switch contentType {
                                case kUTTypeMovie as? String :
                                    url2 = URL(string: "\(def)myNewMedia.mp4")!
                                    break
                                    
                                case kUTTypeImage as? String :
                                    url2 = URL(string: "\(def)myNewMedia")!
                                    break
                                    
                                case .none:
                                    break
                                case .some(_):
                                    break
                                }
                                
                                print("value from the switch case = \(url2)")
                                
                                let writeToURLAbsPath = "\(def)myNewMedia.mp4".split(separator: ":")[1].replacingOccurrences(of: "///", with: "/")
                                
                                //write the data to disc
                                if url1 != nil {
                                    do {
                                        print("Now mediaData is being written to \(url2)")
                                        try mediaData.write(to: url2!, options: .atomic)
                                    } catch {
                                        print("Some error happened while writing data to disc ...\(error).")
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

The above code basically gets the shared media, and tries to save it into the app group location (next step), so that the main application can access the same media.

4. Now, let’s create the app group named “group.path_of_media” for both the Primary Application and Share Extension

You can relate this step to the share extension code above.

Refer to a portion of it below:

guard let def = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.path_of_media")

5. Navigate to the info.plist of the Share Extension target. Define the number of media images or videos to share at a time.

Create the attribute exactly as NSExtensionActivationSupportsImageWithMaxCount of type Number under NSExtension -> NSExtensionAttributes -> NSExtensionActivationRule

A value of 1 allows sharing only 1 media. Selecting multiple media removes the application from the ShareSheet. Try it out.

6. It is time to create the ViewController for the Primary Application. Let’s follow below code, that get the shared media from the app group location.

//
//  ViewController.swift
//  Add application to the Share Sheet
//
//  Created by Animesh Banerjee on 1/5/21.
//

import UIKit
import AVKit

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    
    override func viewDidAppear(_ animated: Bool) {

    }
    override func viewDidLoad() {
        super.viewDidLoad()
        //set the booleans as per the intended media whether it is an image or a video
        loadAndDisplayMedia(image: false, video: true)
    }
    
    func loadAndDisplayMedia(image: Bool, video: Bool) {
        var defFilePath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.path_of_media")
        
        if image {
            defFilePath?.appendPathComponent("myNewMedia")
        } else if video {
            defFilePath?.appendPathComponent("myNewMedia.mp4")
        }
        
        if let path = defFilePath?.absoluteString.split(separator: ":")[1].replacingOccurrences(of: "///", with: "/") {
            //Note that for checking if fileExists, we need to get the absolute path in the form /var/public/ ..... and file:///var/public/ .... will not work
            if FileManager().fileExists(atPath: path) {
                print("File exists ...at path \(path)...")
                if image {
                    imageView.image = UIImage(contentsOfFile: path)
                } else if video {
                    self.playVideo(urlString: defFilePath!.absoluteString)
                }
            }
        }
    }
    
    func playVideo(urlString: String) {
        
        guard let url = URL(string: urlString) else {
            return
        }
        print("filepath receving in the form of url string = \(urlString)")
        print("url = \(url)")
        // Create an AVPlayer, passing it the HTTP Live Streaming URL or from saved video
        var player: AVPlayer?
        player = AVPlayer(url: url)

        // Create a new AVPlayerViewController and pass it a reference to the player.
        let controller = AVPlayerViewController()
        controller.player = player

        DispatchQueue.main.async {
            self.present(controller, animated: true) {
                print("playing ......... ")
                player!.play()
            }
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *