Sponsored
Sponsor This Blog
Connect with a targeted audience of iOS developers and Swift engineers. Promote your developer tools, courses, or services.
Learn More →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://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.self]
}
}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)
let windowScenes = UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
let activeScene = windowScenes
.filter { $0.activationState == .foregroundActive }
let firstActiveScene = activeScene.first
let keyWindow = firstActiveScene?.keyWindow
keyWindow?.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://artemnovichkov.com")!])
}
}
}Using ShareLink
Since iOS 16.0, you can use ShareLink view to share activities. It's a more SwiftUI-way to share activities and it's more convenient to use.
ShareLink(item: URL(string: "https://artemnovichkov.com")!) {
Text("Share")
}Related Resources
The final code is available here, and there is a list of related articles:
