Swift'in en güçlü özelliği nedir diye sorulsa, birçok deneyimli geliştirici "generics ve protocol-oriented programming" der. Apple, Swift'i tanıtırken onu "ilk protocol-oriented programlama dili" olarak nitelendirdi. Bu paradigma, kod tekrarını ortadan kaldırır, tip güvenliğini korur ve test edilebilirliği artırır.
💡 Hızlı Not: Bu rehber Apple'ın WWDC15 "Protocol-Oriented Programming in Swift" session'ından ve Swift Evolution proposal'larından derlendi. Swift 5.9+ ve iOS 17 dahil güncel bilgiler içerir.
İçindekiler
- Generic Fonksiyonlar ve Tipler
- Protocol ile Soyutlama
- Associated Types ve Generic Protocol'ler
- Type Erasure: any vs some
- Opaque Types ve some Keyword
- Conditional Conformance
- Generic Constraints ve where Clause
- Protocol Composition
- Gerçek Dünya Kalıpları
- Production Best Practices
Generic Fonksiyonlar ve Tipler {#generic-fonksiyonlar}
Generic'ler, farklı tipler için aynı mantığı tek bir yerde yazmanı sağlar. Basit bir örnekle başlayalım:
swift
1// ❌ Tip başına ayrı fonksiyon - DRY ihlali2func swapInts(_ a: inout Int, _ b: inout Int) {3 let temp = a; a = b; b = temp4}5func swapStrings(_ a: inout String, _ b: inout String) {6 let temp = a; a = b; b = temp7}8 9// ✅ Generic fonksiyon - tek seferlik10func swap<T>(_ a: inout T, _ b: inout T) {11 let temp = a; a = b; b = temp12}Generic Tipler
swift
1// Generic Stack implementasyonu2struct Stack<Element> {3 private var items: [Element] = []4 5 var isEmpty: Bool { items.isEmpty }6 var count: Int { items.count }7 var top: Element? { items.last }8 9 mutating func push(_ item: Element) {10 items.append(item)11 }12 13 @discardableResult14 mutating func pop() -> Element? {15 items.popLast()16 }17}18 19// Kullanım - derleyici tipi çıkarır20var intStack = Stack<Int>()21intStack.push(1)22intStack.push(2)23 24var stringStack = Stack<String>()25stringStack.push("Hello")Protocol ile Soyutlama {#protocol-soyutlama}
Protocol-Oriented Programming'in temelinde, davranışı protocol'ler ile tanımlama ve protocol extension'lar ile varsayılan implementasyon sağlama yatar:
swift
1// Davranış tanımla2protocol Cacheable {3 var cacheKey: String { get }4 var cacheExpiry: TimeInterval { get }5 func serialize() -> Data?6 static func deserialize(from data: Data) -> Self?7}8 9// Varsayılan implementasyon10extension Cacheable {11 var cacheExpiry: TimeInterval { 3600 } // 1 saat varsayılan12 13 func serialize() -> Data? {14 try? JSONEncoder().encode(self as! Encodable)15 }16}17 18// Sadece fark eden kısımları override et19struct UserProfile: Cacheable, Codable {20 let id: UUID21 let name: String22 let email: String23 24 var cacheKey: String { "user_\(id.uuidString)" }25 var cacheExpiry: TimeInterval { 7200 } // 2 saat26 27 static func deserialize(from data: Data) -> UserProfile? {28 try? JSONDecoder().decode(UserProfile.self, from: data)29 }30}OOP vs POP Karşılaştırma
Özellik | OOP (Class Inheritance) | POP (Protocol + Extension) |
|---|---|---|
İlişki türü | is-a (Köpek bir Hayvan'dır) | can-do (Köpek Yüzebilir) |
Çoklu kalıtım | Yok (tek parent class) | Var (çoklu protocol) |
Value type | Hayır (sadece class) | Evet (struct, enum, class) |
Varsayılan impl. | Class'ta | Protocol extension'da |
Bağımlılık | Sıkı (tight coupling) | Gevşek (loose coupling) |
Test edilebilirlik | Mock class gerekir | Protocol mock ile kolay |
Associated Types ve Generic Protocol'ler {#associated-types}
Protocol'lerde generic type parametresi tanımlamak için associatedtype kullanırız:
swift
1protocol Repository {2 associatedtype Entity3 associatedtype ID: Hashable4 5 func findById(_ id: ID) -> Entity?6 func findAll() -> [Entity]7 func save(_ entity: Entity) throws8 func delete(_ id: ID) throws9}10 11// Concrete implementation12class UserRepository: Repository {13 typealias Entity = User14 typealias ID = UUID15 16 private var storage: [UUID: User] = [:]17 18 func findById(_ id: UUID) -> User? {19 storage[id]20 }21 22 func findAll() -> [User] {23 Array(storage.values)24 }25 26 func save(_ entity: User) throws {27 storage[entity.id] = entity28 }29 30 func delete(_ id: UUID) throws {31 storage.removeValue(forKey: id)32 }33}Type Erasure: any vs some {#type-erasure}
Swift 5.7 ile any keyword'ü geldi ve existential type'lar açık hale getirildi:
swift
1// any - Existential type (runtime polimorfizm)2// Farklı concrete tipleri aynı değişkende tutabilirsin3var shapes: [any Shape] = [Circle(radius: 5), Square(side: 3)]4 5// some - Opaque type (compile-time polimorfizm)6// Derleyici concrete tipi bilir ama dışarıya gizler7func makeShape() -> some Shape {8 Circle(radius: 10) // Derleyici Circle olduğunu bilir9}any vs some Karşılaştırma
Özellik | any Protocol | some Protocol |
|---|---|---|
Tip bilgisi | Runtime | Compile-time |
Performans | Allocation + indirection | Sıfır overhead |
Heterojen koleksiyon | ✅ Evet | ❌ Hayır |
Self/associated type | ⚠️ Kısıtlı | ✅ Tam destek |
Kullanım alanı | Koleksiyonlar, değişken tipler | Fonksiyon dönüş, parametreler |
⚠️ Performans İpucu:anykullandığında Swift heap allocation yapar ve vtable lookup kullanır.someile derleyici doğrudan fonksiyon çağrısı yapabilir — 10x'e kadar hız farkı olabilir tight loop'larda.
Opaque Types ve some Keyword {#opaque-types}
swift
1// SwiftUI'da her gün kullanıyorsun:2var body: some View {3 VStack {4 Text("Hello")5 Image(systemName: "star")6 }7 // Gerçek tip: VStack<TupleView<(Text, Image)>>8 // some View ile gizleniyor - API kararlı kalıyor9}10 11// Primary associated types (Swift 5.7+)12func fetchItems() -> some Collection<Item> {13 // Array<Item> döner ama dışarıya sadece Collection<Item> görünür14 [Item(name: "A"), Item(name: "B")]15}Conditional Conformance {#conditional-conformance}
Bir generic tipin, type parametresi belirli bir protocol'e uyduğunda ek protocol'lere otomatik uyum sağlaması:
swift
1// Array, elemanları Equatable ise Equatable olur2extension Stack: Equatable where Element: Equatable {3 static func == (lhs: Stack, rhs: Stack) -> Bool {4 lhs.items == rhs.items5 }6}7 8// Array, elemanları Codable ise Codable olur9extension Stack: Codable where Element: Codable {}10 11// Hashable propagation12extension Stack: Hashable where Element: Hashable {13 func hash(into hasher: inout Hasher) {14 hasher.combine(items)15 }16}Generic Constraints ve where Clause {#generic-constraints}
swift
1// Basit constraint2func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {3 array.firstIndex(of: value)4}5 6// where clause ile karmaşık constraint'ler7func merge<C1: Collection, C2: Collection>(8 _ first: C1,9 _ second: C210) -> [C1.Element]11where C1.Element == C2.Element, C1.Element: Comparable {12 (Array(first) + Array(second)).sorted()13}14 15// Protocol extension'da where clause16extension Collection where Element: Numeric {17 var total: Element {18 reduce(0, +)19 }20 21 var average: Double where Element: BinaryInteger {22 guard !isEmpty else { return 0 }23 return Double(total) / Double(count)24 }25}26 27// Kullanım28let numbers = [1, 2, 3, 4, 5]29print(numbers.total) // 1530print(numbers.average) // 3.0Protocol Composition {#protocol-composition}
Birden fazla protocol'ü birleştirerek güçlü soyutlamalar oluşturabilirsin:
swift
1// Tek tek protocol'ler2protocol Identifiable { var id: UUID { get } }3protocol Timestamped { var createdAt: Date { get }; var updatedAt: Date { get } }4protocol SoftDeletable { var isDeleted: Bool { get set } }5 6// Composition ile birleştir7typealias BaseEntity = Identifiable & Timestamped & SoftDeletable & Codable8 9// Fonksiyon parametresinde composition10func save(_ entity: some Identifiable & Codable) throws {11 let data = try JSONEncoder().encode(entity)12 try data.write(to: fileURL(for: entity.id))13}14 15// Gerçek dünya: Service katmanı16protocol CRUDService<Entity> {17 associatedtype Entity: BaseEntity18 func create(_ entity: Entity) async throws -> Entity19 func read(id: UUID) async throws -> Entity20 func update(_ entity: Entity) async throws -> Entity21 func delete(id: UUID) async throws22}Gerçek Dünya Kalıpları {#gercek-dunya}
1. Generic Network Layer
swift
1protocol APIEndpoint {2 associatedtype Response: Decodable3 var path: String { get }4 var method: HTTPMethod { get }5 var headers: [String: String] { get }6}7 8class APIClient {9 func request<E: APIEndpoint>(_ endpoint: E) async throws -> E.Response {10 let url = baseURL.appendingPathComponent(endpoint.path)11 var request = URLRequest(url: url)12 request.httpMethod = endpoint.method.rawValue13 endpoint.headers.forEach { request.setValue($1, forHTTPHeaderField: $0) }14 15 let (data, response) = try await URLSession.shared.data(for: request)16 guard let httpResponse = response as? HTTPURLResponse,17 200..<300 ~= httpResponse.statusCode else {18 throw APIError.invalidResponse19 }20 return try JSONDecoder().decode(E.Response.self, from: data)21 }22}2. Type-Safe Builder Pattern
swift
1@resultBuilder2struct QueryBuilder {3 static func buildBlock(_ components: QueryComponent...) -> [QueryComponent] {4 components5 }6}7 8protocol QueryComponent {9 var sql: String { get }10}11 12struct Select: QueryComponent {13 let columns: [String]14 var sql: String { "SELECT \(columns.joined(separator: ", "))" }15}16 17struct Where: QueryComponent {18 let condition: String19 var sql: String { "WHERE \(condition)" }20}21 22func query(@QueryBuilder _ build: () -> [QueryComponent]) -> String {23 build().map(\.sql).joined(separator: " ")24}25 26// Kullanım27let sql = query {28 Select(columns: ["name", "email"])29 Where(condition: "active = true")30}31// "SELECT name, email WHERE active = true"Production Best Practices {#best-practices}
- `some` tercih et —
anysadece heterojen koleksiyonlar için - Protocol extension ile varsayılan implementasyon sağla
- Associated type ile generic protocol'ler oluştur
- Conditional conformance ile otomatik protocol uyumu
- where clause ile constraint'leri dar tut — gereksiz genişletme yapma
- Protocol composition ile küçük, odaklı protocol'ler birleştir
- PAT (Protocol with Associated Types) koleksiyonda kullanma —
anyveya type erasure gerekir
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var: **Kaynaklar:** - [WWDC15: Protocol-Oriented Programming](https://developer.apple.com/videos/play/wwdc2015/408/) - [Swift Generics Documentation](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/) - [SE-0335: Introduce existential any](https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md) - [SE-0346: Lightweight same-type requirements for primary associated types](https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md)

