| Sources/Bedrock | ||
| Tests/BedrockTests | ||
| .gitignore | ||
| Package.swift | ||
| README.md | ||
Bedrock
A foundational design system and UI component library for building consistent, beautiful SwiftUI applications.
Overview
Bedrock is designed to be the foundation upon which apps are built, providing:
- Design System: Consistent spacing, typography, colors, and animations
- Color Protocols: Define consistent color naming with custom palettes per app
- Settings Components: Ready-to-use toggle, picker, and selector views
- Sound & Haptics: Generic sound and haptic feedback management
- Onboarding: Track user onboarding progress and contextual hints
- Cloud Sync: Generic iCloud and local data synchronization
- Visual Effects: Confetti, pulsing animations, debug borders
- Utilities: Device detection, debugging helpers, and more
Installation
Add Bedrock as a dependency in your Package.swift:
dependencies: [
.package(url: "https://github.com/yourname/Bedrock.git", from: "1.0.0")
]
Or for local development:
dependencies: [
.package(path: "../Bedrock")
]
Then add it to your target:
.target(
name: "YourApp",
dependencies: ["Bedrock"]
)
Usage
Design Constants
Use the Design enum for consistent spacing, sizing, and styling:
import Bedrock
struct MyView: View {
var body: some View {
VStack(spacing: Design.Spacing.medium) {
Text("Hello")
.font(.system(size: Design.BaseFontSize.title))
.padding(Design.Spacing.large)
Button("Tap Me") { }
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
}
}
}
Color System
Bedrock provides a protocol-based color system that enforces consistent naming while allowing apps to define their own palettes.
Using Default Colors
Bedrock includes neutral default colors that work out of the box:
Text("Primary Text")
.foregroundStyle(Color.Text.primary)
Text("Secondary Text")
.foregroundStyle(Color.Text.secondary)
VStack { }
.background(Color.Surface.primary)
Creating Custom Color Themes
Apps can define custom color palettes by conforming to the color protocols:
// Define custom accent colors
public enum MyAppAccentColors: AccentColorProvider {
public static let primary = Color(red: 0.9, green: 0.75, blue: 0.3)
public static let light = Color(red: 1.0, green: 0.85, blue: 0.4)
public static let dark = Color(red: 0.7, green: 0.55, blue: 0.2)
public static let secondary = Color(red: 0.2, green: 0.7, blue: 0.7)
}
// Create a complete theme
public enum MyAppTheme: AppColorTheme {
public typealias Surface = DefaultSurfaceColors // Reuse Bedrock defaults
public typealias Text = DefaultTextColors
public typealias Accent = MyAppAccentColors // Custom accents
public typealias Button = DefaultButtonColors
public typealias Status = DefaultStatusColors
public typealias Border = DefaultBorderColors
public typealias Interactive = DefaultInteractiveColors
}
// Use in views
Button("Action") { }
.background(MyAppTheme.Accent.primary)
Sound & Haptic Feedback
Bedrock provides a generic sound manager that works with any app-defined sounds:
// Define your app's sounds
enum MySound: String, AppSound {
case tap, success, error
var resourceName: String { rawValue }
var resourceExtension: String { "mp3" }
var bundle: Bundle { .main }
var fallbackSystemSoundID: UInt32? { nil }
}
// Play sounds and haptics
let soundManager = SoundManager.shared
soundManager.play(MySound.success)
soundManager.playHaptic(.success)
Onboarding State
Track user onboarding and progressive feature discovery:
let onboarding = OnboardingState(appIdentifier: "myApp")
// Register all hint keys for skip functionality
onboarding.registerHintKeys("welcome", "feature1", "feature2")
// Check and show hints
if onboarding.shouldShowHint("welcome") {
// Show welcome hint
onboarding.markHintShown("welcome")
}
// Skip or complete onboarding
onboarding.completeWelcome() // Mark as completed
onboarding.skipOnboarding() // Skip all hints
Cloud Sync
Synchronize data between iCloud and local storage:
// Define your persistable data
struct MyAppData: PersistableData {
static var dataIdentifier = "myApp"
static var empty = MyAppData()
var syncPriority: Int { totalActions }
var lastModified: Date = .now
var totalActions: Int = 0
}
// Use the cloud sync manager
let syncManager = CloudSyncManager<MyAppData>()
await syncManager.save(myData)
let loaded = await syncManager.load()
Visual Effects
Confetti
ZStack {
MainContentView()
if showCelebration {
ConfettiView(colors: [.red, .blue, .green, .yellow])
}
}
Pulsing Animation
Button("Tap Here") { }
.pulsing(isActive: shouldHighlight, color: .white)
Debug Borders
MyComplexView()
.debugBorder(showDebug, color: .red, label: "Header")
Settings Components
Ready-to-use settings UI components:
// Toggle with title and subtitle
SettingsToggle(
title: "Notifications",
subtitle: "Receive push notifications",
isOn: $notificationsEnabled
)
// Volume slider
VolumePicker(label: "Volume", volume: $soundVolume)
// Segmented picker
SegmentedPicker(
title: "Theme",
options: [("Light", "light"), ("Dark", "dark"), ("Auto", "auto")],
selection: $theme
)
// Selectable row
SelectableRow(
title: "Premium Plan",
subtitle: "Unlock all features",
isSelected: plan == .premium,
badge: { BadgePill(text: "$9.99", isSelected: plan == .premium) },
action: { plan = .premium }
)
Device Detection
Adapt your UI based on device characteristics:
if DeviceInfo.isPad {
SidebarView()
} else {
TabBarView()
}
let bounds = DeviceInfo.screenBounds
let isLandscape = DeviceInfo.isLandscape
Design System Reference
Spacing
| Name | Value | Usage |
|---|---|---|
xxxSmall |
1pt | Hairline spacing |
xxSmall |
2pt | Minimal spacing |
xSmall |
4pt | Tight spacing |
small |
8pt | Compact spacing |
medium |
12pt | Default spacing |
large |
16pt | Comfortable spacing |
xLarge |
20pt | Generous spacing |
xxLarge |
24pt | Section spacing |
xxxLarge |
32pt | Large section spacing |
Opacity
| Name | Value | Usage |
|---|---|---|
verySubtle |
0.05 | Barely visible |
subtle |
0.1 | Subtle backgrounds |
hint |
0.2 | Hints and disabled states |
light |
0.3 | Light overlays |
medium |
0.5 | Medium emphasis |
strong |
0.7 | Strong emphasis |
heavy |
0.8 | Heavy overlays |
almostFull |
0.9 | Nearly opaque |
Color Protocols
| Protocol | Properties |
|---|---|
SurfaceColorProvider |
primary, secondary, tertiary, overlay, card, groupedFill, sectionFill |
TextColorProvider |
primary, secondary, tertiary, disabled, placeholder, inverse |
AccentColorProvider |
primary, light, dark, secondary |
ButtonColorProvider |
primaryLight, primaryDark, secondary, destructive, cancelText |
StatusColorProvider |
success, warning, error, info |
BorderColorProvider |
subtle, standard, emphasized, selected |
InteractiveColorProvider |
selected, hover, pressed, focus |
Requirements
- iOS 18.0+
- macOS 15.0+
- Swift 6.0+
License
MIT License