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: