The Share Sheet is the system dialog we all know for sharing content and that many apps provide. Up to iOS 16 there wasn't a SwiftUI-native way to present it; resorting to UIKit's activity controller was the standard course of action. However, things have changed, and SwiftUI has a dedicated view that presents the share sheet, named ShareLink. With it, we can present the built-in controller to share any content we want, as long as it conforms to Transferable protocol. Along with it, we can perform some configuration through the various available initializers.
Let's get to know more about it.
Sharing simple content and URLs
The ShareLink
view has a variety of initializers which allow us to share any of the next two, along with additional data:
- URL
- Any other type that conforms to Transferable protocol
Various built-in types fall in the second category, such as String
, and that's important because sharing text is common. The simplest way to use ShareLink
is this:
1 2 3 |
ShareLink(item: "This text will be shared!") |
The provided argument is a String
value to share. Without any other arguments, this presents a predefined button with an image and the "Share" text. When pressed, the share sheet shows up:

In a similar fashion we can share URL
objects, as demonstrated next:
1 2 3 4 5 6 7 8 9 |
struct ContentView: View { let url = URL(string: "https://serialcoder.dev") var body: some View { ShareLink(item: url!) } } |

Configuring the share sheet
Quite often is necessary to change the default appearance of the share button, and ShareLink
view provides a couple of ways to do that. To change just the title, we can provide it as the first argument before the item
parameter. For instance:
1 2 3 |
ShareLink("My website", item: url!) |

To change both the title and the image of the button, there's the label
parameter where we provide a Label
view. With it, we are able to replace the default image and text:
1 2 3 4 5 |
ShareLink(item: "Shareable Text!") { Label("Share my Text", systemImage: "text.document") } |

Besides the button's appearance, we can also provide additional data that appears if only the selected sharing method allows it. For instance, we can provide the subject and message for content to share via email:
1 2 3 4 5 6 7 |
ShareLink( item: url!, subject: Text("SerialCoder.dev URL"), message: Text("I'm sending you the URL to my website") ) |
The share sheet automatically shows a default preview image and text depending on the kind of data we're trying to share. We can optionally set a different title and image using an initializer that contains the preview
parameter. It expects as argument a SharePreview
instance, where we set the desired title and image:
1 2 3 4 5 6 |
ShareLink( item: "Sharable Text!", preview: SharePreview("Ready to share?", image: previewImage) ) |
Note that previewImage
is an Image
object:
1 2 3 |
let previewImage = Image("IMAGE_NAME") |
Sharing custom types
Share sheet does not exist for String or URL objects only. Actually, we can share any object we want, even of custom types, as long as these conform to the Transferable
protocol.
☝️ Note:
You can read about the Transferable protocol in this and this older posts.
To see an example of this case, suppose we have the following User
type with two properties; username
and accessLevel
. This custom type conforms to both Codable
and Transferable
protocols, specifying the CodableRepresentation
as the required transfer representation:
1 2 3 4 5 6 7 8 9 10 |
struct User: Codable, Transferable { static var transferRepresentation: some TransferRepresentation { CodableRepresentation(contentType: .data) } var username: String var accessLevel: String } |
Now, let the following User
instance:
1 2 3 |
let user = User(username: "Gabriel", accessLevel: "admin") |
To share the user
object, we just need to choose a ShareLink
initializer with the preview
parameter in it. For instance:
1 2 3 4 5 |
ShareLink(item: user, preview: SharePreview("My User Info")) { Label("Share User", systemImage: "person") } |

It's important to clarify something here:
Conforming to Transferable
protocol is necessary only when we want to share actual objects of custom types. If we just need to encode (i.e. using JSONEncoder, PropertyListEncoder, etc) an object into a Data
object and then share the latter, then adopting Transferable
is not mandatory.
Say, for instance, that the User
type of the previous example does not adopt Transferable
:
1 2 3 4 5 6 |
struct User: Codable { var username: String var accessLevel: String } |
This does not prevent us from converting the user
object we met previously into a Data
object and then share that:
1 2 3 4 5 6 7 |
if let data = try? JSONEncoder().encode(user) { ShareLink(item: data, preview: SharePreview("Sharing User Data")) { Label("Share Data", systemImage: "ellipsis.curlybraces") } } |
Keep this distinction in mind in case you don't want to share the object of a custom type itself, but a Data representation of it.
Wrapping up
Presenting the built-in share sheet using the ShareLink
view in SwiftUI is one of the most easy tasks for a developer. There's a variety of initializers to use and options to provide. Adding share features to an app is important, as users are familiar with the share sheet and make use of it. Knowing now how to do that, just go ahead and apply what you met here. Thanks for reading!