--- name: Swift Protocol-Oriented Programming description: Protocol-first architecture patterns for reusability and testability globs: ["**/*.swift"] --- # Protocol-Oriented Programming (POP) **Protocol-first architecture is a priority.** When designing new features, always think about protocols and composition before concrete implementations. ## When Architecting New Code 1. **Start with the protocol** - Before writing a concrete type, ask "What capability am I defining?" and express it as a protocol. 2. **Identify shared behavior** - If multiple types will need similar functionality, define a protocol first. 3. **Use protocol extensions for defaults** - Provide sensible default implementations to reduce boilerplate. 4. **Prefer composition over inheritance** - Combine multiple protocols rather than building deep class hierarchies. ## When Reviewing Existing Code 1. **Look for duplicated patterns** - Similar logic across files is a candidate for protocol extraction. 2. **Identify common interfaces** - Types that expose similar properties/methods should conform to a shared protocol. 3. **Check before implementing** - Search for existing protocols that could be adopted or extended. 4. **Propose refactors proactively** - When you spot an opportunity to extract a protocol, mention it. ## Protocol Design Guidelines ### Naming Conventions Use capability-based suffixes: - `-able`: `Persistable`, `Shareable`, `Validatable` - `-ing`: `DataProviding`, `ErrorHandling`, `Loading` - `-Provider`: `ContentProvider`, `DataProvider` - `-Delegate`: `NavigationDelegate`, `FormDelegate` ### Keep Protocols Focused Each protocol should represent one capability (Interface Segregation Principle): ```swift // GOOD - Focused protocols // Note: Swift provides Identifiable already — adopt it, don't redefine it. protocol Nameable { var displayName: String { get } } protocol Timestamped { var createdAt: Date { get } var updatedAt: Date { get } } // Compose as needed (Identifiable comes from Swift standard library) struct User: Identifiable, Nameable, Timestamped { let id: UUID var displayName: String var createdAt: Date var updatedAt: Date } ``` ```swift // BAD - Kitchen sink protocol protocol Entity { var id: UUID { get } var displayName: String { get } var createdAt: Date { get } var updatedAt: Date { get } func save() async throws func delete() async throws func validate() -> Bool } ``` ### Associated Types Use sparingly. Prefer concrete types or generics at the call site when possible: ```swift // Prefer this for simple cases protocol DataFetching { func fetch(from url: URL) async throws -> T } // Use associated types when the type is fundamental to the protocol protocol Repository { associatedtype Entity func fetch(id: UUID) async throws -> Entity? func save(_ entity: Entity) async throws } ``` ### Value vs Reference Semantics Constrain to `AnyObject` only when reference semantics are required: ```swift // Default - allows structs and classes protocol Configurable { mutating func configure(with options: Options) } // When you need reference semantics (delegates, observers) protocol NavigationDelegate: AnyObject { func didNavigate(to destination: Destination) } ``` ## Protocol Extensions Provide default implementations for common behavior: ```swift protocol Validatable { var validationErrors: [String] { get } var isValid: Bool { get } } extension Validatable { var isValid: Bool { validationErrors.isEmpty } } ``` ## Dependency Injection with Protocols Define protocols for services to enable testing: ```swift protocol NetworkServiceProtocol { func fetch(from url: URL) async throws -> T } // Production implementation final class NetworkService: NetworkServiceProtocol { ... } // Test mock final class MockNetworkService: NetworkServiceProtocol { ... } ``` ## Benefits - **Reusability** - Shared protocols work across features - **Testability** - Mock types can conform to protocols for unit testing - **Flexibility** - New features can adopt existing protocols immediately - **Maintainability** - Fix a bug in a protocol extension, fix it everywhere - **Discoverability** - Protocols document the expected interface clearly ## Common Patterns ### Repository Pattern ```swift protocol Repository { associatedtype Entity: Identifiable func fetch(id: Entity.ID) async throws -> Entity? func fetchAll() async throws -> [Entity] func save(_ entity: Entity) async throws func delete(_ entity: Entity) async throws } ``` ### Service Pattern ```swift protocol AuthServiceProtocol { var isAuthenticated: Bool { get } func signIn(email: String, password: String) async throws func signOut() async throws } ``` ### Coordinator/Navigation Pattern ```swift protocol NavigationCoordinating: AnyObject { func navigate(to destination: Destination) func dismiss() func presentSheet(_ sheet: SheetType) } ```