Skill v1.0.2
currentAutomated scan100/1002 files
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 → To | Impact | Key changes | |
|---|---|---|---|
| v1.0.0 → v1.1.0 | High | Multiple*Tokens → Multiple*Token; button border tokens shortened | |
| v1.1.0 → v1.2.0 | High | Theme renamed: OrangeBusinessTools → OrangeCompact | |
| v1.2.0 → v1.3.0 | Low | oudsVerticalSizeClass removed; UnorderedIcon → UnorderedAsset | |
| v1.3.0 → v1.4.0 | Low | ouds-prefixed View methods removed; OUDSTabBar(selected:) → (selectedTab:) | |
| v1.4.0 → v2.0.0 | High | Token renames (colors, elevations, sizes, badge, bar); charts provider renamed; toolbar action type enriched | |
| v2.0.0 → v2.1.0 | Low | Component token spacePaddingBlockDensityCompactTopAlignmentTopText_container renamed | |
| v2.0.0 → v2.2.0 | Medium | OUDSBadge split into OUDSBadgeStandard, OUDSBadgeCount, OUDSBadgeIcon | |
| v2.2.0 → v2.3.0 | Low | OUDSIcon → OUDSImage; all icon: Image + flipIcon + renderingMode params replaced by OUDSImage | |
| v2.3.0 → v3.0.0 | High | Button/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.0 | Low | OUDSListItemData — 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
- Detect — grep for deprecated symbols (commands below).
- Transform — apply the rules for the relevant version.
- Build —
swift buildmust succeed with zero errors. - Check warnings —
swift build 2>&1 | grep -i deprecatedmust return nothing OUDS-related. - Test —
swift testmust pass.
Detection grep commands
# 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
| Old | New | |
|---|---|---|
MultipleElevationCompositeRawTokens | MultipleElevationCompositeRawToken | |
MultipleColorSemanticTokens | MultipleColorSemanticToken | |
MultipleFontCompositeSemanticTokens | MultipleFontCompositeSemanticToken | |
MultipleFontLetterSpacingSemanticTokens | MultipleFontLetterSpacingSemanticToken | |
MultipleFontLineHeightSemanticTokens | MultipleFontLineHeightSemanticToken | |
MultipleFontSizeSemanticTokens | MultipleFontSizeSemanticToken | |
MultipleSizeSemanticTokens | MultipleSizeSemanticToken | |
MultipleSpaceSemanticTokens | MultipleSpaceSemanticToken | |
theme.button.buttonBorderRadiusDefault | theme.button.borderRadiusDefault | |
theme.button.buttonBorderRadiusRounded | theme.button.borderRadiusRounded | |
theme.button.buttonBorderRadiusSocial | theme.button.borderRadiusSocial | |
theme.button.buttonBorderWidthDefault | theme.button.borderWidthDefault | |
theme.button.buttonBorderWidthDefaultInteraction | theme.button.borderWidthDefaultInteraction | |
theme.button.buttonBorderWidthDefaultInteractionMono | theme.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
| Removed | Replacement | |
|---|---|---|
.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
| Old | New | |
|---|---|---|
repositorySecondaryHigher | repositorySecondaryHigherHigh | |
overlayModalLight/Dark/ | overlayModalSheetLight/Dark/ (drop Modal, add Sheet) | |
xDefault, yDefault, blurDefault, spreadDefault | xElevated, yElevated, blurElevated, spreadElevated | |
colorDefaultLight/Dark | colorElevatedLight/Dark | |
All maxWidthType* sizes | maxWidth* (remove Type) | |
Badge spaceInset | spaceInsetMediumLarge | |
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.iconcase: add newbadgeTypeslot 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: parameter | OUDSBadgeCount(count, accessibilityLabel:status:size:) — count is first unlabelled arg, type UInt8 | |
status: of type StatusWithIcon (carries an icon) | OUDSBadgeIcon(status:accessibilityLabel:size:) | |
| Neither | OUDSBadgeStandard(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
| Component | Old params removed | New param | Notes | |
|---|---|---|---|---|
OUDSButton | icon:, flipIcon:, renderingMode:, accessibilityLabel: | image: OUDSImage | accessibilityLabel moves into OUDSImage | |
OUDSCheckboxItem | icon: Image?, flipIcon:, renderingMode: | image: OUDSImage? | Renamed icon: → image: to resolve init ambiguity | |
OUDSCheckboxItemIndeterminate | icon: Image?, flipIcon:, renderingMode: | image: OUDSImage? | New LocalizedStringKey init added; renamed icon: → image: | |
OUDSCheckboxPickerData | icon: Image?, renderingMode: | image: OUDSImage? | Renamed icon: → image: to resolve init ambiguity | |
OUDSRadioItem | icon: Image?, flipIcon:, renderingMode: | image: OUDSImage? | Renamed icon: → image: to resolve init ambiguity | |
OUDSRadioPickerData | icon: Image? | image: OUDSImage? | Renamed icon: → image:; both the deprecated and the active init use a named tag: parameter | |
OUDSSwitchItem | icon: Image?, flipIcon:, renderingMode: | image: OUDSImage? | isReversed defaults to true; renamed icon: → image: | |
OUDSFilterChip | icon: Image, renderingMode: | image: OUDSImage | Renamed icon: → image:; no flipIcon; accessibilityLabel stays on chip | |
OUDSSuggestionChip | icon: Image, renderingMode: | image: OUDSImage | No flipIcon; icon: → image: for all variants | |
OUDSChipPickerData.Layout | cases .icon(icon: Image, …) / .textAndIcon(text:icon:renderingMode:) — still available | static 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.Status | neutral/accent(icon: Image, flipIcon:, renderingMode:) | neutral/accent(image: OUDSImage) | Deprecated static factories; parameter label renamed icon: → image:; other status unchanged | |
OUDSLink | icon: 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: | |
OUDSTextInput | leadingIcon: Image?, flipLeadingIcon:, leadingIconRenderingMode: | leadingImage: OUDSImage? | Renamed leadingIcon: → leadingImage: to resolve init ambiguity | |
OUDSTextInput.TrailingAction | icon: 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:
| Component | Add this to the call site | |
|---|---|---|
OUDSCheckboxItem | image: nil | |
OUDSCheckboxItemIndeterminate | image: nil | |
OUDSCheckboxPickerData | image: nil | |
OUDSRadioItem | image: nil | |
OUDSRadioPickerData | image: nil | |
OUDSSwitchItem | image: nil | |
OUDSLink | image: nil | |
OUDSTextInput | leadingImage: nil |
// ❌ 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 v3OUDSCheckboxItem("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 islabel:, text:, placeholder:, prefix:, suffix:, leadingImage:, trailingAction:, …A mechanical migration may insertleadingImage: nilright aftertext:, placing it beforeplaceholder:.Always ensureplaceholder:(andprefix:/suffix:if present) appears beforeleadingImage: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) | |
|---|---|---|
sizeMaxHeightIconOnly | sizeMaxHeightIconOnlyDefault | |
sizeMinHeight | sizeMinHeightDefault | |
sizeMinWidth | sizeMinWidthDefault | |
sizeIcon | sizeIconDefault | |
sizeIconOnly | sizeIconOnlyDefault | |
sizeProgressIndicator | sizeProgressIndicatorDefault | |
spaceColumnGapIconChevron | spaceColumnGapIconChevronDefault | |
spaceColumnGapChevron | spaceColumnGapChevronDefault | |
spaceInsetIconOnly | spaceInsetIconOnlyDefault | |
spacePaddingBlock | spacePaddingBlockDefault | |
spacePaddingInlineChevronEnd | spacePaddingInlineChevronEndDefault | |
spacePaddingInlineChevronStart | spacePaddingInlineChevronStartDefault | |
spacePaddingInlineEndIconStart | spacePaddingInlineEndIconStartDefault | |
spacePaddingInlineIconNone | spacePaddingInlineIconNoneDefault | |
spacePaddingInlineStartIconEnd | spacePaddingInlineStartIconEndDefault |
// Before (v2.3)theme.button.sizeMinHeighttheme.button.spacePaddingBlock// After (v3.0)theme.button.sizeMinHeightDefaulttheme.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) | |
|---|---|---|
spacePaddingBlockBottomSlotListItemContainerMobile | spacePaddingBlockBottomSlotContainerMobile | |
spacePaddingBlockBottomSlotListItemContainerTablet | spacePaddingBlockBottomSlotContainerTablet |
// 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) | |
|---|---|---|
sizeIcon | sizeAsset |
// 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: .loading | style: .loading() (indeterminate) | |
| — | style: .loading(progress: 0.75) (determinate, 75%) |
// Before (v2.3)OUDSButton(text: "Delete", style: .loading, size: .small) { }// After (v3.0) — indeterminateOUDSButton(text: "Delete", style: .loading(), size: .small) { }// After (v3.0) — determinate with 75% progressOUDSButton(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) | |
|---|---|---|
spaceInsetLoaderSmall | spaceInsetProgressIndicatorSmall | |
spaceInsetLoaderDefault | spaceInsetProgressIndicatorDefault |
// 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) |
// Before (v2.3)OUDSTag(label: "Processing...", hasLoader: true)// After (v3.0) — indeterminateOUDSTag(loadingLabel: "Processing...")// After (v3.0) — determinate with 75% progressOUDSTag(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 gainsDefault, the other loses it.
| Old (v2.3) | New (v3.0) | |
|---|---|---|
spacePaddingBlock | spacePaddingBlockDefault | |
sizeMinWidthDefault | sizeMinWidth |
// Before (v2.3)theme.link.spacePaddingBlocktheme.link.sizeMinWidthDefault// After (v3.0)theme.link.spacePaddingBlockDefaulttheme.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).
// Before (v2.3)theme.icon.colorContentDefault// After (v3.0) — verify semantic equivalence before replacingtheme.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).
// 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) | |
|---|---|---|
OrangeThemeControlItemComponentTokensProvider | OrangeThemeListItemComponentTokensProvider | |
OrangeCompactThemeControlItemComponentTokensProvider | OrangeCompactThemeListItemComponentTokensProvider | |
SoshThemeControlItemComponentTokensProvider | SoshThemeListItemComponentTokensProvider | |
WireframeThemeControlItemComponentTokensProvider | WireframeThemeListItemComponentTokensProvider | |
AllControlItemComponentTokensProvider | AllListItemComponentTokensProvider | |
ControlItemComponentTokens | ListItemComponentTokens |
// 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:) |
// 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 singleOUDSImage. - Replace
.textAndIcon(text:icon:renderingMode:)with.textAndImage(text:image:), wrapping the asset and rendering mode into a singleOUDSImage.
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) |
// 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 singleOUDSImage. - 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:toimage:. - The type changes from
OUDSImage(required) toOUDSImage?with a default value ofnil, 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) |
// 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() | |
OUDSLegacyTabBarModifier | OUDSLegacyLayoutModifier |
// 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 |
// 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.Labelfor the label, with cases.textual(OUDSTextualContent, isBold:),.custom(AnyView, accessibilityLabel:)and the deprecated.text(String, isBold:); - a
OUDSTextualContent?(public,@frozenenum with.raw(String),.localized(LocalizedStringKey, tableName:, bundle:)and.rich(AttributedString)) for each ofdescription,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:
// Before (v3.0) — only `overline` could be styledOUDSListItemData(label: "Foo", overline: myStyledOverline)// After (v3.2) — any textual field can be styled or localizedOUDSListItemData(label: .textual(.rich(myStyledLabel)),description: .rich(myStyledDescription),overline: .rich(myStyledOverline),extraLabel: .localized("extra.label.key"),helperText: "Plain string literal")
Concrete example with colors:
import OUDSSwiftUIimport 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 exampleOUDSStaticListItem(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 onlytextSlot/bottomSlot) withOUDSListItemData(label: .textual("Foo")). - Replace
hasBoldLabel: truewith.textual("Foo", isBold: true). - Replace
key: LocalizedStringKeywith.textual(.localized(key, tableName:, bundle:)). - Replace
overline: myAttributedStringwithoverline: .rich(myAttributedString)and move theoverline:parameter to its canonical position (afterdescription:in the new init order). - Replace any
Label.text(…)with.textual(…).
Literal pitfall: the new initializers reuse thelabel:name of the deprecated ones, andLabel/AttributedStringare 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 onlytextSlot/bottomSlot: writelabel: .textual("Foo");- a custom view label with a literaloverline: writeoverline: .raw("Over").With a literal label next to a literaldescription,overline,extraLabelorhelperText, 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)withItem(.rich(myAttributedString)). - Replace
Item(someStringVariable)withItem(.raw(someStringVariable)). - Replace
Item(LocalizedStringKey(…), tableName:, bundle:)withItem(.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: someString | description: .raw(someString) | |
description: myAttributedString | description: .rich(myAttributedString) | |
bulletList: someStrings | bulletList: someStrings.map { .raw($0) } | |
bulletList: myAttributedStrings | bulletList: myAttributedStrings.map { .rich($0) } |
Required action:
- Replace
description: myAttributedStringwithdescription: .rich(myAttributedString). - Replace
description: someStringVariablewithdescription: .raw(someStringVariable). - Replace
bulletListvariables of[String]/[AttributedString]with arrays of.raw(…)/.rich(…); an array literal can mix raw literals,.localized(…)and.rich(…).
Literal pitfall: with theLocalizedStringKeyinitializer, a baredescription: "abc"may select a deprecated initializer (warning), becauseAttributedStringis expressible by a string literal. Usedescription: .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: myAttributedString | errorText: .rich(myAttributedString) | |
errorText: nil / omitted | unchanged |
Literal pitfall: a bareerrorText: "abc"may select a deprecated initializer (warning), becauseAttributedStringis expressible by a string literal. Always writeerrorText: .raw("abc").
Required action:
- Replace
errorText: myAttributedStringwitherrorText: .rich(myAttributedString). - Replace
errorText: someString/errorText: "Message"witherrorText: .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: myAttributedString | errorText: .rich(myAttributedString) | |
errorText: nil / omitted | unchanged |
Literal pitfall: a bareerrorText: "abc"may select a deprecated initializer (warning), becauseAttributedStringis expressible by a string literal. Always writeerrorText: .raw("abc").
Required action:
- Replace
errorText: myAttributedStringwitherrorText: .rich(myAttributedString). - Replace
errorText: someString/errorText: "Message"witherrorText: .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: myAttributedString | errorText: .rich(myAttributedString) | |
errorText: nil / omitted | unchanged |
Literal pitfall: a bareerrorText: "abc"may select a deprecated initializer (warning), becauseAttributedStringis expressible by a string literal. Always writeerrorText: .raw("abc").
Required action:
- Replace
errorText: myAttributedStringwitherrorText: .rich(myAttributedString). - Replace
errorText: someString/errorText: "Message"witherrorText: .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: someString | errorText: .raw(someString) | |
errorText: "Message" (literal), nil / omitted | unchanged (no warning, there is no AttributedString initializer to compete) |
Required action:
- Replace
errorText: someStringVariablewitherrorText: .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: myAttributedString | helperText: .rich(myAttributedString) | |
helperText: LocalizedStringKey("key"), tableName: t, bundle: b | helperText: .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 barehelperText: "abc"may select a deprecated initializer (warning), becauseAttributedStringandLocalizedStringKeyare expressible by a string literal. Always writehelperText: .raw("abc").
Required action:
- Replace
helperText: myAttributedStringwithhelperText: .rich(myAttributedString). - Replace
helperText: someString/helperText: "Message"withhelperText: .raw(…). - Replace
helperText: LocalizedStringKey(…), tableName:, bundle:withhelperText: .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: myAttributedString | helperText: .rich(myAttributedString) | |
status: .error(message: "Message") | status: .textualError(message: "Message") | |
status: .richError(message: myAttributedString) | status: .textualError(message: .rich(myAttributedString)) |
Literal pitfall: a barehelperText: "abc"may select a deprecated initializer (warning), becauseAttributedStringis expressible by a string literal. Always writehelperText: .raw("abc").
Required action:
- Replace
helperText: myAttributedStringwithhelperText: .rich(myAttributedString). - Replace
helperText: someString/helperText: "Message"withhelperText: .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: myAttributedString | helperText: .rich(myAttributedString) |
Literal pitfall: a barehelperText: "abc"may select a deprecated initializer (warning), becauseAttributedStringis expressible by a string literal. Always writehelperText: .raw("abc").
Required action:
- Replace
helperText: myAttributedStringwithhelperText: .rich(myAttributedString). - Replace
helperText: someString/helperText: "Message"withhelperText: .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 byHelperText.textual(OUDSTextualContent)(.charactersMaxCount(_:)is unchanged).Status.error(message: String)and.richError(message: AttributedString)are deprecated and replaced byStatus.textualError(message: OUDSTextualContent)(renamed.errorin 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, aStringvariable needs.raw(…)). - Replace
.rich(myAttributedString)with.textual(.rich(myAttributedString)). - Replace
.error(message:)/.richError(message:)with.textualError(message:).
Verification checklist
swift buildswift build 2>&1 | grep -i "deprecated" | grep -iv "apple\|system\|swift\|foundation"# v3.0 breaking changes — must return nothinggrep -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 nothinggrep -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 nothinggrep -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