[2026-04-28] Add apple-browsers .cursor rules (43 files) + executor v2 context
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
---
|
||||
source: ~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/shared-packages.mdc
|
||||
confidence: 0.9
|
||||
namespace: work
|
||||
last_synced: 2026-04-28
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Shared Packages Development Guidelines
|
||||
|
||||
## Package Structure
|
||||
|
||||
### Standard Package Layout
|
||||
```
|
||||
SharedPackages/
|
||||
├── FeatureName/
|
||||
│ ├── Package.swift
|
||||
│ ├── README.md
|
||||
│ ├── Sources/
|
||||
│ │ └── FeatureName/
|
||||
│ │ ├── Public/ # Public API
|
||||
│ │ ├── Internal/ # Internal implementation
|
||||
│ │ └── Resources/ # Assets and resources
|
||||
│ └── Tests/
|
||||
│ └── FeatureNameTests/
|
||||
│ └── FeatureTests.swift
|
||||
```
|
||||
|
||||
### Package.swift Configuration
|
||||
```swift
|
||||
// swift-tools-version: 5.7
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "FeatureName",
|
||||
platforms: [
|
||||
.iOS(.v15),
|
||||
.macOS(.v12)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "FeatureName",
|
||||
targets: ["FeatureName"]
|
||||
)
|
||||
],
|
||||
dependencies: [
|
||||
// Only include truly necessary dependencies
|
||||
.package(url: "https://github.com/DuckDuckGo/BrowserServicesKit", from: "1.0.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "FeatureName",
|
||||
dependencies: ["BrowserServicesKit"],
|
||||
resources: [
|
||||
.process("Resources")
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "FeatureNameTests",
|
||||
dependencies: ["FeatureName"]
|
||||
)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## Cross-Platform Compatibility
|
||||
|
||||
### Platform-Specific Code
|
||||
```swift
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
public typealias PlatformView = UIView
|
||||
public typealias PlatformViewController = UIViewController
|
||||
public typealias PlatformColor = UIColor
|
||||
#elseif os(macOS)
|
||||
import AppKit
|
||||
public typealias PlatformView = NSView
|
||||
public typealias PlatformViewController = NSViewController
|
||||
public typealias PlatformColor = NSColor
|
||||
#endif
|
||||
|
||||
// Use platform-agnostic types
|
||||
public protocol CrossPlatformViewProtocol {
|
||||
var backgroundColor: PlatformColor? { get set }
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Compilation
|
||||
```swift
|
||||
public class FeatureManager {
|
||||
public func performAction() {
|
||||
#if os(iOS)
|
||||
performIOSAction()
|
||||
#elseif os(macOS)
|
||||
performMacOSAction()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private func performIOSAction() {
|
||||
// iOS-specific implementation
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
private func performMacOSAction() {
|
||||
// macOS-specific implementation
|
||||
}
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
## API Design
|
||||
|
||||
### Public API Guidelines
|
||||
```swift
|
||||
// Mark public APIs clearly
|
||||
public protocol FeatureServiceProtocol {
|
||||
func fetchData() async throws -> [Item]
|
||||
}
|
||||
|
||||
public final class FeatureService: FeatureServiceProtocol {
|
||||
// Use dependency injection
|
||||
private let networkClient: NetworkClientProtocol
|
||||
|
||||
public init(networkClient: NetworkClientProtocol) {
|
||||
self.networkClient = networkClient
|
||||
}
|
||||
|
||||
public func fetchData() async throws -> [Item] {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Internal Implementation
|
||||
```swift
|
||||
// Keep implementation details internal
|
||||
internal final class FeatureImplementation {
|
||||
// Not exposed to package consumers
|
||||
}
|
||||
|
||||
// Use extensions for internal helpers
|
||||
internal extension String {
|
||||
var sanitized: String {
|
||||
// Internal helper method
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Resource Management
|
||||
|
||||
### Bundled Resources
|
||||
```swift
|
||||
public enum FeatureResources {
|
||||
private static let bundle = Bundle.module
|
||||
|
||||
public static var configuration: Data {
|
||||
guard let url = bundle.url(forResource: "config", withExtension: "json"),
|
||||
let data = try? Data(contentsOf: url) else {
|
||||
fatalError("Missing required resource: config.json")
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
public static func image(named name: String) -> PlatformImage? {
|
||||
#if os(iOS)
|
||||
return UIImage(named: name, in: bundle, with: nil)
|
||||
#elseif os(macOS)
|
||||
return bundle.image(forResource: name)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Management
|
||||
|
||||
### Minimal Dependencies
|
||||
```swift
|
||||
// Avoid unnecessary dependencies
|
||||
// Bad: Importing entire framework for one function
|
||||
import HeavyFramework
|
||||
|
||||
// Good: Implement minimal version or use protocol
|
||||
protocol DateFormatterProtocol {
|
||||
func string(from date: Date) -> String
|
||||
}
|
||||
```
|
||||
|
||||
### Version Management
|
||||
```swift
|
||||
// Use semantic versioning
|
||||
// Package.swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/example/package",
|
||||
from: "1.0.0"), // Allows 1.x.x
|
||||
.package(url: "https://github.com/example/strict",
|
||||
exact: "2.1.0"), // Exact version
|
||||
.package(url: "https://github.com/example/range",
|
||||
"1.0.0"..<"2.0.0") // Version range
|
||||
]
|
||||
```
|
||||
|
||||
## Testing Shared Packages
|
||||
|
||||
### Cross-Platform Tests
|
||||
```swift
|
||||
import XCTest
|
||||
@testable import FeatureName
|
||||
|
||||
final class FeatureTests: XCTestCase {
|
||||
func testCrossPlatformBehavior() {
|
||||
let feature = Feature()
|
||||
|
||||
#if os(iOS)
|
||||
XCTAssertNotNil(feature.iosSpecificProperty)
|
||||
#elseif os(macOS)
|
||||
XCTAssertNotNil(feature.macOSSpecificProperty)
|
||||
#endif
|
||||
|
||||
// Test common behavior
|
||||
XCTAssertEqual(feature.commonProperty, expectedValue)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test Utilities
|
||||
```swift
|
||||
// Provide test utilities in a separate target
|
||||
public extension XCTestCase {
|
||||
func waitForCondition(
|
||||
_ condition: @autoclosure () -> Bool,
|
||||
timeout: TimeInterval = 1.0,
|
||||
message: String = "Condition not met"
|
||||
) {
|
||||
let expectation = expectation(description: message)
|
||||
|
||||
Task {
|
||||
while !condition() {
|
||||
try? await Task.sleep(nanoseconds: 100_000_000) // 0.1s
|
||||
}
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
wait(for: [expectation], timeout: timeout)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
### Package Documentation
|
||||
```swift
|
||||
/// A service for managing user preferences across platforms.
|
||||
///
|
||||
/// This service provides a unified interface for storing and retrieving
|
||||
/// user preferences, with platform-specific implementations for iOS and macOS.
|
||||
///
|
||||
/// ## Usage Example
|
||||
/// ```swift
|
||||
/// let preferences = UserPreferencesService()
|
||||
/// preferences.set("value", for: .theme)
|
||||
/// let theme = preferences.get(.theme)
|
||||
/// ```
|
||||
public final class UserPreferencesService {
|
||||
/// Initializes a new preferences service.
|
||||
///
|
||||
/// - Parameter storage: The storage backend to use. Defaults to UserDefaults.
|
||||
public init(storage: PreferencesStorage = .userDefaults) {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Migration and Versioning
|
||||
|
||||
### API Evolution
|
||||
```swift
|
||||
public protocol FeatureProtocolV2 {
|
||||
// New required method
|
||||
func newRequiredMethod()
|
||||
|
||||
// Existing method
|
||||
func existingMethod()
|
||||
}
|
||||
|
||||
// Provide default implementation for backward compatibility
|
||||
public extension FeatureProtocolV2 {
|
||||
func newRequiredMethod() {
|
||||
// Default implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecation
|
||||
@available(*, deprecated, renamed: "newMethod()")
|
||||
public func oldMethod() {
|
||||
newMethod()
|
||||
}
|
||||
```
|
||||
|
||||
### Breaking Changes
|
||||
```swift
|
||||
// Use versioned types when making breaking changes
|
||||
public struct ConfigurationV1 {
|
||||
public let setting: String
|
||||
}
|
||||
|
||||
public struct ConfigurationV2 {
|
||||
public let setting: String
|
||||
public let newRequired: Bool
|
||||
|
||||
// Provide migration
|
||||
public init(from v1: ConfigurationV1) {
|
||||
self.setting = v1.setting
|
||||
self.newRequired = false // Default value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Lazy Loading
|
||||
```swift
|
||||
public final class ResourceManager {
|
||||
// Lazy load expensive resources
|
||||
private lazy var heavyResource: HeavyResource = {
|
||||
return HeavyResource()
|
||||
}()
|
||||
|
||||
// Use computed properties for lightweight calculations
|
||||
public var lightweightValue: String {
|
||||
return "Calculated on demand"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Management
|
||||
```swift
|
||||
public final class CacheManager {
|
||||
private let cache = NSCache<NSString, CacheItem>()
|
||||
|
||||
public init() {
|
||||
// Configure cache limits
|
||||
cache.countLimit = 100
|
||||
cache.totalCostLimit = 50 * 1024 * 1024 // 50MB
|
||||
|
||||
// Respond to memory warnings
|
||||
#if os(iOS)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(clearCache),
|
||||
name: UIApplication.didReceiveMemoryWarningNotification,
|
||||
object: nil
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
@objc private func clearCache() {
|
||||
cache.removeAllObjects()
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user