Files
obsidian-vault/work/wiki/apple-browsers/shared-packages.md
T

8.3 KiB

source, confidence, namespace, last_synced, alwaysApply
source confidence namespace last_synced alwaysApply
~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/shared-packages.mdc 0.9 work 2026-04-28 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-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

#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

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

// 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

// 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

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

// 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

// 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

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

// 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

/// 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

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

// 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

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

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