Custom @Environment value for share actions

artemnovichkov

Artem Novichkov

Posted on February 15, 2021

Custom @Environment value for share actions

SwiftUI has a lot of modern and useful features. One of my favourite is @Environment property wrapper. It allows you to get system-wide settings, for instance, current locale or color scheme. Since iOS 14.0 you can use openURL value to open URLs from apps easily.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension EnvironmentValues {

    /// Opens a URL using the appropriate system service.
    public var openURL: OpenURLAction { get }
}
Enter fullscreen mode Exit fullscreen mode

With just a one line of code you can extend behaviour of you views:

import SwiftUI

struct ContentView: View {

    @Environment(\.openURL) var openURL

    var body: some View {
        Button("Share") {
            openURL(URL(string: "https://blog.artemnovichkov.com")!)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

I was wondering how it works under the hood and tried to implement the same trick for sharing activity items with UIActivityViewController. Let's check the result!

Keys and values

At first we should create a custom key, conform to EnvironmentKey protocol and set a default value. It will be an empty struct for now:

struct ShareAction {}

struct ShareActionEnvironmentKey: EnvironmentKey {

    static let defaultValue: ShareAction = .init()
}
Enter fullscreen mode Exit fullscreen mode

Next, we should extend EnvironmentValues to make our ShareAction be available from the environment:

extension EnvironmentValues {

    var share: ShareAction {
        self[ShareActionEnvironmentKey]
    }
}
Enter fullscreen mode Exit fullscreen mode

The last thing is adding callAsFunction in ShareAction struct to use the same syntax:

struct ShareAction {

    func callAsFunction(_ activityItems: [Any]) {
        let vc = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
        UIApplication.shared.windows.first?.rootViewController?.present(vc, animated: true, completion: nil)
    }
}
Enter fullscreen mode Exit fullscreen mode

Unfortunately, there is no SwiftUI-way to open this controller. If you use a better way, let me know!

To read more about callable values of user-defined nominal types, check SE-0253 proposal on Github.

That's it, now we can use .share value and share activities from any view:

import SwiftUI

struct ContentView: View {

    @Environment(\.share) var share

    var body: some View {
        Button("Share") {
            share([URL(string: "https://blog.artemnovichkov.com")!])
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Related Resources

The final code is available here, and there is a list of related articles:

💖 💪 🙅 🚩
artemnovichkov
Artem Novichkov

Posted on February 15, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related