[2026-04-28] Add apple-browsers .cursor rules (43 files) + executor v2 context
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
---
|
||||
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<Content: View>: 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.
|
||||
Reference in New Issue
Block a user