<< All versions

Skill v1.0.3

currentAutomated scan100/100
orange-opensource/ouds-ios/ouds-ios-framework-usage

2 files

──Details
PublishedOctober 31, 2026 at 12:59 PM
Content Hashsha256:64f7ddd8bda45f71...
Git SHA489f33740842
Bump Typepatch
Compare with v1.0.2
──Files
Files (1 file, 17.1 KB)
SKILL.md17.1 KBactive
SKILL.md · 455 lines · 17.1 KB

name: ouds-ios-framework-usage description: Setup and general usage of the OUDS iOS framework — imports, themes, token namespaces, view modifiers, image rules, shared control patterns and custom font registration. Load this first; then load the matching ouds-ios-components-* skill for a specific component family (actions, content-display, controls, dialogs, foundations, indicators, layouts, navigations). license: MIT metadata: version: "1.0.3"


OUDS Framework Usage

1. Basic setup

swift
import OUDSSwiftUI // Always use the umbrella import
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
OUDSThemeableView(theme: OrangeTheme()) {
ContentView()
}
}
}
}
struct ContentView: View {
@Environment(\.theme) private var theme
var body: some View {
VStack(spacing: theme.spaces.fixedMedium) {
OUDSButton(text: "Action", appearance: .default) {}
}
.padding(theme.spaces.fixedLarge)
}
}

2. Imports

Can import internal modules, but for developer experience there are umbrella products:

ProductThemesWhen
OUDSSwiftUIAllDefault
OUDSSwiftUIOrangeOrange + OrangeCompactOrange apps only
OUDSSwiftUIOrangeSoshOrange + SoshMulti-brand
OUDSSwiftUIWireframeWireframePrototyping
OUDSSwiftUIWhiteLabelWhiteLabelWhite label or gray label branded apps

Internal modules: OUDSComponents, OUDSTokensRaw, OUDSTokensSemantic, OUDSTokensComponent, OUDSThemesOrange, OUDSThemesSosh, OUDSFoundations, etc.


3. Themes

swift
OUDSThemeableView(theme: OrangeTheme()) { … } // inject at root
@Environment(\.theme) private var theme // consume anywhere

Available: OrangeTheme, OrangeCompact, SoshTheme, WireframeTheme, WhiteLabel.


4. Token namespaces

NamespaceContent
theme.colorsColor semantic tokens
theme.colorModesColor-mode tokens (for OUDSColoredSurface)
theme.bordersBorder style / width / radius
theme.fontsTypography
theme.sizesSizes
theme.spacesSpacing
theme.dimensionsDimensions
theme.elevationsShadows / elevation
theme.gridsGrid
theme.opacitiesOpacity
theme.effectsVisual effects

5. View modifiers

swift
Text("Hello")
.font(theme.fonts.bodyDefaultMedium) // typography — use view modifier with token
.foregroundColor(theme.colors.contentDefault)
.background(theme.colors.bgPrimary)
.border(style: theme.borders.styleDefault,
width: theme.borders.widthThin,
radius: theme.borders.radiusMedium,
color: theme.colors.actionEnabled)
.shadow(theme.elevations.emphasized)

For Shape types, use fill(_:style:) with a color token — the light/dark variant is resolved automatically:

swift
Circle()
.fill(theme.colors.actionEnabled) // shape fill with token
RoundedRectangle(cornerRadius: 8)
.fill(theme.colors.bgPrimary, style: FillStyle(eoFill: true)) // with custom fill style

6. Images in OUDS components

Never call SwiftUI modifiers on an `Image` that is wrapped in an `OUDSImage` and passed to an OUDS component.

OUDS components accept an OUDSImage (which wraps the bare SwiftUI Image with its flip flag, accessibility label and rendering mode), through parameters named image:. Calling any modifier on the Image (including .accessibilityHidden(true)) changes its type to some View and produces a compile error.

The component handles the accessibility of its own images internally. You must not alter them from the call site.

Never do this:

swift
OUDSButton(
text: "Add",
image: OUDSImage(asset: Image(systemName: "plus").accessibilityHidden(true)), // ❌ compile error: Image modifier → some View
appearance: .default) {}
OUDSLink(
text: "Back",
image: OUDSImage(asset: Image(systemName: "chevron.left").accessibilityHidden(true)), // ❌ compile error: Image modifier → some View
size: .default) {}

Always do this — pass a bare `Image` inside `OUDSImage`, swiftlint comment on the line before:

swift
// swiftlint:disable:next accessibility_label_for_image
OUDSButton(text: "Add", image: OUDSImage(asset: Image(systemName: "plus")), appearance: .default) {}
// swiftlint:disable:next accessibility_label_for_image
OUDSLink(text: "Back", image: OUDSImage(asset: Image(systemName: "chevron.left")), size: .default) {}
// Exception: OUDSToolBarItem still takes a bare `Image`
// swiftlint:disable:next accessibility_label_for_image
OUDSToolBarItem(icon: Image("ic_share"), accessibilityLabel: "Share") {}

The // swiftlint:disable:next accessibility_label_for_image comment must appear on the line immediately before the component call, never on the line of the Image(...) itself.

Exception: Image(decorative: "name") suppresses the linter rule automatically and needs no comment.


7. Registering custom fonts

To use a custom font family with OUDS, two steps are required after adding the TTF files to your project:

Step 1 — Register the font files (Core Text, call once at app startup):

swift
private static var fontsAlreadyRegistered = false
private func registerFonts() {
guard !Self.fontsAlreadyRegistered else { return }
Bundle.main.urls(forResourcesWithExtension: "ttf", subdirectory: nil)?
.forEach { CTFontManagerRegisterFontsForURL($0 as CFURL, .process, nil) }
Self.fontsAlreadyRegistered = true
}

Step 2 — Register PostScript names for each family + weight combination you use:

swift
registerFont(postScript: "WinkyRough-Regular_Light", forCombination: PSFNMK("Winky Rough", Font.Weight.light))
registerFont(postScript: "WinkyRough-Regular", forCombination: PSFNMK("Winky Rough", Font.Weight.regular))
registerFont(postScript: "WinkyRough-Regular_Medium", forCombination: PSFNMK("Winky Rough", Font.Weight.medium))
registerFont(postScript: "WinkyRough-Regular_SemiBold",forCombination: PSFNMK("Winky Rough", Font.Weight.semibold))
registerFont(postScript: "WinkyRough-Regular_Bold", forCombination: PSFNMK("Winky Rough", Font.Weight.bold))
registerFont(postScript: "WinkyRough-Regular_Black", forCombination: PSFNMK("Winky Rough", Font.Weight.black))

kApplePostScriptFontNames exposes the full map (read-only). OUDS uses it internally to resolve Font objects from theme font tokens. Unregistered combinations fall back to the family name without spaces.


8. Styled AttributedString with hyperlinks

OUDS provides utilities to create styled AttributedString from plain text or Markdown, with custom styling for links based on their text or URL.

Font tokens (e.g. theme.fonts.bodyDefaultMedium) are MultipleFontCompositeSemanticToken values, not SwiftUI Font. Convert them with Font.makeFont(family:from:isCompact:) before using them in AttributedStringUrlConfiguration or AttributedString.from(...).

Basic usage — Markdown

swift
import OUDSComponents
import SwiftUI
let markdown = "Read our [terms of service](https://example.com/terms) and [privacy policy](https://example.com/privacy)"
let textColor = theme.colors.contentDefault.color(for: colorScheme)
let textFont = Font(Font.makeFont(family: theme.fontFamily,
from: theme.fonts.bodyDefaultMedium,
isCompact: horizontalSizeClass == .compact || verticalSizeClass == .compact))
let urlColor = theme.colors.contentBrandPrimary.color(for: colorScheme)
let urlFont = Font(Font.makeFont(family: theme.fontFamily,
from: theme.fonts.bodyStrongMedium,
isCompact: horizontalSizeClass == .compact || verticalSizeClass == .compact))
let configurations: [AttributedStringUrlConfiguration] = [
AttributedStringUrlConfiguration(
text: "terms of service",
color: urlColor,
font: urlFont
),
AttributedStringUrlConfiguration(
text: "privacy policy",
urlToOpen: URL(string: "https://example.com/privacy")!,
color: urlColor,
font: urlFont
),
AttributedStringUrlConfiguration(
color: urlColor,
font: urlFont
)
]
let attributedString = AttributedString.from(
markdown: markdown,
foregroundColor: textColor,
font: textFont,
urlConfigurations: configurations
)

Basic usage — plain text

Use AttributedString.from(text:foregroundColor:font:urlConfigurations:) when the text and the words to turn into hyperlinks are known upfront (no Markdown syntax). Each configuration must provide both text and urlToOpen, otherwise it is ignored:

swift
let attributedString = AttributedString.from(
text: "You must read the terms of service before continuing",
foregroundColor: textColor,
font: textFont,
urlConfigurations: [
AttributedStringUrlConfiguration(
text: "terms of service",
urlToOpen: URL(string: "https://example.com/terms")!,
color: urlColor,
font: urlFont
)
]
)

A LocalizedStringKey-based overload is also available: AttributedString.from(_:tableName:bundle:foregroundColor:font:urlConfigurations:). AttributedStringUrlConfiguration also has a LocalizedStringKey-based initializer: AttributedStringUrlConfiguration(_:tableName:bundle:urlToOpen:color:font:).

Foreground color only (no hyperlinks)

If you only need to apply a color to a whole text or Markdown source (no custom font, no hyperlink styling), use the simpler overloads:

swift
let coloredText = AttributedString.from(text: "Some plain text", foregroundColor: textColor)
let coloredKey = AttributedString.from("some.localized.key", foregroundColor: textColor)
let coloredMarkdown = AttributedString.from(markdown: someMarkdown, foregroundColor: textColor)

Each of these also has a ColorSemanticToken-based overload, e.g. AttributedString.from(text:foregroundColor: ColorSemanticToken).

Configuration options

InitializerUse case
AttributedStringUrlConfiguration(text:urlToOpen:color:font:)Match by text and URL, set custom URL. Required form for AttributedString.from(text:...)
AttributedStringUrlConfiguration(text:color:font:)Match by text only (URL taken from Markdown). Only usable with AttributedString.from(markdown:...)
AttributedStringUrlConfiguration(urlToOpen:color:font:)Match by URL only. Only usable with AttributedString.from(markdown:...)
AttributedStringUrlConfiguration(color:font:)Default fallback for any unmatched link. Only usable with AttributedString.from(markdown:...)

Matching priority (Markdown only)

For each hyperlink found in the Markdown source, the first configuration in the urlConfigurations array that matches (by URL or by text) is applied:

  1. Configurations are scanned in array order; the first one whose urlToOpen matches the link's URL, or whose text matches the link's text, wins.
  2. If no configuration matches, the default configuration (the one with no text and no urlToOpen) is applied, if any.
  3. If neither matches, a warning is logged and the base foregroundColor / font style is kept.

AttributedString.from(text:...) does not use this fallback logic: only configurations with both text and urlToOpen set are applied, matched by exact text.


10. AsyncImage — cached image loading

`OUDSAsyncImage is a cached version of SwiftUI/AsyncImage`. It loads images from URLs and caches them in memory (100MB) and on disk (500MB).

swift
import OUDSSwiftUI
// Basic usage
OUDSAsyncImage(url: URL(string: "https://example.com/photo.png"))
// With content transformation and placeholder
OUDSAsyncImage(url: url) { image in
image.resizable()
} placeholder: {
ProgressView()
}
// With phases for full control
OUDSAsyncImage(url: url) { phase in
switch phase {
case .empty: ProgressView()
case .success(let image): image.resizable()
case .failure: Image(systemName: "photo")
}
}
// Cache management
OUDSAsyncImageCache.shared.clearCache() // memory + disk
OUDSAsyncImageCache.shared.clearMemoryCache()
OUDSAsyncImageCache.shared.clearDiskCache()

The cache uses `OUDSAsyncImageCache/Settings` internally (100MB memory, 500MB disk).


11. Layout and localization helpers

RTL/LTR image flipping

If your app supports right-to-left (RTL) layouts, use `OUDSImage` to flip icons automatically:

swift
@Environment(\.layoutDirection) private var layoutDirection
OUDSCheckboxItem(
"Label",
isOn: $isOn,
image: OUDSImage(
asset: Image(systemName: "figure.handball"),
flipped: layoutDirection == .rightToLeft
),
isReversed: layoutDirection == .rightToLeft
)

Detecting Arabic locale

Use `OUDSUtils/isArabicLanguageInUse()` to detect if Arabic is in use and switch font family:

swift
func localizedHelveticaFont() -> String {
return (OUDSUtils.isArabicLanguageInUse() ? "Helvetica Neue Arabic" : "Helvetica Neue")
}
let theme = OrangeTheme(fontFamily: localizedHelveticaFont())

12. Styled text with .rich(AttributedString)

Several OUDS components accept styled text via OUDSTextualContent or OUDSListItemData.Label. Use .rich(AttributedString) to apply custom colors and fonts.

Important: Use AttributedString.from(text:foregroundColor:font:) to create styled text. The Swift standard library's AttributedString("text", foregroundColor:) initializer does not exist.

Basic colored text

swift
import OUDSSwiftUI
import SwiftUI
// Color only (font is optional)
let redText = AttributedString.from(text: "Error", foregroundColor: .red)
// Color + font
let styledText = AttributedString.from(
text: "Warning",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryNegativeHigh),
font: .body
)
// Using theme colors
let greenText = AttributedString.from(
text: "Success",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryOpacityPositiveMedium),
font: .headline
)

In OUDSListItemData

swift
OUDSStaticListItem(
data: OUDSListItemData(
label: .textual(.rich(AttributedString.from(
text: "Payment problems",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryNegativeHigh),
font: .body
))),
description: .rich(AttributedString.from(
text: "Action required",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryNegativeHigh),
font: .body
)),
overline: .rich(AttributedString.from(
text: "Services activated",
foregroundColor: Color(hexadecimalCode: theme.colors.repositoryOpacityPositiveMedium),
font: .body
)),
extraLabel: .rich(AttributedString.from(
text: "offers available",
foregroundColor: .orange,
font: .body
))
),
trailing: .badge(.count(OUDSBadgeCount(1, accessibilityLabel: "1", status: .negative, size: .medium)))
)

In other components

The same OUDSTextualContent ("literal" / .raw(_:), .localized(_:tableName:bundle:), .rich(_:)) is used by:

ComponentParameters
OUDSListItemDatalabel (through .textual(_:isBold:)), description, overline, extraLabel, helperText
OUDSBulletList.Itemthe text (first parameter)
OUDSAlertMessagedescription, bulletList (array of OUDSTextualContent)
OUDSSwitchItem, OUDSCheckboxItem, OUDSCheckboxItemIndeterminate, OUDSRadioItemerrorText
OUDSPinCodeInput, OUDSTextInput, OUDSPasswordInputhelperText, and status: .textualError(message:)
OUDSTextAreahelperText: .textual(_:), and status: .textualError(message:)
Literal pitfall: AttributedString is itself expressible by a string literal, so a bare literal on a parameter which had an AttributedString overload
may select a deprecated initializer (warning). Prefer .raw("…") (or label: .textual("…") for OUDSListItemData).

13. Component skills

Load the matching skill for the component family you need. Each family skill mirrors OUDS/Core/Components/Sources/<Family>/ in the repo.

FamilySkillComponents
Actionsouds-ios-components-actionsOUDSButton
Content Displayouds-ios-components-content-displayOUDSBulletList
Controlsouds-ios-components-controlsOUDSCheckbox, OUDSRadio, OUDSSwitch, OUDSSuggestionChip / OUDSFilterChip / OUDSChipPicker, OUDSPinCodeInput, OUDSPasswordInput, OUDSTextInput, OUDSTextArea
Dialogsouds-ios-components-dialogsOUDSAlertMessage, OUDSInlineAlert
Foundationsouds-ios-components-foundationsOUDSDisplay, OUDSHeading, OUDSLabel, OUDSBody, OUDSCode
Indicatorsouds-ios-components-indicatorsOUDSBadge*, OUDSTag, OUDSInputTag, OUDSCircularProgressIndicator, OUDSLinearProgressIndicator
Layoutsouds-ios-components-layoutsOUDSColoredSurface, OUDSHorizontalDivider, OUDSVerticalDivider
Navigationsouds-ios-components-navigationsOUDSLink, OUDSTabBar, OUDSToolBarItem / toolBarTop / toolBarBottom

← v1.0.2All versions