Custom @Environment value for share actions
2 min read • ––– views
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 }
}
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://www.artemnovichkov.com")!)
}
}
}
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()
}
Next, we should extend EnvironmentValues
to make ourShareAction
be available from the environment:
extension EnvironmentValues {
var share: ShareAction {
self[ShareActionEnvironmentKey]
}
}
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)
}
}
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://www.artemnovichkov.com")!])
}
}
}
Related Resources
The final code is available here, and there is a list of related articles: