Sharing Content in SwiftUI with ShareLink

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. Additionally, 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:

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:

struct ContentView: View {
    let url = URL(string: "https://serialcoder.dev")
    
    var body: some View {
        ShareLink(item: url!)
    }
}

Configuring the share sheet

It's quite often 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:

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:

ShareLink(item: "Shareable Text!") {
    Label("Share my Text", systemImage: "text.document")
}

Besides the button's appearance, we can also provide additional data that is used if only the selected sharing method supports it. For instance, we can provide the subject and message for content to share via email:

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:

ShareLink(
    item: "Sharable Text!",
    preview: SharePreview("Ready to share?", image: previewImage)
)

Note that previewImage is an Image object:

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:

struct User: Codable, Transferable {
    static var transferRepresentation: some TransferRepresentation {
        CodableRepresentation(contentType: .data)
    }
    
    var username: String
    var accessLevel: String
}

Now, let's declare the following User instance:

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:

ShareLink(item: user, preview: SharePreview("My User Info")) {
    Label("Share User", systemImage: "person")
}

Important

Conforming to Transferable protocol is necessary only when we want to share actual objects of custom types. If we just need to encode an object into a Data object (i.e. using JSONEncoder, PropertyListEncoder, etc) 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:

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:

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 aware of how to use of it. Knowing now how to do that, just go ahead and apply what you met here. Thanks for reading!