Custom @Environment value for share actions
The article is now available on my blog:
https://www.artemnovichkov.com/blog/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 useopenURL
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://blog.artemnovichkov.com")!) } } }
I was wondering how it works under the hood and tried to implement the same trick for sharing activity items withUIActivityViewController
. Let's check the result!
Keys and values
At first we should create a custom key, conform toEnvironmentKey
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 extendEnvironmentValues
to make ourShareAction
be available from the environment:
extension EnvironmentValues { var share: ShareAction { self[ShareActionEnvironmentKey] } }
The last thing is addingcallAsFunction
inShareAction
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://blog.artemnovichkov.com")!]) } } }
Related Resources
The final code is available here, and there is a list of related articles:
- Environment Documentation
- What is @Environment in SwiftUI by Sarun W.
- The power of Environment in SwiftUI by Majid Jabrayilov
- How to use @EnvironmentObject to share data between views by Paul Hudson