--- source: ~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/swiftui-advanced.mdc confidence: 0.9 namespace: work last_synced: 2026-04-28 alwaysApply: false --- # Advanced SwiftUI Patterns ## ViewModifier Composition Create reusable ViewModifiers for common styling: ```swift // ✅ ADVANCED - Composable ViewModifiers struct DuckDuckGoButtonStyle: ViewModifier { let style: ButtonStyleType func body(content: Content) -> some View { content .padding(.horizontal, 16) .padding(.vertical, 12) .background(backgroundColorForStyle()) .foregroundColor(textColorForStyle()) .cornerRadius(8) .font(.body.weight(.medium)) } private func backgroundColorForStyle() -> Color { switch style { case .primary: return Color(designSystemColor: .buttonPrimaryBackground) case .secondary: return Color(designSystemColor: .buttonSecondaryBackground) case .ghost: return .clear } } private func textColorForStyle() -> Color { switch style { case .primary: return Color(designSystemColor: .buttonPrimaryText) case .secondary: return Color(designSystemColor: .buttonSecondaryText) case .ghost: return Color(designSystemColor: .textLink) } } } // Usage extension View { func duckDuckGoButtonStyle(_ style: ButtonStyleType) -> some View { modifier(DuckDuckGoButtonStyle(style: style)) } } ``` ## PreferenceKey for Cross-View Communication Use PreferenceKey for sophisticated view communication: ```swift // ✅ ADVANCED - PreferenceKey for collecting data from child views struct ViewSizePreferenceKey: PreferenceKey { static var defaultValue: [String: CGSize] = [:] static func reduce(value: inout [String: CGSize], nextValue: () -> [String: CGSize]) { value.merge(nextValue()) { $1 } } } struct SizeReportingView: View { let id: String let content: Content init(id: String, @ViewBuilder content: () -> Content) { self.id = id self.content = content() } var body: some View { content .background( GeometryReader { geometry in Color.clear .preference(key: ViewSizePreferenceKey.self, value: [id: geometry.size]) } ) } } // Usage struct ParentView: View { @State private var childSizes: [String: CGSize] = [:] var body: some View { VStack { SizeReportingView(id: "header") { HeaderView() } SizeReportingView(id: "content") { ContentView() } } .onPreferenceChange(ViewSizePreferenceKey.self) { sizes in self.childSizes = sizes } } } ``` ## Environment-based Dependency Injection Use SwiftUI Environment for dependency injection: ```swift // ✅ ADVANCED - Environment-based DI struct DependencyProviderKey: EnvironmentKey { static let defaultValue: DependencyProvider = AppDependencyProvider.shared } extension EnvironmentValues { var dependencies: DependencyProvider { get { self[DependencyProviderKey.self] } set { self[DependencyProviderKey.self] = newValue } } } // Usage in views struct FeatureView: View { @Environment(\.dependencies) var dependencies @StateObject private var viewModel: FeatureViewModel init() { // Note: This approach has limitations - see ios-architecture.md for preferred DI pattern } var body: some View { // View implementation } } ``` ## Complex Animation Patterns Use sophisticated animations for better UX: ```swift // ✅ ADVANCED - Coordinated animations struct TabSwitcherView: View { @State private var selectedTab: Int = 0 @State private var animationPhase: AnimationPhase = .idle enum AnimationPhase { case idle, switching, settled } var body: some View { VStack { TabPickerView(selectedTab: $selectedTab) .animation(.easeInOut(duration: 0.3), value: selectedTab) TabContent(selectedTab: selectedTab) .transition(.asymmetric( insertion: .move(edge: .trailing).combined(with: .opacity), removal: .move(edge: .leading).combined(with: .opacity) )) .animation(.spring(response: 0.6, dampingFraction: 0.8), value: selectedTab) } .onChange(of: selectedTab) { newValue in withAnimation(.easeInOut(duration: 0.1)) { animationPhase = .switching } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { withAnimation(.easeOut(duration: 0.1)) { animationPhase = .settled } } } } } ``` ## Custom Layout with Layout Protocol (iOS 16+) Create sophisticated layouts: ```swift // ✅ ADVANCED - Custom layout for complex arrangements @available(iOS 16.0, macOS 13.0, *) struct FlexibleGrid: Layout { let spacing: CGFloat let itemSize: CGSize func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { let containerWidth = proposal.width ?? 300 let itemsPerRow = max(1, Int(containerWidth / (itemSize.width + spacing))) let rows = (subviews.count + itemsPerRow - 1) / itemsPerRow let totalWidth = CGFloat(itemsPerRow) * itemSize.width + CGFloat(itemsPerRow - 1) * spacing let totalHeight = CGFloat(rows) * itemSize.height + CGFloat(rows - 1) * spacing return CGSize(width: totalWidth, height: totalHeight) } func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { let containerWidth = bounds.width let itemsPerRow = max(1, Int(containerWidth / (itemSize.width + spacing))) for (index, subview) in subviews.enumerated() { let row = index / itemsPerRow let column = index % itemsPerRow let x = bounds.minX + CGFloat(column) * (itemSize.width + spacing) let y = bounds.minY + CGFloat(row) * (itemSize.height + spacing) subview.place(at: CGPoint(x: x, y: y), proposal: ProposedViewSize(itemSize)) } } } ``` ## State Management with @Observable (iOS 17+) Use the modern @Observable macro when available: ```swift // ✅ MODERN - @Observable for iOS 17+ @available(iOS 17.0, macOS 14.0, *) @Observable final class ModernViewModel { var items: [Item] = [] var isLoading = false var selectedItem: Item? private let service: FeatureServiceProtocol init(service: FeatureServiceProtocol) { self.service = service } @MainActor func loadData() async { isLoading = true defer { isLoading = false } do { items = try await service.fetchItems() } catch { // Handle error } } } // Usage struct ModernFeatureView: View { @State private var viewModel: ModernViewModel init(service: FeatureServiceProtocol) { self._viewModel = State(initialValue: ModernViewModel(service: service)) } var body: some View { List(viewModel.items, selection: $viewModel.selectedItem) { item in ItemRow(item: item) } .task { await viewModel.loadData() } } } ``` See `swiftui-style.md` for basic SwiftUI patterns and design system integration.