How to build a basic Share Extension in Swift

In this tutorial, you'll learn to build a basic share extension for your iOS app in swift using UIKit and SwiftUI

Share:

Mar 22, 2023 1099 Words

Read Time: 6 Minutes

A screenshot of code-snippet to build a share extension in Swift in iOS

Introduction

One of the most useful features in iOS, is the ability to share data between apps using share-sheet menu.

This sheet feature is generally used to share data like urls, images, videos to upload as social media posts.

A less detailed screenshot of share-sheet from the internet
➚ This beautiful screenshot was taken from my iOS app. Get it for yourself!

While using a share-sheet in iOS, is relatively simple, the same cannot be said for building the receiving end of the share-sheet.

What is a share extension?1

In order for your iOS apps to be able to receive data from a share-sheet, you’ll want to create a ‘share extension’ and release it with the app.

In this tutorial, you’ll learn the following:

  1. How to setup a share extension for your app
  2. How to pass an image to your app’s share extension
  3. How to render the image in your share extension using SwiftUI

How to build a share extension?

Setup

You can create a share extension for an iOS app in Xcode using the following method:

  1. Click on the project folder in file explorer in your iOS app project

    Creating a share extension step 1

  2. Click the + icon in the botton toolbar as showing the image below

    Creating a share extension step 2

  3. Search, click on share extension & click Activate in the next dialog box

    Creating a share extension step 3

πŸ’‘ If Xcode asks you to provide a name for your share extension, give it a name that fits your needs.

Once this is setup, you’ll be able to see a share extension view created in your file explorer.

Configuration settings

  1. Delete all code from ShareViewController.swift file and add the following code:

    import UIKit
    
    @objc(PrincipalClassName)
    class ShareViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            // do something here
        }
    }
    
  2. In info file, Insert the key NSExtensionPrincipalClass with value PrincipalClassName as provided in the image below

    NSExtensionPrincipalClass set as PrincipalClassName in info.plist

  3. In info file, delete the NSExtensionMainStoryboard key-value pair because we won’t be needing it

  4. Delete the file MainInterface.storyboard file (if you want to) because we will not be needing it

We can now start focusing on the functionality.

Handling the inputs to the share extension

In ShareViewController.swift, we shall add functionality to your share extension to do perform some active with given input from share-sheet.

We have overridden the viewDidLoad method to do something when the extension loads in the system and on your screen.

In the UIViewController object, we use the self.extensionContext to extract the input data from the share-sheet.

πŸ’‘ In our example, we would like to render images from our gallery in our extension.

Before I explain how this is done, here’s the piece of code we use to get this done:

import UIKit
import SwiftUI

@objc(PrincipalClassName)
class ShareViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // self.extensionContext can be used to retrieve images, videos and urls from share extension input
        let extensionAttachments = (self.extensionContext!.inputItems.first as! NSExtensionItem).attachments
        for provider in extensionAttachments! {
            // loadItem can be used to extract different types of data from NSProvider object in attachements
            provider.loadItem(forTypeIdentifier: "public.image"){ data, _ in
                // Load Image data from image URL
                if let url = data as? URL {
                    if let imageData = try? Data(contentsOf: url) {
                        // Load Image as UIImage from image data
                        let uiimg = UIImage(data: imageData)!
                        // Convert to SwiftUI Image
                        let image = Image(uiImage: uiimg)
                        // .. Do something with the Image
                    }
                }
            }
        }
    }
}

The above code does the following:

  1. We extract the attachments containing data like image, urls, video etc
  2. Since we want the image, we extract image data using public.image typecast, from the attachments into data variable
  3. The data contains a local url that represents the image in our device, which looks like for example data/Media/PhotoData/OutgoingTemp/XXX/IMG_0001.PNG
  4. The url is loaded into UIImage object which is converted to Image object in SwiftUI

How to render swiftUI in a share extension?

We have the required Image object in SwiftUI, we now have to render a SwiftUI view inside the share extension using the following piece of code:

// ..
// [START] The following piece of code can be used to render swifUI views from UIKit
DispatchQueue.main.async {
    let u = UIHostingController(
        rootView: ImageView(image: image)
    )
    u.view.frame = (self.view.bounds)
    self.view.addSubview(u.view)
    self.addChild(u)
}
// [END]
// ..

πŸ’‘ The class UIHostingController is a UIKit view controller, that can embed any SwiftUI View as a child view.

The sample code snippet for rendering looks like this,

import UIKit
import SwiftUI

@objc(PrincipalClassName)
class ShareViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // self.extensionContext can be used to retrieve images, videos and urls from share extension input
        let extensionAttachments = (self.extensionContext!.inputItems.first as! NSExtensionItem).attachments
        for provider in extensionAttachments! {
            // loadItem can be used to extract different types of data from NSProvider object in attachements
            provider.loadItem(forTypeIdentifier: "public.image"){ data, _ in
                // Load Image data from image URL
                if let url = data as? URL {
                    if let imageData = try? Data(contentsOf: url) {
                        // Load Image as UIImage from image data
                        let uiimg = UIImage(data: imageData)!
                        // Convert to SwiftUI Image
                        let image = Image(uiImage: uiimg)
                        // [START] The following piece of code can be used to render swifUI views from UIKit
                        DispatchQueue.main.async {
                            let u = UIHostingController(
                                rootView: VStack {image.resizable().aspectRatio(contentMode: .fit)}
                            )
                            u.view.frame = (self.view.bounds)
                            self.view.addSubview(u.view)
                            self.addChild(u)
                        }
                        // [END]
                    }
                }
            }
        }
    }
}

Code snippet

We can create a custom SwiftUI struct and plug it with UIHostingController2.

import UIKit
import SwiftUI

struct ImageView: View {
    @State var image: Image
    
    var body: some View {
        VStack {
            Spacer()
            Text("πŸ‘‹ Hello, from share extension").font(.largeTitle)
            image.resizable().aspectRatio(contentMode: .fit)
            Spacer()
        }
    }
}


@objc(PrincipalClassName)
class ShareViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // self.extensionContext can be used to retrieve images, videos and urls from share extension input
        let extensionAttachments = (self.extensionContext!.inputItems.first as! NSExtensionItem).attachments
        for provider in extensionAttachments! {
            // loadItem can be used to extract different types of data from NSProvider object in attachements
            provider.loadItem(forTypeIdentifier: "public.image"){ data, _ in
                // Load Image data from image URL
                if let url = data as? URL {
                    if let imageData = try? Data(contentsOf: url) {
                        // Load Image as UIImage from image data
                        let uiimg = UIImage(data: imageData)!
                        // Convert to SwiftUI Image
                        let image = Image(uiImage: uiimg)
                        // [START] The following piece of code can be used to render swifUI views from UIKit
                        DispatchQueue.main.async {
                            let u = UIHostingController(
                                rootView: ImageView(image: image)
                            )
                            u.view.frame = (self.view.bounds)
                            self.view.addSubview(u.view)
                            self.addChild(u)
                        }
                        // [END]
                    }
                }
            }
        }
    }
}

Demonstration

A demonstration gif of share extension in action

Conclusion

In this tutorial, you’ve learnt how build a simple share extension for an iOS app. This knowledge can be good enough to start building one for your next project.

πŸ‘‹ βΈ» @TnvMadhav

References

Find more posts from following topics

1
12
13
14
15
16
17
18
19
2
20
2024
21
22
23
24
26
27
28
29
3
30
4
5
6
8
9
@binding
abbacchio
accurate-requests
anime
api-development
api-testing
api-testing-tools
array
automated-testing
bad-habits
base64-decoder
base64-encoder
biography
blog
blogging
books
browser
bulma-css
bulma.io
bulmacss
button-swiftui
chatgpt
chrome
clipboard
code
code-block
code-navigation
code-snippet
codecoverage
comparison
compile
computer-networks
configuring-debugger-for-django-in-vs-code
configuring-launch.json-for-python-debugger
copy
copy-to-clipboard
copy-to-clipboard-neovim
css
current-date
current-time
current-timestamp
database
debugger-setup-in-visual-studio-code
debugging-django-app-in-visual-studio-code
debugging-python-code-in-visual-studio-code
debugging-python-programs-with-visual-studio-code
debugging-python-with-virtual-environment-in-vs-code
december
developer-productivity
developers
development-workflow
django
django-rest-framework
dom
dynamic-sitemap-in-nextjs
encryption
engineering-dashboard
enumerate
experience
fiction
flowcharts
generics
git
git-diff
github
global-keyboard-shorcut
global-shortcut
go-hugo
go-programming
go-test
go-to-line
go-tool
go1.18
golang
golang-development
golden-wind
good-habits
gorilla-websocket
gpt
gpt-3.5
gpt-4
gpt-4-api
guide
gumroad
habits
habits-tracker-notion-template
hamburger-menu
hotkeys
html
hugo
ide
image
image-sharing
image-tool-for-ios
imagerenderer
include-timestamp
integrated-development-environment
ios
ios-16
ios16
iphone
iphone-13
iphone-13-pro
iphone-13-pro-max
javascript
jojos-bizzarre-adventure
jump-to-definition
keyboard-shortcut
leonardo-da-vinci
lessons
linux
logging
ls
lsp
macos
map
markdown
markdown-code
mental-programming
menu
menubarextra
mergesort
mermaid-syntax
mistake-tracker-notion
mobile-view
modifier
modulo-operation
navbar
navigationlink
navigationstack
neovim
nested-functions
next.js
nextjs
nextjs-markdown
nextjs-sitemap
nextjs-sitemaps
nice-shot
nice-shot-pro
notion
notion-api
notion-api-python
notion-budget
notion-budget-template
notion-budget-tracker
notion-bug-report-tracker
notion-dashboard
notion-expense-manager
notion-habits
notion-habits-dashboard
notion-habits-template
notion-habits-tracker
notion-habits-tracker-template
notion-issue-tracker
notion-mistake-tracker
notion-product
notion-product-dashboard
notion-product-roadmap
notion-product-roadmap-dashboard
notion-tasks
notion-tasks-dashboard
notion-tasks-template
notion-tasks-tracker
notion-template
notionworkspaces
november
october
openai
osx
pagination
personal-ifttt-framework
photospicker
photospickeritem
phpickerfilter
postgres
postman-capabilities
postman-request
pre-request-script
product-roadmap-notion-template
product-roadmap-template
productivity
programming
python
python-api
python-debugger-tutorial-for-vs-code
python-debugging-mode-in-vs-code
python-notion-api
python3
reading
real-time-communication
rehype
remark
request-data
running-debugger-in-visual-studio-code
running-django-app-in-debugging-mode
running-program-in-debugging-mode-in-vs-code
running-python-code-in-debugging-mode
safari
screenshot-app-for-ios
screenshot-app-ios
screenshot-ios
screenshot-tool-for-ios
september
set-current-timestamp
setting-up-debugger-in-vs-code-for-python
share-extension
sharelink
sharepreview
sharesheet
simple-websocket-server
sitemap
slice
slices
slider
sort
sorting
space-complexity
sql
ssh
step-by-step-guide
stocks-profits-tracker
stocks-profits-tracker-template
stocks-tracker
struct
sustained-vigilance
swift
swiftui
swiftui-button
swiftui-button-action
swiftui-button-style
table-of-contents
tasks-tracker-notion-template
tcp
test
testing
textfield-swiftui
til
tim-sort
time-complexity
timeliness
timestamp-integration
timsort
transferable
triggers-and-actions
tutorial
unittest
unix
us-stocks
usa-stocks
useful-ios-features
using-breakpoints-in-python-debugger
using-virtual-environment-with-python-debugger
vanilla-javascript
variable
vim
visual-mode
visual-studio-code
vs-code
vscode
vscode-go-to-line
walter-isaacson
web-sockets-in-go
websocket-client
websocket-programming
websocket-server
white-screen-of-death
wsod
xcode
xss