--- 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() 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() } } ```