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

363 lines
8.3 KiB
Markdown

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