Tüm Yazılar
KategoriSwift
Okuma Süresi
22 dk
Yayın Tarihi
...
Kelime Sayısı
1.784kelime

Kahveni hazırla - bu içerikli bir makale!

Swift'in tip sistemi, generics ve protocol-oriented programming paradigmasını derinlemesine öğrenin. Associated types, opaque types, type erasure ve gerçek dünya kalıpları.

Swift Generics ve Protocol-Oriented Programming: Tam Rehber

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

  1. Generic Fonksiyonlar ve Tipler
  2. Protocol ile Soyutlama
  3. Associated Types ve Generic Protocol'ler
  4. Type Erasure: any vs some
  5. Opaque Types ve some Keyword
  6. Conditional Conformance
  7. Generic Constraints ve where Clause
  8. Protocol Composition
  9. Gerçek Dünya Kalıpları
  10. 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 ihlali
2func swapInts(_ a: inout Int, _ b: inout Int) {
3 let temp = a; a = b; b = temp
4}
5func swapStrings(_ a: inout String, _ b: inout String) {
6 let temp = a; a = b; b = temp
7}
8 
9// ✅ Generic fonksiyon - tek seferlik
10func swap<T>(_ a: inout T, _ b: inout T) {
11 let temp = a; a = b; b = temp
12}

Generic Tipler

swift
1// Generic Stack implementasyonu
2struct 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 @discardableResult
14 mutating func pop() -> Element? {
15 items.popLast()
16 }
17}
18 
19// Kullanım - derleyici tipi çıkarır
20var 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ımla
2protocol 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 implementasyon
10extension Cacheable {
11 var cacheExpiry: TimeInterval { 3600 } // 1 saat varsayılan
12 
13 func serialize() -> Data? {
14 try? JSONEncoder().encode(self as! Encodable)
15 }
16}
17 
18// Sadece fark eden kısımları override et
19struct UserProfile: Cacheable, Codable {
20 let id: UUID
21 let name: String
22 let email: String
23 
24 var cacheKey: String { "user_\(id.uuidString)" }
25 var cacheExpiry: TimeInterval { 7200 } // 2 saat
26 
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 Entity
3 associatedtype ID: Hashable
4 
5 func findById(_ id: ID) -> Entity?
6 func findAll() -> [Entity]
7 func save(_ entity: Entity) throws
8 func delete(_ id: ID) throws
9}
10 
11// Concrete implementation
12class UserRepository: Repository {
13 typealias Entity = User
14 typealias ID = UUID
15 
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] = entity
28 }
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 tutabilirsin
3var 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 gizler
7func makeShape() -> some Shape {
8 Circle(radius: 10) // Derleyici Circle olduğunu bilir
9}

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: any kullandığında Swift heap allocation yapar ve vtable lookup kullanır. some ile 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ıyor
9}
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ür
14 [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 olur
2extension Stack: Equatable where Element: Equatable {
3 static func == (lhs: Stack, rhs: Stack) -> Bool {
4 lhs.items == rhs.items
5 }
6}
7 
8// Array, elemanları Codable ise Codable olur
9extension Stack: Codable where Element: Codable {}
10 
11// Hashable propagation
12extension 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 constraint
2func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
3 array.firstIndex(of: value)
4}
5 
6// where clause ile karmaşık constraint'ler
7func merge<C1: Collection, C2: Collection>(
8 _ first: C1,
9 _ second: C2
10) -> [C1.Element]
11where C1.Element == C2.Element, C1.Element: Comparable {
12 (Array(first) + Array(second)).sorted()
13}
14 
15// Protocol extension'da where clause
16extension 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ım
28let numbers = [1, 2, 3, 4, 5]
29print(numbers.total) // 15
30print(numbers.average) // 3.0

Protocol Composition {#protocol-composition}

Birden fazla protocol'ü birleştirerek güçlü soyutlamalar oluşturabilirsin:

swift
1// Tek tek protocol'ler
2protocol 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ştir
7typealias BaseEntity = Identifiable & Timestamped & SoftDeletable & Codable
8 
9// Fonksiyon parametresinde composition
10func 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: BaseEntity
18 func create(_ entity: Entity) async throws -> Entity
19 func read(id: UUID) async throws -> Entity
20 func update(_ entity: Entity) async throws -> Entity
21 func delete(id: UUID) async throws
22}

Gerçek Dünya Kalıpları {#gercek-dunya}

1. Generic Network Layer

swift
1protocol APIEndpoint {
2 associatedtype Response: Decodable
3 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.rawValue
13 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.invalidResponse
19 }
20 return try JSONDecoder().decode(E.Response.self, from: data)
21 }
22}

2. Type-Safe Builder Pattern

swift
1@resultBuilder
2struct QueryBuilder {
3 static func buildBlock(_ components: QueryComponent...) -> [QueryComponent] {
4 components
5 }
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: String
19 var sql: String { "WHERE \(condition)" }
20}
21 
22func query(@QueryBuilder _ build: () -> [QueryComponent]) -> String {
23 build().map(\.sql).joined(separator: " ")
24}
25 
26// Kullanım
27let 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}

  1. `some` tercih etany sadece heterojen koleksiyonlar için
  2. Protocol extension ile varsayılan implementasyon sağla
  3. Associated type ile generic protocol'ler oluştur
  4. Conditional conformance ile otomatik protocol uyumu
  5. where clause ile constraint'leri dar tut — gereksiz genişletme yapma
  6. Protocol composition ile küçük, odaklı protocol'ler birleştir
  7. PAT (Protocol with Associated Types) koleksiyonda kullanma — any veya 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)

Etiketler

#swift#generics#protocol-oriented#pop#type-system#advanced
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz