Repository navigation
Deprecate WebView and add an environment-aware Color.rgbo(in:) - #6
Merged
Merged
Conversation
Two public helpers now have a built-in counterpart that covers what they do, but only from an OS version above the package minimum. They are deprecated per platform from exactly that version, so only apps whose deployment target already includes the replacement see a warning; nothing is removed in this minor release. - Color.rgbo: SwiftUI's resolve(in:) returns a Color.Resolved with red, green, blue and opacity (iOS 17, macOS 14, tvOS 17, watchOS 10, visionOS 1). It also resolves dynamic colors against the given environment, which rgbo cannot do. - WebView: WebKit ships a native SwiftUI WebView with WebPage (iOS 26, macOS 26, visionOS 26). Using WebView as a type in files that import both modules is ambiguous, so the docs explain the qualified name and how WebPage covers link handling and scrolling. The color space conversions inside the package read the components through a new internal property, so the package itself compiles without deprecation warnings on visionOS, where rgbo is deprecated from the package minimum on. The DocC articles move both symbols into a Deprecated group that points to the replacement.
The package had no tests. CI uses the swiftbuild build system so that the String Catalog is compiled, which the tests depend on.
Deprecating rgbo in favor of resolve(in:) traded a readable API for a less readable one. rgbo stays public and gains rgbo(in:), which uses resolve(in:) where available, so a view gets the components it actually displays, including color scheme overrides. On older OS versions it falls back to rgbo. The parameterless rgbo keeps resolving through UIColor/NSColor: without a view there is no environment, and EnvironmentValues() would always resolve the light variant, while the native path follows the app's current appearance and contrast setting. On macOS it now reads extended sRGB instead of device RGB, which clipped wide-gamut colors and differed from UIColor and Color.Resolved.
hsbo still converted to device RGB on macOS, so HSB values and every change(.hueHSB/.saturation/.brightness, …) clipped wide-gamut colors while rgbo no longer did. getHue on extended sRGB components returns saturation and brightness above 1 for such colors, and Color(hue:saturation:brightness:opacity:) recreates them exactly. The rgbo documentation now says precisely which traits dynamic colors resolve against and that wide-gamut components must be clamped before converting to a fixed range. The test comparing rgbo with rgbo(in:) is skipped when "Increase contrast" is on, and the environment test only runs where resolve(in:) exists.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Ahead of 2.1.0, every public symbol in
Sources/HandySwiftUIwas checked for a built-in SwiftUI, Foundation or system API that now does the same job. One helper,WebView, has a real replacement, but only from OS 26, so it is deprecated per platform from exactly that version.Color.rgbostays public:resolve(in:)is not a readable replacement. Instead,rgbogains an environment-aware overload built onresolve(in:). Nothing is removed.WebViewWebViewwithWebPage(NavigationDecidingfor link taps,callJavaScript(_:)/webViewScrollPosition(_:)for the scroll offset)WebViewis unavailable on tvOS and watchOS, and so is HandySwiftUI's)@available(iOS, deprecated: 26.0, …), macOS 26, visionOS 26. Norenamed:, since the initializers differColor.rgboThe
WebViewdoc comment explains the migration, including one trap: in a file that imports both WebKit and HandySwiftUI,WebViewused as a type is ambiguous and needsHandySwiftUI.WebView. The DocC article listsWebViewin a "Deprecated" topic group.rgboand the newrgbo(in:)rgbo(in: EnvironmentValues)(new) returns the same tuple. Starting with iOS 17, macOS 14, tvOS 17, watchOS 10 and visionOS 1, it readsColor.resolve(in:), so a view gets the components it actually displays, e.g. the dark variant of.primaryin dark mode or under.environment(\.colorScheme, .dark). On older versions it returnsrgbo.rgbo(parameterless) keeps resolving throughUIColor/NSColor. A first attempt routed it throughresolve(in: EnvironmentValues()), but measurements showed that this always returns the light variant of dynamic colors, while the native path follows the app's current appearance and contrast setting (see the table below). Without a view there is no environment to pass, so the native path is the correct one for the parameterless variant.rgboandhsbonow convert to extended sRGB instead of device RGB. Device RGB clipped wide-gamut colors: Display P3 red came out as1, 0, 0, a different color than displayed. Extended sRGB is also whatUIColorandColor.Resolveduse, so for plain colors macOS, the iOS-based platforms andrgbo(in:)now agree. Dynamic colors can still differ betweenrgboandrgbo(in:), because they resolve against different traits. For every sRGB color the values are unchanged. The internal color space conversions (hlco,change(_:by:), …) build onrgbo/hsboand get the same correction.getHueon extended sRGB components returns saturation and brightness above 1 for wide-gamut colors, andColor(hue:saturation:brightness:opacity:)recreates them exactly (Display P3 red:h 0.9903, s 1.2075, b 1.0931→1.0931, −0.2268, −0.1501).rgbocan now return components below 0 or above 1 for wide-gamut colors, as it always did on iOS. Code likeUInt8(red * 255)must clamp first; the doc comment says so.rgbodoc comment now says precisely which traits dynamic colors resolve against:UITraitCollection.currenton iOS-based platforms (usually default traits outside of trait-aware callbacks) andNSAppearance.currentDrawing()on macOS. It points torgbo(in:)for the values a view actually displays.Measured on macOS 27 with the test process in dark appearance (red, green, blue, opacity):
rgbo(device RGB)rgboresolve(in: EnvironmentValues())rgbo(in:)lightrgbo(in:)dark.redColor(red: 0.2, green: 0.4, blue: 0.6)Color(.sRGB, …, opacity: 0.5).primaryColor(.displayP3, red: 1, …).accentColor.accentColordiffers between the two paths in a test process without an asset catalog:NSColorfalls back to the user's system accent color, SwiftUI's environment to its default blue. Both are consistent within their path, so this was left as is.Deliberately not deprecated
These have a related built-in API, but it is not a real equivalent:
DisclosureSection:Section(_:isExpanded:content:)exists since iOS 17 / macOS 14 / tvOS 17 / watchOS 10, but the SDK docs say only sidebar lists show a disclosure control ("Not all contexts provide a default control to trigger collapse or expansion").DisclosureSectionexists forForms, where the built-in one shows nothing.UIImage.heicData(compressionQuality:): UIKit'sheicData()(iOS 17, tvOS 17, watchOS 10) has no compression quality parameter.Color.rgbo,hsbo,hlco,toHex(alpha:),init(hex:):Color.Resolvedonly gives RGB, and not in a readable call (see above).onAppBecomeActive/onAppResignActive:scenePhaseis per scene, not per app, and does not follow app activation on macOS.confirmDeleteDialog(…):confirmationDialog(_:isPresented:presenting:…)has no localized default title, message and buttons.PrimaryButtonStyle/SecondaryButtonStyle:.borderedProminent/.borderedlook different and have nodisabled/compactoptions.CheckboxUniversalToggleStyle:.checkboxis unavailable on iOS, tvOS, watchOS and visionOS.CachedAsyncImage:AsyncImagehas no cache.SideTabView:.sidebarAdaptable(iOS 18) is a collapsible sidebar / tab bar, not a vertical icon rail.PersistentModel.isPersisted: the newPersistentIdentifier.isTemporary(iOS 27) flips onsave(), whileisPersistedflips oninsert(_:).Notification.Name.publisher/post(object:on:): thin sugar overNotificationCenterAPIs that have existed since before the helper.AsyncButton,AsyncView,HPicker,VPicker,MultiSelector,LimitedTextField,SearchableGridPicker,OpenPanel,Platform,Xcode.isRunningForPreviews,onFirstAppear,throwingTask,progressOverlay,applyIf/ifLet/ platform modifiers, theBindinghelpers,Text(format:partialStyling:),highlightMatchingTokenizedPrefixes, the semanticColorstatics, the label and labeled-content styles,Emoji,SFSymbol.How this was verified
Availability was read from the
.swiftinterfacefiles of the SDKs in Xcode 27.1 (iPhoneOS 27.1, MacOSX 27.0):This branch adds a Swift Testing target (CI runs
swift test --build-system swiftbuild).ColorComponentsTestscompares the newrgbowith the old device RGB implementation for nine plain and dynamic colors and checksrgbo(in:)in light and dark environments. To get the red run, the macOS conversion was set back to device RGB andrgbo(in:)was made to ignore its environment (what callers had before):hsbogot the same treatment in a follow-up commit, with its own red run (device RGB restored):rgboMatchesResolvedComponentsForTheCurrentAppearanceis disabled through a test condition while "Increase contrast" is on, becausergbothen returns the high-contrast variants, which anEnvironmentValues()cannot request.dynamicColorsResolveAgainstTheGivenEnvironmentcarries the availability ofresolve(in:), since the fallback ignores the environment.The three tests that pass in the red run guard what must not change: sRGB components are identical to the old implementation, and
rgboagrees withrgbo(in:)for the current appearance.The package was built on
mainand on this branch, and the deduplicated diagnostics (file + message) were compared.swift build(macOS) andxcodebuildfor generic iOS, tvOS and visionOS were identical on both sides: no new warnings.A throwaway consumer package showed that the
WebViewdeprecation fires only from the intended deployment target:The same consumer, importing WebKit as well, compiled
WebView(url:)andWebView(WebPage())without ambiguity, while a stored property typedWebViewfailed with'WebView' is ambiguous for type lookup in this contextuntil it was written asHandySwiftUI.WebView.