<< All versions

Skill v1.0.2

currentAutomated scan100/100
orange-opensource/ouds-ios/ouds-ios-guide-migration

2 files

──Details
PublishedNovember 1, 2026 at 10:59 AM
Content Hashsha256:abb396d92675447c...
Git SHA489f33740842
Bump Typepatch
Compare with v1.0.1
──Files
Files (1 file, 46.5 KB)
SKILL.md46.5 KBactive
SKILL.md · 907 lines · 46.5 KB

name: ouds-ios-guide-migration description: Use when the user wants to migrate OUDS iOS code from an older version (1.0.0 or later) to the current version (3.2.0 or higher), or when deprecated or removed OUDS APIs are detected in the codebase license: MIT metadata: version: "1.0.2"


Skill: ouds-ios-guide-migration

Migration guide from v1.0.0 to v3.2.0 (current). Covers all breaking changes and deprecated symbols. For full before/after examples, refer to MIGRATION.md in the project root.


Quick-reference: what changed, when

From → ToImpactKey changes
v1.0.0 → v1.1.0HighMultiple*Tokens → Multiple*Token; button border tokens shortened
v1.1.0 → v1.2.0HighTheme renamed: OrangeBusinessTools → OrangeCompact
v1.2.0 → v1.3.0LowoudsVerticalSizeClass removed; UnorderedIcon → UnorderedAsset
v1.3.0 → v1.4.0Lowouds-prefixed View methods removed; OUDSTabBar(selected:) → (selectedTab:)
v1.4.0 → v2.0.0HighToken renames (colors, elevations, sizes, badge, bar); charts provider renamed; toolbar action type enriched
v2.0.0 → v2.1.0LowComponent token spacePaddingBlockDensityCompactTopAlignmentTopText_container renamed
v2.0.0 → v2.2.0MediumOUDSBadge split into OUDSBadgeStandard, OUDSBadgeCount, OUDSBadgeIcon
v2.2.0 → v2.3.0LowOUDSIcon → OUDSImage; all icon: Image + flipIcon + renderingMode params replaced by OUDSImage
v2.3.0 → v3.0.0HighButton/tag/link component token renames (add Default suffix); icon.colorContentDefault removed; theme.controlItem → theme.listItem; OUDSLink.Indicator.back → .previous; OUDSChipPickerData.Layout.icon(icon:…) → .image(image:); .textAndIcon → .textAndImage; alert status .neutral(icon:)/.accent(icon:) → (image:); OUDSBadgeIcon status .neutral(icon:flipped:renderingMode:)/.accent(…) → (image: OUDSImage); forceOUDSLegacyTabBar → forceOUDSLegacyLayout; OUDSLegacyTabBarModifier → OUDSLegacyLayoutModifier; OUDSButton.Style.loading → .loading(); OUDSTag hasLoader: removed → use loadingLabel:/loadingKey: inits, now with optional progress:
v3.0.0 → v3.2.0LowOUDSListItemData — all 6 String/AttributedString initializers deprecated; new unified initializers init(label: Label, …) / init(label: some View, accessibilityLabel:, …) taking OUDSListItemData.Label + OUDSTextualContent (.raw / .localized / .rich) for description / overline / extraLabel / helperText. OUDSTextualContent becomes public @frozen; both it and Label conform to ExpressibleByStringLiteral. Label.text deprecated in favor of new Label.textual(OUDSTextualContent, isBold:). OUDSBulletList.Item — String / AttributedString / LocalizedStringKey initializers deprecated; new init(_: OUDSTextualContent, …). OUDSTextArea — HelperText.plain / .rich deprecated in favor of .textual(OUDSTextualContent); Status.error(message:) / .richError(message:) deprecated in favor of .textualError(message: OUDSTextualContent). OUDSPasswordInput — 4 initializers (helperText String? / AttributedString) deprecated; 2 new initializers taking helperText: OUDSTextualContent?. OUDSTextInput — 4 initializers (helperText String? / AttributedString) deprecated; 2 new initializers taking helperText: OUDSTextualContent?; Status.error(message:) / .richError(message:) deprecated in favor of .textualError(message:) (also used by OUDSPasswordInput). OUDSPinCodeInput — 3 initializers (helperText String? / AttributedString / helperText key: LocalizedStringKey) deprecated; 1 new initializer taking helperText: OUDSTextualContent?; Status.error(message:) / .richError(message:) deprecated in favor of .textualError(message: OUDSTextualContent). OUDSCheckboxItemIndeterminate — 2 initializers (errorText: String?) deprecated; 2 new initializers taking errorText: OUDSTextualContent?. OUDSSwitchItem, OUDSCheckboxItem and OUDSRadioItem — 4 initializers each (errorText String? / AttributedString) deprecated; 2 new initializers each taking errorText: OUDSTextualContent?. OUDSAlertMessage — 8 initializers (String / AttributedString description, [String] / [AttributedString] bulletList) deprecated; 2 new initializers (label: / _ key:) taking description: OUDSTextualContent? and bulletList: [OUDSTextualContent].

Migration workflow

  1. Detect — grep for deprecated symbols (commands below).
  2. Transform — apply the rules for the relevant version.
  3. Build — swift build must succeed with zero errors.
  4. Check warnings — swift build 2>&1 | grep -i deprecated must return nothing OUDS-related.
  5. Test — swift test must pass.

Detection grep commands

bash
# Breaking changes (v3.0.0)
grep -rn \
"theme\.controlItem\|icon\.colorContentDefault\|spaceInsetLoader\|\.sizeMaxHeightIconOnly\b\|\.sizeMinHeight\b\|\.sizeMinWidth\b\|\.sizeIcon\b\|\.sizeIconOnly\b\|\.sizeProgressIndicator\b\|\.spaceColumnGapIconChevron\b\|\.spaceColumnGapChevron\b\|\.spaceInsetIconOnly\b\|\.spacePaddingBlock\b\|\.spacePaddingInlineChevronEnd\b\|\.spacePaddingInlineChevronStart\b\|\.spacePaddingInlineEndIconStart\b\|\.spacePaddingInlineIconNone\b\|\.spacePaddingInlineStartIconEnd\b\|indicator: \.back\b\|forceOUDSLegacyTabBar\|OUDSLegacyTabBarModifier\|hasLoader:" \
--include="*.swift" .
# Active deprecations (v2.x)
grep -rn \
"OUDSBadge\b\|OUDSIcon\b\|icon: Image\|flipIcon\|renderingMode:\|spacePaddingBlockDensityCompactTopAlignmentTopText_container" \
--include="*.swift" .
# Removed symbols (v1.x era)
grep -rn \
"oudsVerticalSizeClass\|UnorderedIcon\|\.free(\|OUDSTabBar(selected:\|\.ouds[A-Z]\|Multiple.*Tokens\b\|OrangeBusinessTools\|buttonBorderRadius\|buttonBorderWidth" \
--include="*.swift" .

v1.0.0 → v1.1.0

OldNew
MultipleElevationCompositeRawTokensMultipleElevationCompositeRawToken
MultipleColorSemanticTokensMultipleColorSemanticToken
MultipleFontCompositeSemanticTokensMultipleFontCompositeSemanticToken
MultipleFontLetterSpacingSemanticTokensMultipleFontLetterSpacingSemanticToken
MultipleFontLineHeightSemanticTokensMultipleFontLineHeightSemanticToken
MultipleFontSizeSemanticTokensMultipleFontSizeSemanticToken
MultipleSizeSemanticTokensMultipleSizeSemanticToken
MultipleSpaceSemanticTokensMultipleSpaceSemanticToken
theme.button.buttonBorderRadiusDefaulttheme.button.borderRadiusDefault
theme.button.buttonBorderRadiusRoundedtheme.button.borderRadiusRounded
theme.button.buttonBorderRadiusSocialtheme.button.borderRadiusSocial
theme.button.buttonBorderWidthDefaulttheme.button.borderWidthDefault
theme.button.buttonBorderWidthDefaultInteractiontheme.button.borderWidthDefaultInteraction
theme.button.buttonBorderWidthDefaultInteractionMonotheme.button.borderWidthDefaultInteractionMono

v1.1.0 → v1.2.0

Replace everywhere: OUDSThemeOrangeBusinessTools → OUDSThemeOrangeCompact, OrangeBusinessToolsTheme → OrangeCompactTheme, OrangeBusinessTools → OrangeCompact.


v1.2.0 → v1.3.0

  • Remove all uses of @Environment(\.oudsVerticalSizeClass).
  • Rename OUDSBulletList.UnorderedIcon → OUDSBulletList.UnorderedAsset; case .free(…) → .icon(…).

v1.3.0 → v1.4.0

RemovedReplacement
.oudsForegroundStyle(_:).foregroundStyle(_:)
.oudsForegroundColor(_:).foregroundColor(_:)
.oudsBackground(_:).background(_:)
.oudsAccentColor(_:).accentColor(_:)
.oudsShadow(_:).shadow(_:)
.oudsBorder(style:width:radius:color:).border(style:width:radius:color:)
.oudsColoredSurface(_:).coloredSurface(_:)
.oudsGridMargin(_:).gridMargin(_:)
.oudsRequestAccessibleFocus(_:).requestAccessibleFocus(_:)
.oudsRequestAccessibleFocus(_:for:).requestAccessibleFocus(_:for:)
.oudsHorizontalDivider(dividerColor:).horizontal(color:)
.oudsVerticalDivider(color:).vertical(color:)
OUDSTabBar(selected: 0, count: 3) { … }@State var selectedTab = 0; OUDSTabBar(selectedTab: $selectedTab, count: 3) { … }

v1.4.0 → v2.0.0

Renamed semantic tokens

OldNew
repositorySecondaryHigherrepositorySecondaryHigherHigh
overlayModalLight/Dark/overlayModalSheetLight/Dark/ (drop Modal, add Sheet)
xDefault, yDefault, blurDefault, spreadDefaultxElevated, yElevated, blurElevated, spreadElevated
colorDefaultLight/DarkcolorElevatedLight/Dark
All maxWidthType* sizesmaxWidth* (remove Type)
Badge spaceInsetspaceInsetMediumLarge
Bar colorActiveIndicator*colorCurrentIndicator*
Bar opacityActiveIndicator*, borderRadiusActiveIndicator*, sizeWidthActiveIndicator*same with Current

Removed tokens

Remove: blur160, opacityGrayLight80800, actionAccentLight/Dark/ (use colorAccent bar tokens), radio/checkbox sizeIndicator (use listItem.sizeControlIndicator), control item tokens: sizeMaxHeightAssetsContainer, sizeLoader, sizeErrorIcon, borderRadiusItemOnly, colorBgHover/Focus/Pressed/Loading*, colorContentLoader*, spacePaddingInlineErrorIcon*.

Other changes

  • theme.charts.* → theme.colorsCharts.*
  • OUDSToolBarItem.ActionType .icon case: add new badgeType slot in pattern matching.

v2.0.0 → v2.1.0

theme.controlItem.spacePaddingBlockDensityCompactTopAlignmentTopText_container → spacePaddingBlockDensityCompactTopAlignmentTopTextContainer


v2.0.0 → v2.2.0 — OUDSBadge split

OUDSBadge is deprecated. Identify the variant and replace:

If the call has…Use
count: parameterOUDSBadgeCount(count, accessibilityLabel:status:size:) — count is first unlabelled arg, type UInt8
status: of type StatusWithIcon (carries an icon)OUDSBadgeIcon(status:accessibilityLabel:size:)
NeitherOUDSBadgeStandard(accessibilityLabel:status:size:)

v2.2.0 → v2.3.0 — OUDSImage migration

Universal rule

All components that used icon: Image, flipIcon: Bool, renderingMode: as separate parameters now use OUDSImage:

icon: Image("x") → OUDSImage(asset: Image("x"))
flipIcon: true → OUDSImage(asset:, flipped: true) [omit if false]
renderingMode: .original → OUDSImage(asset:, renderingMode: .original) [omit if .template]

OUDSIcon → OUDSImage

OUDSIcon is a deprecated typealias. Global find-and-replace: OUDSIcon( → OUDSImage(.

Components index

ComponentOld params removedNew paramNotes
OUDSButtonicon:, flipIcon:, renderingMode:, accessibilityLabel:image: OUDSImageaccessibilityLabel moves into OUDSImage
OUDSCheckboxItemicon: Image?, flipIcon:, renderingMode:image: OUDSImage?Renamed icon: → image: to resolve init ambiguity
OUDSCheckboxItemIndeterminateicon: Image?, flipIcon:, renderingMode:image: OUDSImage?New LocalizedStringKey init added; renamed icon: → image:
OUDSCheckboxPickerDataicon: Image?, renderingMode:image: OUDSImage?Renamed icon: → image: to resolve init ambiguity
OUDSRadioItemicon: Image?, flipIcon:, renderingMode:image: OUDSImage?Renamed icon: → image: to resolve init ambiguity
OUDSRadioPickerDataicon: Image?image: OUDSImage?Renamed icon: → image:; both the deprecated and the active init use a named tag: parameter
OUDSSwitchItemicon: Image?, flipIcon:, renderingMode:image: OUDSImage?isReversed defaults to true; renamed icon: → image:
OUDSFilterChipicon: Image, renderingMode:image: OUDSImageRenamed icon: → image:; no flipIcon; accessibilityLabel stays on chip
OUDSSuggestionChipicon: Image, renderingMode:image: OUDSImageNo flipIcon; icon: → image: for all variants
OUDSChipPickerData.Layoutcases .icon(icon: Image, …) / .textAndIcon(text:icon:renderingMode:) — still availablestatic factories .icon(OUDSImage, accessibilityLabel:) / .textAndIcon(_ text: String, image: OUDSImage)The static factory's first argument has no label (_): drop the text: label when migrating from the enum case
OUDSTag.Statusneutral/accent(icon: Image, flipIcon:, renderingMode:)neutral/accent(image: OUDSImage)Deprecated static factories; parameter label renamed icon: → image:; other status unchanged
OUDSLinkicon: Image?, renderingMode:image: OUDSImage?Renamed icon: → image: to resolve init ambiguity; navigation inits unchanged. Two-step trap: if you first replaced Image(…) with OUDSImage(asset: Image(…)) but kept the icon: label, you end up with icon: OUDSImage(…) which still compiles (no error) but is wrong — you must also rename the label to image:
OUDSTextInputleadingIcon: Image?, flipLeadingIcon:, leadingIconRenderingMode:leadingImage: OUDSImage?Renamed leadingIcon: → leadingImage: to resolve init ambiguity
OUDSTextInput.TrailingActionicon: Image, flipIcon:, renderingMode:image: OUDSImage—

⚠ Residual init ambiguity — workaround

Even after the icon: → image: / leadingIcon: → leadingImage: rename, a call site that omits the image parameter entirely can still trigger a Swift compile-time ambiguity error, because the deprecated initialisers (icon: Image? = nil) and the active initialisers (image: OUDSImage? = nil) both match a call with no image argument at all.

Symptom: error: ambiguous use of 'init(...)' on a component call with no image argument.

Fix: Pass the image parameter explicitly as nil to force resolution toward the active init:

ComponentAdd this to the call site
OUDSCheckboxItemimage: nil
OUDSCheckboxItemIndeterminateimage: nil
OUDSCheckboxPickerDataimage: nil
OUDSRadioItemimage: nil
OUDSRadioPickerDataimage: nil
OUDSSwitchItemimage: nil
OUDSLinkimage: nil
OUDSTextInputleadingImage: nil
swift
// ❌ ambiguous while deprecated inits coexist:
OUDSCheckboxItem("Label", isOn: $isOn)
OUDSRadioItem("Label", isOn: $isOn)
OUDSRadioPickerData(tag: "a", label: "Option A")
OUDSSwitchItem("Label", isOn: $isOn)
OUDSLink(text: "Learn more", size: .default) {}
OUDSTextInput(label: "Email", text: $text)
// ✅ unambiguous — active init selected:
// temporary — remove once deprecated inits are gone in v3
OUDSCheckboxItem("Label", isOn: $isOn, image: nil)
OUDSRadioItem("Label", isOn: $isOn, image: nil)
OUDSRadioPickerData(tag: "a", label: "Option A", image: nil)
OUDSSwitchItem("Label", isOn: $isOn, image: nil)
OUDSLink(text: "Learn more", image: nil, size: .default) {}
// ⚠ leadingImage: comes AFTER placeholder:/prefix:/suffix: in the active init:
OUDSTextInput(label: "Email", text: $text, placeholder: "you@example.com", leadingImage: nil)
⚠ `OUDSTextInput` parameter order: the active init signature is
label:, text:, placeholder:, prefix:, suffix:, leadingImage:, trailingAction:, …
A mechanical migration may insert leadingImage: nil right after text:, placing it before placeholder:.
Always ensure placeholder: (and prefix:/suffix: if present) appears before leadingImage: in the call site.

This workaround is only needed until the deprecated initialisers are removed in v3.


v2.3.0 → v3.0.0

Backward compatibility: None. All changes are breaking.

Button component tokens — add Default suffix

15 size and space tokens for OUDSButton have been renamed. Add the Default suffix to each.

Old (v2.3)New (v3.0)
sizeMaxHeightIconOnlysizeMaxHeightIconOnlyDefault
sizeMinHeightsizeMinHeightDefault
sizeMinWidthsizeMinWidthDefault
sizeIconsizeIconDefault
sizeIconOnlysizeIconOnlyDefault
sizeProgressIndicatorsizeProgressIndicatorDefault
spaceColumnGapIconChevronspaceColumnGapIconChevronDefault
spaceColumnGapChevronspaceColumnGapChevronDefault
spaceInsetIconOnlyspaceInsetIconOnlyDefault
spacePaddingBlockspacePaddingBlockDefault
spacePaddingInlineChevronEndspacePaddingInlineChevronEndDefault
spacePaddingInlineChevronStartspacePaddingInlineChevronStartDefault
spacePaddingInlineEndIconStartspacePaddingInlineEndIconStartDefault
spacePaddingInlineIconNonespacePaddingInlineIconNoneDefault
spacePaddingInlineStartIconEndspacePaddingInlineStartIconEndDefault
swift
// Before (v2.3)
theme.button.sizeMinHeight
theme.button.spacePaddingBlock
// After (v3.0)
theme.button.sizeMinHeightDefault
theme.button.spacePaddingBlockDefault

Required action: for each row, global find-and-replace theme.button.<oldName> → theme.button.<newName>.


Accordion component tokens — ListItem removed

Old (v2.3)New (v3.0)
spacePaddingBlockBottomSlotListItemContainerMobilespacePaddingBlockBottomSlotContainerMobile
spacePaddingBlockBottomSlotListItemContainerTabletspacePaddingBlockBottomSlotContainerTablet
swift
// Before (v2.3)
theme.accordion.spacePaddingBlockBottomSlotListItemContainerMobile
// After (v3.0)
theme.accordion.spacePaddingBlockBottomSlotContainerMobile

Required action: remove ListItem from the token names for accordion component.


Alert Message component token — sizeIcon → sizeAsset

Old (v2.3)New (v3.0)
sizeIconsizeAsset
swift
// Before (v2.3)
theme.alertMessage.sizeIcon
// After (v3.0)
theme.alert.sizeAsset

Required action: replace sizeIcon by sizeAsset for alertMessage component.


OUDSButton style — .loading → .loading()

The OUDSButton.Style enum case for loading now takes an optional progress parameter (nil for indeterminate, double for determinate percentage).

Old (v2.3)New (v3.0)
style: .loadingstyle: .loading() (indeterminate)
—style: .loading(progress: 0.75) (determinate, 75%)
swift
// Before (v2.3)
OUDSButton(text: "Delete", style: .loading, size: .small) { }
// After (v3.0) — indeterminate
OUDSButton(text: "Delete", style: .loading(), size: .small) { }
// After (v3.0) — determinate with 75% progress
OUDSButton(text: "Delete", style: .loading(progress: 0.75), size: .small) { }

Required action: replace style: .loading with style: .loading() (or .loading(progress: value) for determinate).


Tag component tokens — Loader → ProgressIndicator

Old (v2.3)New (v3.0)
spaceInsetLoaderSmallspaceInsetProgressIndicatorSmall
spaceInsetLoaderDefaultspaceInsetProgressIndicatorDefault
swift
// Before (v2.3)
theme.tag.spaceInsetLoaderSmall
// After (v3.0)
theme.tag.spaceInsetProgressIndicatorSmall

Required action: replace Loader with ProgressIndicator in all theme.tag token names.


OUDSTag — hasLoader removed, progress added

The hasLoader: Bool parameter has been removed from OUDSTag(label:...) and OUDSTag(_ key:...). Loading tags must now be built exclusively with the dedicated loadingLabel: / loadingKey: initializers, which gained a new optional progress: Double? parameter (nil for indeterminate, double for determinate percentage).

Old (v2.3)New (v3.0)
OUDSTag(label: "...", hasLoader: true)OUDSTag(loadingLabel: "...") (indeterminate)
—OUDSTag(loadingLabel: "...", progress: 0.75) (determinate, 75%)
OUDSTag(label: "...", hasLoader: false)OUDSTag(label: "...") (just drop the parameter)
swift
// Before (v2.3)
OUDSTag(label: "Processing...", hasLoader: true)
// After (v3.0) — indeterminate
OUDSTag(loadingLabel: "Processing...")
// After (v3.0) — determinate with 75% progress
OUDSTag(loadingLabel: "Processing...", progress: 0.75)

Required action: replace any OUDSTag(label:hasLoader: true, ...) / OUDSTag(_ key:hasLoader: true, ...) with OUDSTag(loadingLabel:...) / OUDSTag(loadingKey:...), optionally passing progress:. If hasLoader: false was passed, just remove the parameter.


Link component tokens — mixed rename

Note the asymmetry: one token gains Default, the other loses it.
Old (v2.3)New (v3.0)
spacePaddingBlockspacePaddingBlockDefault
sizeMinWidthDefaultsizeMinWidth
swift
// Before (v2.3)
theme.link.spacePaddingBlock
theme.link.sizeMinWidthDefault
// After (v3.0)
theme.link.spacePaddingBlockDefault
theme.link.sizeMinWidth

Required action: apply both renames independently; a bulk "add Default" pass will break sizeMinWidth.


Removed: icon.colorContentDefault

The token theme.icon.colorContentDefault has been fully removed (was deprecated in v2.x).

swift
// Before (v2.3)
theme.icon.colorContentDefault
// After (v3.0) — verify semantic equivalence before replacing
theme.colors.contentDefault

Required action: replace any use of theme.icon.colorContentDefault with theme.colors.contentDefault.


Provider renamed: controlItem → listItem

The controlItem tokens provider no longer exists in Figma and has been removed from the Swift API. This affects both the theme accessor and all public Swift types (classes, protocols).

swift
// Before (v2.3)
theme.controlItem.someToken
// After (v3.0)
theme.listItem.someToken

Required action: global find-and-replace theme.controlItem → theme.listItem across the codebase.

All Swift types have been renamed accordingly:

Old type (v2.3)New type (v3.0)
OrangeThemeControlItemComponentTokensProviderOrangeThemeListItemComponentTokensProvider
OrangeCompactThemeControlItemComponentTokensProviderOrangeCompactThemeListItemComponentTokensProvider
SoshThemeControlItemComponentTokensProviderSoshThemeListItemComponentTokensProvider
WireframeThemeControlItemComponentTokensProviderWireframeThemeListItemComponentTokensProvider
AllControlItemComponentTokensProviderAllListItemComponentTokensProvider
ControlItemComponentTokensListItemComponentTokens
swift
// Before (v2.3)
class MyProvider: OrangeThemeControlItemComponentTokensProvider { … }
let provider: AllControlItemComponentTokensProvider = MyProvider()
// After (v3.0)
class MyProvider: OrangeThemeListItemComponentTokensProvider { … }
let provider: AllListItemComponentTokensProvider = MyProvider()

Required action: rename all subclasses and type annotations referencing the old types above.


OUDSChipPickerData.Layout — .icon and .textAndIcon cases replaced

Both layout cases of OUDSChipPickerData.Layout that accepted bare Image parameters have been removed and replaced by cases that accept OUDSImage.

Old (v2.3)New (v3.0)
.icon(icon:accessibilityLabel:renderingMode:).image(image:)
.textAndIcon(text:icon:renderingMode:).textAndImage(text:image:)
swift
// Before (v2.3)
.icon(icon: Image("someImage"), accessibilityLabel: "Foo", renderingMode: .original)
.textAndIcon(text: "Foo", icon: Image("someImage"), renderingMode: .original)
// After (v3.0)
.image(image: OUDSImage(asset: Image("someImage"), accessibilityLabel: "Foo", renderingMode: .original))
.textAndImage(text: "Foo", image: OUDSImage(asset: Image("someImage"), renderingMode: .original))

Required action:

  • Replace .icon(icon:accessibilityLabel:renderingMode:) with .image(image:), wrapping the asset, accessibility label and rendering mode into a single OUDSImage.
  • Replace .textAndIcon(text:icon:renderingMode:) with .textAndImage(text:image:), wrapping the asset and rendering mode into a single OUDSImage.

OUDSBadgeIcon status — neutral and accent now accept OUDSImage

The .neutral(icon:flipped:renderingMode:) and .accent(icon:flipped:renderingMode:) cases of OUDSBadgeIcon status have been replaced: the three separate parameters are now grouped into a single OUDSImage passed as image:.

Old (v2.3)New (v3.0)
.neutral(icon:flipped:renderingMode:).neutral(image: OUDSImage)
.accent(icon:flipped:renderingMode:).accent(image: OUDSImage)
swift
// Before (v2.3)
.neutral(icon: Image("someImage"), flipped: false, renderingMode: .original)
.accent(icon: Image("someImage"), flipped: false, renderingMode: .original)
// After (v3.0)
.neutral(image: OUDSImage(asset: Image("someImage"), flipped: false, renderingMode: .original))
.accent(image: OUDSImage(asset: Image("someImage"), flipped: false, renderingMode: .original))

Required action:

  • Replace .neutral(icon:flipped:renderingMode:) with .neutral(image:), wrapping asset, flip flag and rendering mode into a single OUDSImage.
  • Replace .accent(icon:flipped:renderingMode:) with .accent(image:), same wrapping.

Alert status — icon: parameter renamed to image: and made optional

The .neutral(icon:) and .accent(icon:) cases of alert status have two changes:

  • The parameter label is renamed from icon: to image:.
  • The type changes from OUDSImage (required) to OUDSImage? with a default value of nil, making the parameter optional.
Old (v2.3)New (v3.0)
.neutral(icon: someOudsImage).neutral(image: someOudsImage)
.accent(icon: someOudsImage).accent(image: someOudsImage)
—.neutral() / .accent() (no image, new in v3.0)
swift
// Before (v2.3)
.neutral(icon: someOudsImage)
.accent(icon: someOudsImage)
// After (v3.0)
.neutral(image: someOudsImage)
.accent(image: someOudsImage)
// Also valid in v3.0 — no image (uses nil default)
.neutral()
.accent()

Required action:

  • Replace .neutral(icon:) with .neutral(image:).
  • Replace .accent(icon:) with .accent(image:).

Modifier renamed: forceOUDSLegacyTabBar → forceOUDSLegacyLayout

The .forceOUDSLegacyTabBar() view modifier and its underlying OUDSLegacyTabBarModifier type have been renamed to reflect a broader layout scope.

Old (v2.3)New (v3.0)
.forceOUDSLegacyTabBar().forceOUDSLegacyLayout()
OUDSLegacyTabBarModifierOUDSLegacyLayoutModifier
swift
// Before (v2.3)
SomeView().forceOUDSLegacyTabBar()
let modifier: OUDSLegacyTabBarModifier = ...
// After (v3.0)
SomeView().forceOUDSLegacyLayout()
let modifier: OUDSLegacyLayoutModifier = ...

Required action: global find-and-replace forceOUDSLegacyTabBar → forceOUDSLegacyLayout and OUDSLegacyTabBarModifier → OUDSLegacyLayoutModifier.


OUDSLink indicator — .back → .previous

The OUDSLink.Indicator enum case for backward navigation has been renamed.

Old (v2.3)New (v3.0)
.back.previous
swift
// Before (v2.3)
OUDSLink(text: "Back", indicator: .back, size: .default) { }
// After (v3.0)
OUDSLink(text: "Back", indicator: .previous, size: .default) { }

Required action: replace all indicator: .back with indicator: .previous in OUDSLink calls.


v3.0.0 → v3.2.0

Backward compatibility: Fully source-compatible. All v3.0.0 call sites keep compiling; they produce deprecation warnings but still work. No API has been removed.

OUDSListItemData — unified initializers with OUDSTextualContent and .textual label

The six initializers of OUDSListItemData (one per combination of String / some View / LocalizedStringKey label × raw / AttributedString overline) are deprecated and replaced by two unified initializers:

  • init(label: Label, description:, overline:, extraLabel:, helperText:, textSlot:, bottomSlot:)
  • init(label: some View, accessibilityLabel:, description:, overline:, extraLabel:, helperText:, textSlot:, bottomSlot:)

They accept:

  • a OUDSListItemData.Label for the label, with cases .textual(OUDSTextualContent, isBold:), .custom(AnyView, accessibilityLabel:) and the deprecated .text(String, isBold:);
  • a OUDSTextualContent? (public, @frozen enum with .raw(String), .localized(LocalizedStringKey, tableName:, bundle:) and .rich(AttributedString)) for each of description, overline, extraLabel, helperText.

Both OUDSTextualContent and Label conform to ExpressibleByStringLiteral: label: "Foo" is .textual(.raw("Foo"), isBold: false) and description: "Bar" is .raw("Bar"). Never use `.text(…)` on a `Label` in new code. In v4, .textual will be renamed .text.

Old (v3.0 — deprecated)New (v3.2)
OUDSListItemData(label: "Foo")OUDSListItemData(label: .textual("Foo"))
OUDSListItemData(label: "Foo", hasBoldLabel: true)OUDSListItemData(label: .textual("Foo", isBold: true))
OUDSListItemData(label: "Foo", description: "Bar", overline: "Over")unchanged
OUDSListItemData(label: "Foo", overline: myAttributedString)OUDSListItemData(label: "Foo", overline: .rich(myAttributedString))
OUDSListItemData(label: myView, accessibilityLabel: "Foo", overline: "Over")OUDSListItemData(label: myView, accessibilityLabel: "Foo", overline: .raw("Over"))
OUDSListItemData(key: "foo.label")OUDSListItemData(label: .textual(.localized("foo.label")))
Label.text("Foo", isBold: true)Label.textual("Foo", isBold: true)

New capability (v3.2): every textual field can now be a styled AttributedString or a localized string, not just overline:

swift
// Before (v3.0) — only `overline` could be styled
OUDSListItemData(label: "Foo", overline: myStyledOverline)
// After (v3.2) — any textual field can be styled or localized
OUDSListItemData(
label: .textual(.rich(myStyledLabel)),
description: .rich(myStyledDescription),
overline: .rich(myStyledOverline),
extraLabel: .localized("extra.label.key"),
helperText: "Plain string literal")

Concrete example with colors:

swift
import OUDSSwiftUI
import SwiftUI
// Colored label (red)
let redLabel = AttributedString.from(
text: "Payment problems",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryNegativeHigh),
font: .body
)
// Colored overline (green)
let greenOverline = AttributedString.from(
text: "Services activated",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryOpacityPositiveMedium),
font: .body
)
// Colored extraLabel (orange)
let orangeExtra = AttributedString.from(
text: "offers available",
foregroundColor: .orange,
font: .body
)
// Full example
OUDSStaticListItem(
data: OUDSListItemData(
label: .textual(.rich(redLabel)),
description: .rich(AttributedString.from(
text: "Action required",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryOpacityWarningMedium),
font: .body
)),
overline: .rich(greenOverline),
extraLabel: .rich(orangeExtra)
),
trailing: .badge(.count(OUDSBadgeCount(3, accessibilityLabel: "3", status: .negative, size: .medium)))
)

Required action:

  • Replace OUDSListItemData(label: "Foo") (label alone, or with only textSlot / bottomSlot) with OUDSListItemData(label: .textual("Foo")).
  • Replace hasBoldLabel: true with .textual("Foo", isBold: true).
  • Replace key: LocalizedStringKey with .textual(.localized(key, tableName:, bundle:)).
  • Replace overline: myAttributedString with overline: .rich(myAttributedString) and move the overline: parameter to its canonical position (after description: in the new init order).
  • Replace any Label.text(…) with .textual(…).
Literal pitfall: the new initializers reuse the label: name of the deprecated ones, and Label / AttributedString are expressible by a string literal. Two forms may select a deprecated initializer (warning):
- a bare literal label without any textual field, e.g. OUDSListItemData(label: "Foo"), or with only textSlot / bottomSlot: write label: .textual("Foo");
- a custom view label with a literal overline: write overline: .raw("Over").
With a literal label next to a literal description, overline, extraLabel or helperText, the new initializer is selected without warning.

OUDSBulletList.Item — unified initializer with OUDSTextualContent

The three initializers of OUDSBulletList.Item (String, AttributedString, LocalizedStringKey + tableName: / bundle:) are deprecated and replaced by a single one:

  • init(_ text: OUDSTextualContent, subListType:, subListTextStyle:, subListHasBoldText:, subItems:)

A string literal is converted to .raw(…) without any warning: OUDSBulletList.Item("Foo") needs no change. The deprecated initializers are marked @_disfavoredOverload, so only calls passing a String or AttributedString variable (or a LocalizedStringKey) warn.

Old (deprecated)New (v3.2)
OUDSBulletList.Item("Foo") (literal)unchanged
OUDSBulletList.Item(someString)OUDSBulletList.Item(.raw(someString))
OUDSBulletList.Item(myAttributedString)OUDSBulletList.Item(.rich(myAttributedString))
OUDSBulletList.Item(LocalizedStringKey("key"), tableName: t, bundle: b)OUDSBulletList.Item(.localized("key", tableName: t, bundle: b))

Required action:

  • Replace Item(myAttributedString) with Item(.rich(myAttributedString)).
  • Replace Item(someStringVariable) with Item(.raw(someStringVariable)).
  • Replace Item(LocalizedStringKey(…), tableName:, bundle:) with Item(.localized(…, tableName:, bundle:)).

OUDSAlertMessage — unified initializers with OUDSTextualContent

The eight initializers of OUDSAlertMessage (label String / LocalizedStringKey × description String / AttributedString × bulletList [String] / [AttributedString]) are deprecated and replaced by two:

  • init(label: String, status:, description: OUDSTextualContent?, bulletList: [OUDSTextualContent], link:, onClose:)
  • init(_ key: LocalizedStringKey, tableName:, bundle:, status:, description: OUDSTextualContent?, bulletList: [OUDSTextualContent], link:, onClose:)

String literals are converted to .raw(…) without warning (description: "Foo", bulletList: ["A", "B"] need no change). The deprecated initializers are @_disfavoredOverload: only calls passing String / AttributedString variables warn.

Old (deprecated)New (v3.2)
description: "Foo", bulletList: ["A", "B"] (literals)unchanged
description: someStringdescription: .raw(someString)
description: myAttributedStringdescription: .rich(myAttributedString)
bulletList: someStringsbulletList: someStrings.map { .raw($0) }
bulletList: myAttributedStringsbulletList: myAttributedStrings.map { .rich($0) }

Required action:

  • Replace description: myAttributedString with description: .rich(myAttributedString).
  • Replace description: someStringVariable with description: .raw(someStringVariable).
  • Replace bulletList variables of [String] / [AttributedString] with arrays of .raw(…) / .rich(…); an array literal can mix raw literals, .localized(…) and .rich(…).
Literal pitfall: with the LocalizedStringKey initializer, a bare description: "abc" may select a deprecated initializer (warning), because AttributedString is expressible by a string literal. Use description: .raw("abc").

OUDSSwitchItem — unified initializers with OUDSTextualContent

The four initializers of OUDSSwitchItem (label String / LocalizedStringKey × errorText String? / AttributedString) are deprecated and replaced by two taking errorText: OUDSTextualContent? = nil.

Old (deprecated)New (v3.2)
errorText: someString or errorText: "Message"errorText: .raw(…)
errorText: myAttributedStringerrorText: .rich(myAttributedString)
errorText: nil / omittedunchanged
Literal pitfall: a bare errorText: "abc" may select a deprecated initializer (warning), because AttributedString is expressible by a string literal. Always write errorText: .raw("abc").

Required action:

  • Replace errorText: myAttributedString with errorText: .rich(myAttributedString).
  • Replace errorText: someString / errorText: "Message" with errorText: .raw(…).

OUDSCheckboxItem — unified initializers with OUDSTextualContent

The four initializers of OUDSCheckboxItem (label String / LocalizedStringKey × errorText String? / AttributedString) are deprecated and replaced by two taking errorText: OUDSTextualContent? = nil.

Old (deprecated)New (v3.2)
errorText: someString or errorText: "Message"errorText: .raw(…)
errorText: myAttributedStringerrorText: .rich(myAttributedString)
errorText: nil / omittedunchanged
Literal pitfall: a bare errorText: "abc" may select a deprecated initializer (warning), because AttributedString is expressible by a string literal. Always write errorText: .raw("abc").

Required action:

  • Replace errorText: myAttributedString with errorText: .rich(myAttributedString).
  • Replace errorText: someString / errorText: "Message" with errorText: .raw(…).

OUDSRadioItem — unified initializers with OUDSTextualContent

The four initializers of OUDSRadioItem (label String / LocalizedStringKey × errorText String? / AttributedString) are deprecated and replaced by two taking errorText: OUDSTextualContent? = nil.

Old (deprecated)New (v3.2)
errorText: someString or errorText: "Message"errorText: .raw(…)
errorText: myAttributedStringerrorText: .rich(myAttributedString)
errorText: nil / omittedunchanged
Literal pitfall: a bare errorText: "abc" may select a deprecated initializer (warning), because AttributedString is expressible by a string literal. Always write errorText: .raw("abc").

Required action:

  • Replace errorText: myAttributedString with errorText: .rich(myAttributedString).
  • Replace errorText: someString / errorText: "Message" with errorText: .raw(…).

OUDSCheckboxItemIndeterminate — errorText as OUDSTextualContent

The two initializers of OUDSCheckboxItemIndeterminate (label String / LocalizedStringKey) now take errorText: OUDSTextualContent? = nil instead of errorText: String? = nil; the former ones are deprecated. The error can now also be localized or rich.

Old (deprecated)New (v3.2)
errorText: someStringerrorText: .raw(someString)
errorText: "Message" (literal), nil / omittedunchanged (no warning, there is no AttributedString initializer to compete)

Required action:

  • Replace errorText: someStringVariable with errorText: .raw(someStringVariable).

OUDSPinCodeInput — unified initializer and textualError status

The three initializers of OUDSPinCodeInput (helperText String? / AttributedString / helperText key: LocalizedStringKey + tableName: + bundle:) are deprecated and replaced by one taking helperText: OUDSTextualContent? = nil. Status.error(message: String) and Status.richError(message: AttributedString) are deprecated and replaced by Status.textualError(message: OUDSTextualContent) (renamed .error in v4).

Old (deprecated)New (v3.2)
helperText: someString or helperText: "Message"helperText: .raw(…)
helperText: myAttributedStringhelperText: .rich(myAttributedString)
helperText: LocalizedStringKey("key"), tableName: t, bundle: bhelperText: .localized("key", tableName: t, bundle: b)
status: .error(message: "Message")status: .textualError(message: "Message")
status: .richError(message: myAttributedString)status: .textualError(message: .rich(myAttributedString))
Literal pitfall: a bare helperText: "abc" may select a deprecated initializer (warning), because AttributedString and LocalizedStringKey are expressible by a string literal. Always write helperText: .raw("abc").

Required action:

  • Replace helperText: myAttributedString with helperText: .rich(myAttributedString).
  • Replace helperText: someString / helperText: "Message" with helperText: .raw(…).
  • Replace helperText: LocalizedStringKey(…), tableName:, bundle: with helperText: .localized(…, tableName:, bundle:).
  • Replace .error(message:) / .richError(message:) with .textualError(message:).

OUDSTextInput — unified initializers and textualError status

The four initializers of OUDSTextInput (label String / LocalizedStringKey × helperText String? / AttributedString) are deprecated and replaced by two taking helperText: OUDSTextualContent? = nil. OUDSTextInput.Status.error(message: String) and .richError(message: AttributedString) are deprecated and replaced by .textualError(message: OUDSTextualContent) (renamed .error in v4). This Status is also used by OUDSPasswordInput.

Old (deprecated)New (v3.2)
helperText: someString or helperText: "Message"helperText: .raw(…)
helperText: myAttributedStringhelperText: .rich(myAttributedString)
status: .error(message: "Message")status: .textualError(message: "Message")
status: .richError(message: myAttributedString)status: .textualError(message: .rich(myAttributedString))
Literal pitfall: a bare helperText: "abc" may select a deprecated initializer (warning), because AttributedString is expressible by a string literal. Always write helperText: .raw("abc").

Required action:

  • Replace helperText: myAttributedString with helperText: .rich(myAttributedString).
  • Replace helperText: someString / helperText: "Message" with helperText: .raw(…).
  • Replace .error(message:) / .richError(message:) with .textualError(message:).

OUDSPasswordInput — unified initializers with OUDSTextualContent

The four initializers of OUDSPasswordInput (label String / LocalizedStringKey × helperText String? / AttributedString) are deprecated and replaced by two taking helperText: OUDSTextualContent? = nil. Its status is OUDSTextInput.Status: use .textualError(message:) (see the OUDSTextInput section).

Old (deprecated)New (v3.2)
helperText: someString or helperText: "Message"helperText: .raw(…)
helperText: myAttributedStringhelperText: .rich(myAttributedString)
Literal pitfall: a bare helperText: "abc" may select a deprecated initializer (warning), because AttributedString is expressible by a string literal. Always write helperText: .raw("abc").

Required action:

  • Replace helperText: myAttributedString with helperText: .rich(myAttributedString).
  • Replace helperText: someString / helperText: "Message" with helperText: .raw(…).

OUDSTextArea — textual helper text and textualError status

OUDSTextArea has no initializer to deprecate, but its enums changed:

  • HelperText.plain(String) and .rich(AttributedString) are deprecated and replaced by HelperText.textual(OUDSTextualContent) (.charactersMaxCount(_:) is unchanged).
  • Status.error(message: String) and .richError(message: AttributedString) are deprecated and replaced by Status.textualError(message: OUDSTextualContent) (renamed .error in v4).
Old (deprecated)New (v3.2)
helperText: .plain("Message")helperText: .textual("Message")
helperText: .plain(someString)helperText: .textual(.raw(someString))
helperText: .rich(myAttributedString)helperText: .textual(.rich(myAttributedString))
status: .error(message: "Message")status: .textualError(message: "Message")
status: .richError(message: myAttributedString)status: .textualError(message: .rich(myAttributedString))

Required action:

  • Replace .plain(…) with .textual(…) (a literal is enough, a String variable needs .raw(…)).
  • Replace .rich(myAttributedString) with .textual(.rich(myAttributedString)).
  • Replace .error(message:) / .richError(message:) with .textualError(message:).

Verification checklist

bash
swift build
swift build 2>&1 | grep -i "deprecated" | grep -iv "apple\|system\|swift\|foundation"
# v3.0 breaking changes — must return nothing
grep -rn \
"theme\.controlItem\|icon\.colorContentDefault\|spaceInsetLoader\|\.sizeMaxHeightIconOnly\b\|\.sizeMinHeight\b\|\.sizeMinWidth\b\|\.sizeIcon\b\|\.sizeIconOnly\b\|\.sizeProgressIndicator\b\|\.spaceColumnGapIconChevron\b\|\.spaceColumnGapChevron\b\|\.spaceInsetIconOnly\b\|\.spacePaddingBlock\b\|\.spacePaddingInlineChevronEnd\b\|\.spacePaddingInlineChevronStart\b\|\.spacePaddingInlineEndIconStart\b\|\.spacePaddingInlineIconNone\b\|\.spacePaddingInlineStartIconEnd\b\|Layout\.icon(icon:\|\.textAndIcon\b\|indicator: \.back\b\|\.neutral(icon:\|\.accent(icon:\|neutral(icon:\|accent(icon:\|OrangeThemeControlItemComponentTokensProvider\|OrangeCompactThemeControlItemComponentTokensProvider\|SoshThemeControlItemComponentTokensProvider\|WireframeThemeControlItemComponentTokensProvider\|AllControlItemComponentTokensProvider\|ControlItemComponentTokens\|forceOUDSLegacyTabBar\|OUDSLegacyTabBarModifier\|hasLoader:" \
--include="*.swift" .
# v2.x deprecated symbols — must return nothing
grep -rn \
"OUDSBadge\b\|OUDSIcon\b\|icon: Image\|leadingIcon: Image\|flipIcon\|renderingMode:\|spacePaddingBlockDensityCompactTopAlignmentTopText_container\|buttonBorderRadius\|buttonBorderWidth\|Multiple.*Tokens\b\|OrangeBusinessTools\|oudsVerticalSizeClass\|UnorderedIcon\|\.ouds[A-Z]" \
--include="*.swift" .
# v2.3.0 init label disambiguation — must return nothing
grep -rn \
"icon: OUDSImage\|leadingIcon: OUDSImage" \
--include="*.swift" .
# v3.2.0 deprecated OUDSListItemData String/AttributedString initializers and Label.text.
# A warning-free call site uses `label: .textual(…)` and OUDSTextualContent for other fields.
grep -rnE \
'OUDSListItemData\(\s*label:|OUDSListItemData\([^)]*hasBoldLabel:|OUDSListItemData\([^)]*key:|label:\s*\.text\(' \
--include="*.swift" .
# v3.2.0 deprecated initializers taking String / AttributedString (OUDSBulletList.Item, OUDSAlertMessage, OUDSSwitchItem…).
# No reliable grep exists, string literals remain valid. Rely on the build warnings, they all mention `OUDSTextualContent`.
# Fix a variable with `.raw(var)` / `.rich(var)`, and a bare literal flagged as deprecated with `.raw("…")`.
swift build 2>&1 | grep -i "deprecated" | grep -i "OUDSTextualContent"
swift test
← v1.0.1All versions