Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- BREAKING: `.showOnboardingIfNeeded` and `.presentOnboardingIfNeeded` now only manage storage/presentation state and take an `onboardingContent` view builder. Provide your onboarding UI and call the supplied `markComplete` action when finished.
- `WelcomeScreen` now conforms to `View` directly—use `.with(continueAction:)` to inject your completion handler instead of wrapping in a separate view type.
- Lowered minimum deployment targets to iOS 16.0 and macOS 13.0.
- Replaced newer style/shape API usage with compatibility-safe fallbacks where needed.

### Added
- New `WelcomeScreen.modern` layout with card-style feature list and inline terms/privacy links.
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ let package = Package(
name: "SwiftUI-Onboarding",
defaultLocalization: "en",
platforms: [
.macOS(.v15),
.iOS(.v18)
.macOS(.v13),
.iOS(.v16)
],
products: PackageProduct.allCases.map(\.description),
targets: InternalTarget.allCases.map(\.target)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ SwiftUI-Onboarding is a SwiftUI library that provides an Apple-like app onboardi

## Requirements

- **iOS**: 18.0 or later
- **macOS**: 15.0 or later
- **iOS**: 16.0 or later
- **macOS**: 13.0 or later
- **Swift**: 6.0 or later

## Installation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extension NotificationsPriming: View {
footer
}
}
.background(.background.secondary)
.background(Color.onboardingSecondaryBackground)
.onAppear(perform: onAppear)
.dynamicTypeSize(.xSmall ... .xxxLarge)
}
Expand Down Expand Up @@ -53,16 +53,12 @@ extension NotificationsPriming: View {
}
}
.padding(20)
.background(.background.secondary)
.background(Color.onboardingSecondaryBackground)
}

private var devicePreview: some View {
UnevenRoundedRectangle(
topLeadingRadius: 48,
topTrailingRadius: 48,
style: .continuous
)
.fill(.background.secondary)
RoundedRectangle(cornerRadius: 48, style: .continuous)
.fill(Color.onboardingSecondaryBackground)
.stroke(.secondary.opacity(0.6), lineWidth: 4)
.mask {
LinearGradient(
Expand Down Expand Up @@ -103,19 +99,19 @@ extension NotificationsPriming: View {

Text(config.notificationTimestamp, bundle: config.bundle)
.font(.footnote.weight(.semibold))
.foregroundStyle(.secondary)
.foregroundColor(.secondary)
}

Text(config.notificationBody, bundle: config.bundle)
.font(.subheadline)
.foregroundStyle(.secondary)
.foregroundColor(.secondary)
.lineLimit(2)
}
}
.padding(14)
.background(
.background.tertiary.opacity(0.9),
in: .rect(cornerRadius: 16)
Color.onboardingTertiaryBackground.opacity(0.9),
in: RoundedRectangle(cornerRadius: 16, style: .continuous)
)
.overlay {
RoundedRectangle(cornerRadius: 16, style: .continuous)
Expand All @@ -129,7 +125,7 @@ extension NotificationsPriming: View {
appIcon
.resizable()
.frame(width: 44, height: 44)
.clipShape(.rect(cornerRadius: 12))
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
} else {
RoundedRectangle(cornerRadius: 12, style: .continuous)
.fill(config.accentColor.opacity(0.18))
Expand All @@ -151,7 +147,7 @@ extension NotificationsPriming: View {

Text(config.subtitle, bundle: config.bundle)
.font(.body.weight(.medium))
.foregroundStyle(.secondary)
.foregroundColor(.secondary)
}
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extension AppleBottomSection: View {
}
.padding(.horizontal, 28)
.padding(.vertical, 24)
.background(.background.secondary)
.background(Color.onboardingSecondaryBackground)
.mask(opacityLinearGradient)
.opacity(isAnimating ? 1 : 0)
.onAppear(perform: onAppear)
Expand All @@ -68,9 +68,9 @@ extension AppleBottomSection: View {
private var disclosureText: some View {
Group {
Text(verbatim: appDisplayName)
.foregroundStyle(.secondary) +
.foregroundColor(.secondary) +
Text(.privacyDataCollection, bundle: .module)
.foregroundStyle(.secondary) +
.foregroundColor(.secondary) +
Text(.privacyDataManagement, bundle: .module)
.foregroundStyle(accentColor)
.bold()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ struct FeatureView: View {

private var contentText: some View {
Text(info.content)
.foregroundStyle(.secondary)
.foregroundColor(.secondary)
.font(.subheadline)
}
}
Expand All @@ -94,6 +94,6 @@ struct FeatureView: View {
}
.padding(40)
}
.defaultScrollAnchor(.center, for: .alignment)
.scrollBounceBehavior(.basedOnSize)
.onboardingCenteredScrollAnchor()
.onboardingBasedOnSizeScrollBounce()
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ extension AppleTitleSection: View {
config.appIcon
.resizable()
.frame(width: 60, height: 60)
.clipShape(.rect(cornerRadius: 10))
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
.padding(.bottom)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ extension AppleWelcomeScreen: View {
.padding(.vertical, 24)
}
.scrollIndicators(.hidden)
.defaultScrollAnchor(.center, for: .alignment)
.scrollBounceBehavior(.basedOnSize)
.background(.background.secondary)
.onboardingCenteredScrollAnchor()
.onboardingBasedOnSizeScrollBounce()
.background(Color.onboardingSecondaryBackground)
.safeAreaInset(edge: .bottom, content: bottomSection)
.onAppear(perform: onAppear)
.dynamicTypeSize(.xSmall ... .xxxLarge)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ extension ModernBottomSection: View {
.tint(accentColor)
}
.padding(20)
.background(.background.secondary)
.background(Color.onboardingSecondaryBackground)
.opacity(isAnimating ? 1 : 0)
.onAppear(perform: onAppear)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct ModernDisclaimerSection: View {
VStack(alignment: .center, spacing: 8) {
Text(.modernDisclaimerPrefix, bundle: .module)
.font(.footnote)
.foregroundStyle(.secondary)
.foregroundColor(.secondary)

HStack(spacing: 4) {
Text(.modernDisclaimerTerms, bundle: .module)
Expand All @@ -28,7 +28,7 @@ struct ModernDisclaimerSection: View {
}
Text(.modernDisclaimerAnd, bundle: .module)
.font(.footnote)
.foregroundStyle(.secondary)
.foregroundColor(.secondary)
Text(.modernDisclaimerPrivacy, bundle: .module)
.font(.footnote.weight(.semibold))
.foregroundStyle(accentColor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ private struct ModernFeatureCard: View {
.frame(width: 40, height: 40)
.foregroundStyle(accentColor)
.background(
.background.tertiary,
in: .rect(cornerRadius: 12)
Color.onboardingTertiaryBackground,
in: RoundedRectangle(cornerRadius: 12, style: .continuous)
)

VStack(alignment: .leading, spacing: 8) {
Expand All @@ -68,15 +68,15 @@ private struct ModernFeatureCard: View {

Text(feature.content)
.font(.subheadline)
.foregroundStyle(.secondary)
.foregroundColor(.secondary)
}

Spacer()
}
.padding(16)
.background(
.background.secondary,
in: .rect(cornerRadius: 12)
Color.onboardingSecondaryBackground,
in: RoundedRectangle(cornerRadius: 12, style: .continuous)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ extension ModernTitleSection: View {
config.appIcon
.resizable()
.frame(width: 60, height: 60)
.clipShape(.rect(cornerRadius: 10))
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
.padding(.bottom)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ extension ModernWelcomeScreen: View {
}
.scrollIndicators(.hidden)
.safeAreaInset(edge: .bottom, content: bottomSection)
.background(.background.secondary)
.background(Color.onboardingSecondaryBackground)
.onAppear(perform: onAppear)
.dynamicTypeSize(.xSmall ... .xxxLarge)
}
Expand Down
55 changes: 55 additions & 0 deletions Sources/Onboarding/Utilities/View+Compatibility.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// View+Compatibility.swift
//
// Created by James Sedlacek on 3/15/26.
//

import SwiftUI

#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif

extension Color {
static var onboardingSecondaryBackground: Color {
#if canImport(UIKit)
Color(uiColor: .secondarySystemBackground)
#elseif canImport(AppKit)
Color(nsColor: .windowBackgroundColor)
#else
Color.secondary
#endif
}

static var onboardingTertiaryBackground: Color {
#if canImport(UIKit)
Color(uiColor: .tertiarySystemBackground)
#elseif canImport(AppKit)
Color(nsColor: .controlBackgroundColor)
#else
Color.secondary.opacity(0.5)
#endif
}
}

extension View {
@ViewBuilder
func onboardingCenteredScrollAnchor() -> some View {
if #available(iOS 18, macOS 15, *) {
self.defaultScrollAnchor(.center, for: .alignment)
} else {
self
}
}

@ViewBuilder
func onboardingBasedOnSizeScrollBounce() -> some View {
if #available(iOS 16.4, macOS 13.3, *) {
self.scrollBounceBehavior(.basedOnSize)
} else {
self
}
}
}