diff --git a/README.md b/README.md index 562babde1..c38e97ac1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Package Releases](https://img.shields.io/github/v/release/vsanthanam/SafariView)](https://github.com/vsanthanam/SafariView/releases) [![Build Statis](https://img.shields.io/github/actions/workflow/status/vsanthanam/SafariView/xcodebuild-build-test.yml)](https://github.com/vsanthanam/SafariView/actions) [![Swift Version](https://img.shields.io/badge/swift-5.8-critical)](https://swift.org) -[![Supported Platforms](https://img.shields.io/badge/platform-iOS%2012-lightgrey)](https://developer.apple.com) +[![Supported Platforms](https://img.shields.io/badge/platform-iOS%2016-lightgrey)](https://developer.apple.com) A SwiftUI wrapper around `SFSafariViewController` diff --git a/Sources/SafariView/EnvironmentValues.swift b/Sources/SafariView/Environment.swift similarity index 72% rename from Sources/SafariView/EnvironmentValues.swift rename to Sources/SafariView/Environment.swift index 10335547f..fd285c7df 100644 --- a/Sources/SafariView/EnvironmentValues.swift +++ b/Sources/SafariView/Environment.swift @@ -1,5 +1,5 @@ // SafariView -// EnvironmentValues.swift +// Environment.swift // // MIT License // @@ -27,90 +27,64 @@ import SwiftUI public extension EnvironmentValues { - /// The configuration used for ``SafariView`` + /// The additional activies to include the share sheet displayed inside a ``SafariView`` /// - /// You can retrieve this value for the currnet scope using the @Environment property wrapper + /// You can retrieve this value for the currnet scope using the `@Environment` property wrapper /// /// ```swift /// struct MyView: View { /// - /// @Environment(\.safariViewConfiguration) - /// var safariViewConriguration + /// @Environment(\.safariViewIncludedActivities) + /// var safariViewIncludedActivities /// /// } - /// ``` - /// - /// This property is equivelent to `SFSafariViewController`'s `.preferredControlTintColor` property - var safariViewConfiguration: SafariView.Configuration { - get { self[SafariViewConfigurationEnvironmentKey.self] } - set { self[SafariViewConfigurationEnvironmentKey.self] = newValue } + /// ` + var safariViewIncludedActivities: SafariView.IncludedActivities { + get { self[SafariViewIncludedActivitiesEnvironmentKey.self] } + set { self[SafariViewIncludedActivitiesEnvironmentKey.self] = newValue } } - /// The control tint color used for ``SafariView`` + /// The activity types to exclude from the share sheet displayed inside a ``SafariView`` /// - /// You can retrieve this value for the currnet scope using the @Environment property wrapper + /// You can retrieve this value for the currnet scope using the `@Environment` property wrapper /// /// ```swift /// struct MyView: View { /// - /// @Environment(\.safariViewControlTintColor) - /// var safariControlTintColor + /// @Environment(\.safariViewExcludedActivityTypes) + /// var safariViewExcludedActivityTypes /// /// } /// ``` - /// - /// This property is equivelent to `SFSafariViewController`'s `.preferredControlTintColor` property + var safariViewExcludedActivityTypes: SafariView.ExcludedActivityTypes { + get { self[SafariViewExcludedActivityTypesEnvironmentKey.self] } + set { self[SafariViewExcludedActivityTypesEnvironmentKey.self] = newValue } + } + +} + +extension EnvironmentValues { + + var safariViewConfiguration: SafariView.Configuration { + get { self[SafariViewConfigurationEnvironmentKey.self] } + set { self[SafariViewConfigurationEnvironmentKey.self] = newValue } + } + var safariViewControlTintColor: Color { get { self[SafariViewControlTintColorEnvironmentKey.self] } set { self[SafariViewControlTintColorEnvironmentKey.self] = newValue } } - /// The bar tint color used for ``SafariView`` - /// - /// You can retrieve this value for the currnet scope using the @Environment property wrapper - /// - /// ```swift - /// struct MyView: View { - /// @Environment(\.safariViewControlBarColor) - /// var safariBarTintColor - /// } - /// ``` - /// - /// This property is equivelent to `SFSafariViewController`'s `.preferredBarTintColor` property var safariViewBarTintColor: Color? { get { self[SafariViewBarTintColorEnvironmentKey.self] } set { self[SafariViewBarTintColorEnvironmentKey.self] = newValue } } - /// Set the safari view's dismiss button style - /// - /// You can retrieve this value for the currnet scope using the @Environment property wrapper - /// - /// ```swift - /// struct MyView: View { - /// - /// @Environment(\.safariViewDismissButtonStyle) - /// var safariViewDismissButtonStyle - /// - /// } - /// ``` - /// - /// This property is equivelent to `SFSafariViewController`'s `.dismissButtonStyle` property var safariViewDismissButtonStyle: SafariView.DismissButtonStyle { get { self[SafariViewDismissButtonStyleEnvironmentKey.self] } set { self[SafariViewDismissButtonStyleEnvironmentKey.self] = newValue } } - var safariViewIncludedActivities: SafariView.IncludedActivities { - get { self[SafariViewIncludedActivitiesEnvironmentKey.self] } - set { self[SafariViewIncludedActivitiesEnvironmentKey.self] = newValue } - } - - var safariViewExcludedActivityTypes: SafariView.ExcludedActivityTypes { - get { self[SafariViewExcludedActivityTypesEnvironmentKey.self] } - set { self[SafariViewExcludedActivityTypesEnvironmentKey.self] = newValue } - } - } private struct SafariViewConfigurationEnvironmentKey: EnvironmentKey { diff --git a/Sources/SafariView/ExcludedActivityTypes.swift b/Sources/SafariView/ExcludedActivityTypes.swift index 6a054a996..876814fcb 100644 --- a/Sources/SafariView/ExcludedActivityTypes.swift +++ b/Sources/SafariView/ExcludedActivityTypes.swift @@ -28,13 +28,17 @@ import UIKit public extension SafariView { - /// A struct used to exclude activity types from a SafariView + /// A struct used to exclude activity types from the share sheet of a ``SafariView``. struct ExcludedActivityTypes: ExpressibleByArrayLiteral { + /// Exclude activity types conditionally, based on the URL and/or page title. + /// - Parameter excludedActivityTypes: Closure used to exclude activity types public init(_ excludedActivityTypes: @escaping (URL, String?) -> [UIActivity.ActivityType]) { self.excludedActivityTypes = excludedActivityTypes } + /// Exclude activity types using a predefined list + /// - Parameter excludedActivityTypes: A list of activity types to exclude from the share sheet public init(_ excludedActivityTypes: [UIActivity.ActivityType] = []) { self.excludedActivityTypes = { _, _ in excludedActivityTypes } } @@ -49,8 +53,19 @@ public extension SafariView { // MARK: - ExpressiblyByArrayLiteral + /// The type of the elements of an array literal. public typealias ArrayLiteralElement = UIActivity.ActivityType + /// Creates an `ExcludedActivityTypes` containing the elements of the given array literal + /// + /// Do not call this initializer directly. It is used by the compiler when you use an array literal. Instead, create a new `ExcludedActivityTypes` using an array literal as its value by enclosing a comma-separated list of values in square brackets. You can use an array literal anywhere an `ExcludedActivityTypes` is expected by the type context. For example: + /// + /// ```swift + /// let excluded: SafariView.ExcludedActivityTypes = [.addToReadingList, .airDrop, .print, .sharePlay] + /// ``` + /// + /// In this example, the assignment to the `excluded` constant calls this array literal initializer behind the scenes. + /// - Parameter elements: A variadic list of activity types. public init(arrayLiteral elements: ArrayLiteralElement...) { self.init { _, _ in elements } } diff --git a/Sources/SafariView/IncludedActivities.swift b/Sources/SafariView/IncludedActivities.swift index a6f983eb5..5bd7fc5a3 100644 --- a/Sources/SafariView/IncludedActivities.swift +++ b/Sources/SafariView/IncludedActivities.swift @@ -28,14 +28,19 @@ import UIKit public extension SafariView { + /// A struct used to include custom activities in the share sheet of a ``SafariView`` struct IncludedActivities: ExpressibleByArrayLiteral { // MARK: - Initializers + /// Include activities conditionally, based on the URL and/or page title. + /// - Parameter includedActivities: Closure used to provide activitues public init(_ includedActivities: @escaping (_ url: URL, _ pageTitle: String?) -> [UIActivity]) { self.includedActivities = includedActivities } + /// Include activities using a predefined list + /// - Parameter includedActivities: A list of activities to include in the share sheet public init(_ includedActivities: [UIActivity]) { self.includedActivities = { _, _ in includedActivities } } @@ -50,8 +55,19 @@ public extension SafariView { // MARK: - ExpressiblyByArrayLiteral + /// The type of the elements of an array literal. public typealias ArrayLiteralElement = UIActivity + /// Creates an `IncludedActivities` containing the elements of the given array literal + /// + /// Do not call this initializer directly. It is used by the compiler when you use an array literal. Instead, create a new `IncludedActivities` using an array literal as its value by enclosing a comma-separated list of values in square brackets. You can use an array literal anywhere an `IncludedActivities` is expected by the type context. For example: + /// + /// ```swift + /// let included: SafariView.IncludedActivities = [someActivity, someOtherActivity] + /// ``` + /// + /// In this example, the assignment to the `included` constant calls this array literal initializer behind the scenes. + /// - Parameter elements: A variadic list of activities. public init(arrayLiteral elements: ArrayLiteralElement...) { self.init { _, _ in elements } } diff --git a/Sources/SafariView/Modifiers.swift b/Sources/SafariView/Modifiers.swift index d42f83e05..d3aa4fffb 100644 --- a/Sources/SafariView/Modifiers.swift +++ b/Sources/SafariView/Modifiers.swift @@ -79,25 +79,122 @@ public extension View { return ModifiedContent(content: self, modifier: modifier) } - func includedSafariActivities(_ includedActivities: SafariView.IncludedActivities) -> some View { - let modifier = SafariViewIncludedActivitiesModifier(includedActivities: includedActivities) + /// Include additional activities in the share sheet of safari views within this view. + /// + /// Use this modifier to include a list of activities to display in the share sheet of a ``SafariView`` + /// + /// This modifier replaces the the supplied activities to the existing values in the environment. + /// If you wish to append values to the existing environment instead, you'll need to retrieve them first, like so: + /// + /// ```swift + /// struct MyView: View { + /// + /// @Environment(\.safariViewIncludedActivities) + /// var safariActivities + /// + /// var body: some View { + /// SafariView(url: some_url) + /// .includedSafariActivities(safariActivities + newValues) + /// } + /// + /// } + /// ``` + /// + /// - Parameter activities: The activities to include. You may use an array literal of `UIActivity` types. + /// - Returns: The modified content + func includedSafariActivities(_ activities: SafariView.IncludedActivities) -> some View { + let modifier = SafariViewIncludedActivitiesModifier(activities: activities) return ModifiedContent(content: self, modifier: modifier) } - func includedSafariActivities(_ includedActivities: @escaping (_ url: URL, _ pageTitle: String?) -> [UIActivity]) -> some View { - let activities = SafariView.IncludedActivities(includedActivities) - let modifier = SafariViewIncludedActivitiesModifier(includedActivities: activities) + /// Conditionally include activities in the share sheet of safari views within this view. + /// + /// Use this modifier to conditionally include additional activities to display in the share sheet of a ``SafariView``, based in the URL and page title. + /// + /// This modifier replaces the the supplied activities to the existing values in the environment. + /// If you wish to append values to the existing environment instead, you'll need to retrieve them first, like so: + /// + /// ```swift + /// struct MyView: View { + /// + /// @Environment(\.safariViewIncludedActivities) + /// var safariActivities + /// + /// var body: some View { + /// SafariView(url: some_url) + /// .includedSafariActivities { url, pageTitle in + /// // custom logic + /// return safariActivities + newValues + /// } + /// } + /// + /// } + /// ``` + /// + /// - Parameter activities: Closure used to conditionally include activities + /// - Returns: The modified content + func includedSafariActivities(_ activities: @escaping (_ url: URL, _ pageTitle: String?) -> [UIActivity]) -> some View { + let activities = SafariView.IncludedActivities(activities) + let modifier = SafariViewIncludedActivitiesModifier(activities: activities) return ModifiedContent(content: self, modifier: modifier) } - func excludedSafariActivityTypes(_ excludedActivityTypes: SafariView.ExcludedActivityTypes) -> some View { - let modifier = SafariViewExcludedActivityTypesModifier(excludedActivityTypes: excludedActivityTypes) + /// Exclude activity types from the share sheet of safari views within this view. + /// + /// Use this modifier to exclude a list of activity types from the share sheet of a ``SafariView`` + /// + /// This modifier replaces the the supplied activity types to the existing values in the environment. + /// If you wish to append values to the existing environment instead, you'll need to retrieve them first, like so: + /// + /// ```swift + /// struct MyView: View { + /// + /// @Environment(\.safariViewExcludedActivityTypes) + /// var excludedTypes + /// + /// var body: some View { + /// SafariView(url: some_url) + /// .excludedSafariActivityTypes(excludedTypes + newValues) + /// } + /// + /// } + /// ``` + /// + /// - Parameter activityTypes: The activity types to exclude. You may use an array literal of `UIActivity.ActivityType` values. + /// - Returns: The modified content + func excludedSafariActivityTypes(_ activityTypes: SafariView.ExcludedActivityTypes) -> some View { + let modifier = SafariViewExcludedActivityTypesModifier(activityTypes: activityTypes) return ModifiedContent(content: self, modifier: modifier) } - func excludedSafariActivityTypes(_ excludedActivityTypes: @escaping (_ url: URL, _ pageTitle: String?) -> [UIActivity.ActivityType]) -> some View { - let activityTypes = SafariView.ExcludedActivityTypes(excludedActivityTypes) - let modifier = SafariViewExcludedActivityTypesModifier(excludedActivityTypes: activityTypes) + /// Conditionally exclude activity types from the share sheet of safari views within this view. + /// + /// Use this modifier to conditionally exclude activity types from the share sheet of a ``SafariView``, based on the URL and page title. + /// + /// This modifier replaces the the supplied activity types to the existing values in the environment. + /// If you wish to append values to the existing environment instead, you'll need to retrieve them first, like so: + /// + /// ```swift + /// struct MyView: View { + /// + /// @Environment(\.safariViewExcludedActivityTypes) + /// var excludedTypes + /// + /// var body: some View { + /// SafariView(url: some_url) { url, pageTitle in + /// // custom logic + /// return excludedTypes + newValues + /// } + /// } + /// + /// } + /// ``` + /// + /// - Parameter activityTypes: Closure used to conditionally exclude activities + /// - Returns: The modified content + func excludedSafariActivityTypes(_ activityTypes: @escaping (_ url: URL, _ pageTitle: String?) -> [UIActivity.ActivityType]) -> some View { + let activityTypes = SafariView.ExcludedActivityTypes(activityTypes) + let modifier = SafariViewExcludedActivityTypesModifier(activityTypes: activityTypes) return ModifiedContent(content: self, modifier: modifier) } @@ -195,8 +292,8 @@ private struct SafariViewIncludedActivitiesModifier: ViewModifier { // MARK: - Initializers - init(includedActivities: SafariView.IncludedActivities) { - self.includedActivities = includedActivities + init(activities: SafariView.IncludedActivities) { + self.activities = activities } // MARK: - ViewModifier @@ -204,12 +301,15 @@ private struct SafariViewIncludedActivitiesModifier: ViewModifier { @ViewBuilder func body(content: Content) -> some View { content - .environment(\.safariViewIncludedActivities, includedActivities) + .environment(\.safariViewIncludedActivities, safariViewIncludedActivities + activities) } // MARK: - Private - private let includedActivities: SafariView.IncludedActivities + @Environment(\.safariViewIncludedActivities) + private var safariViewIncludedActivities: SafariView.IncludedActivities + + private let activities: SafariView.IncludedActivities } @@ -217,8 +317,8 @@ private struct SafariViewExcludedActivityTypesModifier: ViewModifier { // MARK: - Initializers - init(excludedActivityTypes: SafariView.ExcludedActivityTypes) { - self.excludedActivityTypes = excludedActivityTypes + init(activityTypes: SafariView.ExcludedActivityTypes) { + self.activityTypes = activityTypes } // MARK: - ViewBuilder @@ -226,11 +326,14 @@ private struct SafariViewExcludedActivityTypesModifier: ViewModifier { @ViewBuilder func body(content: Content) -> some View { content - .environment(\.safariViewExcludedActivityTypes, excludedActivityTypes) + .environment(\.safariViewExcludedActivityTypes, safariExcludedActivityTypes + activityTypes) } // MARK: - Private - private let excludedActivityTypes: SafariView.ExcludedActivityTypes + @Environment(\.safariViewExcludedActivityTypes) + private var safariExcludedActivityTypes: SafariView.ExcludedActivityTypes + + private let activityTypes: SafariView.ExcludedActivityTypes } diff --git a/Sources/SafariView/SafariView.docc/Environment.md b/Sources/SafariView/SafariView.docc/Environment.md index d33f34bac..1f04939d7 100644 --- a/Sources/SafariView/SafariView.docc/Environment.md +++ b/Sources/SafariView/SafariView.docc/Environment.md @@ -8,9 +8,5 @@ Some Documentation About Environment Values ### Values -- ``safariViewConfiguration`` -- ``safariViewBarTintColor`` -- ``safariViewControlTintColor`` -- ``safariViewDismissButtonStyle`` - ``safariViewIncludedActivities`` - ``safariViewExcludedActivityTypes`` diff --git a/Sources/SafariView/SafariView.docc/ExcludedActivityTypes.md b/Sources/SafariView/SafariView.docc/ExcludedActivityTypes.md new file mode 100644 index 000000000..030cc4fca --- /dev/null +++ b/Sources/SafariView/SafariView.docc/ExcludedActivityTypes.md @@ -0,0 +1,29 @@ +# ``SafariView/SafariView/ExcludedActivityTypes`` + +@Metadata { + @DocumentationExtension(mergeBehavior: append) +} + +You can initialize instances of this type using an array literal of `UIActivity.ActivityType` values. For example: + +```swift +let excluded: SafariView.ExcludedActivityTypes = [.addToReadingList, .airDrop, .print, .sharePlay] +``` + +To change the the excluded activity types used by ``SafariView`` at the current scope, use the ``SwiftUI/View/excludedSafariActivityTypes(_:)-tvrg`` view modifier, or the ``SwiftUI/EnvironmentValues/safariViewExcludedActivityTypes`` environment value. + +## Topics + +### Initializers + +- ``init(_:)-1ktmq`` +- ``init(_:)-67duh`` + +### Operators + +- ``+(_:_:)`` + +### Literal Expression Support + +- ``ArrayLiteralElement`` +- ``init(arrayLiteral:)`` diff --git a/Sources/SafariView/SafariView.docc/IncludedActivities.md b/Sources/SafariView/SafariView.docc/IncludedActivities.md new file mode 100644 index 000000000..cb187a841 --- /dev/null +++ b/Sources/SafariView/SafariView.docc/IncludedActivities.md @@ -0,0 +1,29 @@ +# ``SafariView/SafariView/IncludedActivities`` + +@Metadata { + @DocumentationExtension(mergeBehavior: append) +} + +You can initialize instances of this type using an array literal of `UIActivity` values. For example: + +```swift +let excluded: SafariView.IncludedActivities = [someActivity, someOtherActivity] +``` + +To change the the included activities used by ``SafariView`` at the current scope, use the ``SwiftUI/View/includedSafariActivities(_:)-2u8l9`` view modifier, or the ``SwiftUI/EnvironmentValues/safariViewIncludedActivities`` environment value. + +## Topics + +### Initializers + +- ``init(_:)-6d955`` +- ``init(_:)-9q5v6`` + +### Operators + +- ``+(_:_:)`` + +### Literal Expression Support + +- ``init(arrayLiteral:)`` +- ``ArrayLiteralElement`` diff --git a/Sources/SafariView/SafariView.docc/Modifiers.md b/Sources/SafariView/SafariView.docc/Modifiers.md index 0cc3d5170..3d4cacead 100644 --- a/Sources/SafariView/SafariView.docc/Modifiers.md +++ b/Sources/SafariView/SafariView.docc/Modifiers.md @@ -9,6 +9,9 @@ Some Content About View Modifiers ### Configuration - ``safariConfiguration(_:)`` + +### Appearance + - ``safariBarTintColor(_:)`` - ``safariControlTintColor(_:)`` - ``safariDismissButtonStyle(_:)`` @@ -22,7 +25,7 @@ Some Content About View Modifiers ### Activities -- ``includedSafariActivities(_:)-362lz`` - ``includedSafariActivities(_:)-2u8l9`` -- ``excludedSafariActivityTypes(_:)-1v8zq`` +- ``includedSafariActivities(_:)-362lz`` - ``excludedSafariActivityTypes(_:)-tvrg`` +- ``excludedSafariActivityTypes(_:)-1v8zq`` diff --git a/Sources/SafariView/SafariView.docc/SafariView.md b/Sources/SafariView/SafariView.docc/SafariView.md index 97c940ee8..0d3e09ce9 100644 --- a/Sources/SafariView/SafariView.docc/SafariView.md +++ b/Sources/SafariView/SafariView.docc/SafariView.md @@ -12,9 +12,6 @@ UI features include the following: - A read-only address field with a security indicator and a Reader button - An Action button that invokes an activity view controller offering custom services from your app, and activities, such as messaging, from the system and other extensions - A Done button, back and forward navigation buttons, and a button to open the page directly in Safari -- On devices that support 3D Touch, automatic Peek and Pop for links and detected data - -To learn about 3D Touch, see [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/) and [Adopting 3D Touch on iPhone](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/index.html#//apple_ref/doc/uid/TP40016543). The library contains a single `View`-conforming struct, also named `SafariView`, as well as several view modifiers used to control the appearance, behavior and presentation of a ``SafariView/SafariView`` diff --git a/Sources/SafariView/SafariView.docc/View.md b/Sources/SafariView/SafariView.docc/View.md index e4b444162..0f959f343 100644 --- a/Sources/SafariView/SafariView.docc/View.md +++ b/Sources/SafariView/SafariView.docc/View.md @@ -4,6 +4,8 @@ @DocumentationExtension(mergeBehavior: append) } +For configuration, appearance, and presentation options, see the documentation for the included view modifiers here: ``SwiftUI/View``. + ## Topics ### Creating a Safari View diff --git a/Sources/SafariView/SafariView.swift b/Sources/SafariView/SafariView.swift index d37b032f9..4149c0449 100644 --- a/Sources/SafariView/SafariView.swift +++ b/Sources/SafariView/SafariView.swift @@ -141,6 +141,9 @@ public struct SafariView: View { @Environment(\.safariViewBarTintColor) private var barTintColor: Color? + @Environment(\.safariViewControlTintColor) + private var controlTintColor: Color + @Environment(\.safariViewDismissButtonStyle) private var dismissButtonStyle: DismissButtonStyle @@ -157,7 +160,7 @@ public struct SafariView: View { private func apply(to controller: SFSafariViewController) { controller.preferredBarTintColor = barTintColor.map(UIColor.init) - controller.preferredControlTintColor = UIColor(Color.accentColor) + controller.preferredControlTintColor = UIColor(controlTintColor) controller.dismissButtonStyle = dismissButtonStyle } @@ -661,6 +664,7 @@ public struct SafariView: View { } private extension UIView { + var controller: UIViewController? { if let nextResponder = next as? UIViewController { return nextResponder @@ -670,6 +674,7 @@ private extension UIView { return nil } } + } private extension SafariView.Configuration {