[{"content":"Proje Hakkında Water Well, günlük su tüketimini takip etmeyi alışkanlığa dönüştürmek için tasarladığım iOS uygulaması. Su, kahve, çay ve daha fazlasını tek dokunuşla kaydedebilirsiniz — uygulama her içeceğin gerçek hidrasyon değerini hesaplayarak doğru takip sağlar.\nÖne Çıkan Özellikler Akıllı Takip — Her içeceğin hidrasyon katsayısı ayrı hesaplanır; kahve su ile aynı etkiyi yaratmaz Kişisel Avatar — Günlük hedefe ulaştıkça avatar gerçek zamanlı olarak su ile dolar Akıllı Hatırlatıcılar — Yalnızca sustuğunuzda devreye girer; 5 dakika ile 2 saat arası özelleştirilebilir Kişiselleştirilmiş Hedef — Kilo, aktivite seviyesi ve hava durumuna göre hesaplanan günlük hedef İlerleme Analitiği — Gün, hafta, ay bazında geçmiş ve en uzun seriler Geniş İçecek Kütüphanesi — Su, kahve, çay, süt, meyve suyu, latte, cappuccino, sparkling water ve özel içecekler İstatistikler Aktif Kullanıcı 50.000+ Kayıt Edilen Bardak 2.000.000+ App Store Puanı 4.8 ★ 1. haftada hidrasyon artışı Kullanıcıların %48\u0026rsquo;i Teknoloji Stack Katman Teknoloji UI SwiftUI Platform iPhone \u0026amp; iPad Dağıtım App Store Bağlantılar Website: waterwell.antalyasoftware.com App Store: Water Well – Daily Hydration Tracker ","permalink":"https://kemalturk.com/projects/water-well/","summary":"\u003ch2 id=\"proje-hakkında\"\u003eProje Hakkında\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eWater Well\u003c/strong\u003e, günlük su tüketimini takip etmeyi alışkanlığa dönüştürmek için tasarladığım iOS uygulaması. Su, kahve, çay ve daha fazlasını tek dokunuşla kaydedebilirsiniz — uygulama her içeceğin gerçek hidrasyon değerini hesaplayarak doğru takip sağlar.\u003c/p\u003e\n\u003ch2 id=\"öne-çıkan-özellikler\"\u003eÖne Çıkan Özellikler\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eAkıllı Takip\u003c/strong\u003e — Her içeceğin hidrasyon katsayısı ayrı hesaplanır; kahve su ile aynı etkiyi yaratmaz\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eKişisel Avatar\u003c/strong\u003e — Günlük hedefe ulaştıkça avatar gerçek zamanlı olarak su ile dolar\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAkıllı Hatırlatıcılar\u003c/strong\u003e — Yalnızca sustuğunuzda devreye girer; 5 dakika ile 2 saat arası özelleştirilebilir\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eKişiselleştirilmiş Hedef\u003c/strong\u003e — Kilo, aktivite seviyesi ve hava durumuna göre hesaplanan günlük hedef\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eİlerleme Analitiği\u003c/strong\u003e — Gün, hafta, ay bazında geçmiş ve en uzun seriler\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eGeniş İçecek Kütüphanesi\u003c/strong\u003e — Su, kahve, çay, süt, meyve suyu, latte, cappuccino, sparkling water ve özel içecekler\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"istatistikler\"\u003eİstatistikler\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eAktif Kullanıcı\u003c/td\u003e\n          \u003ctd\u003e50.000+\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eKayıt Edilen Bardak\u003c/td\u003e\n          \u003ctd\u003e2.000.000+\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eApp Store Puanı\u003c/td\u003e\n          \u003ctd\u003e4.8 ★\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1. haftada hidrasyon artışı\u003c/td\u003e\n          \u003ctd\u003eKullanıcıların %48\u0026rsquo;i\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"teknoloji-stack\"\u003eTeknoloji Stack\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eKatman\u003c/th\u003e\n          \u003cth\u003eTeknoloji\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eUI\u003c/td\u003e\n          \u003ctd\u003eSwiftUI\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePlatform\u003c/td\u003e\n          \u003ctd\u003eiPhone \u0026amp; iPad\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDağıtım\u003c/td\u003e\n          \u003ctd\u003eApp Store\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"bağlantılar\"\u003eBağlantılar\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eWebsite:\u003c/strong\u003e \u003ca href=\"https://waterwell.antalyasoftware.com\"\u003ewaterwell.antalyasoftware.com\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eApp Store:\u003c/strong\u003e \u003ca href=\"https://apps.apple.com/app/id6476093645\"\u003eWater Well – Daily Hydration Tracker\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","title":"Water Well — Daily Hydration Tracker"},{"content":"Proje Hakkında Mirra, yapay zeka ile sıradan fotoğrafları profesyonel kaliteye taşıyan native iOS uygulaması. Herhangi bir beceri gerektirmeden, saniyeler içinde stüdyo kalitesinde sonuçlar üretir.\nÖzellikler AI Ofis Portreleri — Bir selfie\u0026rsquo;den LinkedIn, özgeçmiş veya iş profili için hazır profesyonel fotoğraf üretir (en popüler özellik) Tek Dokunuşla Arka Plan Kaldırma — Konuyu anında ayırır, arka planı siler Akıllı İyileştirme — Işık, keskinlik ve renk dengesini otomatik optimize eder AI Filtreler — Basit preset\u0026rsquo;lerin çok ötesine geçen, sürekli büyüyen modern efekt kütüphanesi Kişisel Galeri — Düzenlenen tüm fotoğraflar tek bir galeride toplanır Teknik Detaylar Boyut 26 MB Minimum iOS iOS 17.6 Platform iPhone \u0026amp; iPad İndirme Ücretsiz Teknoloji Stack Katman Teknoloji UI SwiftUI AI / ML Core ML Platform iPhone \u0026amp; iPad Dağıtım App Store Bağlantılar Website: mirra.antalyasoftware.com App Store: Mirra – AI Photo Editor ","permalink":"https://kemalturk.com/projects/mirra-ai-photo-editor/","summary":"\u003ch2 id=\"proje-hakkında\"\u003eProje Hakkında\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eMirra\u003c/strong\u003e, yapay zeka ile sıradan fotoğrafları profesyonel kaliteye taşıyan native iOS uygulaması. Herhangi bir beceri gerektirmeden, saniyeler içinde stüdyo kalitesinde sonuçlar üretir.\u003c/p\u003e\n\u003ch2 id=\"özellikler\"\u003eÖzellikler\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eAI Ofis Portreleri\u003c/strong\u003e — Bir selfie\u0026rsquo;den LinkedIn, özgeçmiş veya iş profili için hazır profesyonel fotoğraf üretir (en popüler özellik)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTek Dokunuşla Arka Plan Kaldırma\u003c/strong\u003e — Konuyu anında ayırır, arka planı siler\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAkıllı İyileştirme\u003c/strong\u003e — Işık, keskinlik ve renk dengesini otomatik optimize eder\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAI Filtreler\u003c/strong\u003e — Basit preset\u0026rsquo;lerin çok ötesine geçen, sürekli büyüyen modern efekt kütüphanesi\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eKişisel Galeri\u003c/strong\u003e — Düzenlenen tüm fotoğraflar tek bir galeride toplanır\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"teknik-detaylar\"\u003eTeknik Detaylar\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBoyut\u003c/td\u003e\n          \u003ctd\u003e26 MB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMinimum iOS\u003c/td\u003e\n          \u003ctd\u003eiOS 17.6\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePlatform\u003c/td\u003e\n          \u003ctd\u003eiPhone \u0026amp; iPad\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eİndirme\u003c/td\u003e\n          \u003ctd\u003eÜcretsiz\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"teknoloji-stack\"\u003eTeknoloji Stack\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eKatman\u003c/th\u003e\n          \u003cth\u003eTeknoloji\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eUI\u003c/td\u003e\n          \u003ctd\u003eSwiftUI\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eAI / ML\u003c/td\u003e\n          \u003ctd\u003eCore ML\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePlatform\u003c/td\u003e\n          \u003ctd\u003eiPhone \u0026amp; iPad\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDağıtım\u003c/td\u003e\n          \u003ctd\u003eApp Store\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"bağlantılar\"\u003eBağlantılar\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eWebsite:\u003c/strong\u003e \u003ca href=\"https://mirra.antalyasoftware.com\"\u003emirra.antalyasoftware.com\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eApp Store:\u003c/strong\u003e \u003ca href=\"https://apps.apple.com/tr/app/mirra-ai-photo-editor/id6755906677\"\u003eMirra – AI Photo Editor\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","title":"Mirra — AI Photo Editor"},{"content":"Giriş Swift 5.5 ile birlikte gelen async/await, iOS geliştirme dünyasında callback ve completion handler cehenneminden kurtulmanın en temiz yolu haline geldi. Bu yazıda gerçek dünya senaryolarıyla Swift Concurrency\u0026rsquo;yi ele alacağız.\nNeden async/await? Klasik completion handler yaklaşımının sorunları:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // ❌ Eski yöntem — callback cehennemi func fetchUser(id: String, completion: @escaping (Result\u0026lt;User, Error\u0026gt;) -\u0026gt; Void) { networkService.get(\u0026#34;/users/\\(id)\u0026#34;) { result in switch result { case .success(let data): do { let user = try JSONDecoder().decode(User.self, from: data) self.fetchPosts(for: user) { postsResult in // Bir kat daha içeriye... } } catch { completion(.failure(error)) } case .failure(let error): completion(.failure(error)) } } } 1 2 3 4 5 6 7 8 9 10 11 // ✅ async/await ile temiz kod func fetchUser(id: String) async throws -\u0026gt; User { let data = try await networkService.get(\u0026#34;/users/\\(id)\u0026#34;) return try JSONDecoder().decode(User.self, from: data) } func loadUserWithPosts(id: String) async throws -\u0026gt; (User, [Post]) { let user = try await fetchUser(id: id) let posts = try await fetchPosts(for: user) return (user, posts) } Structured Concurrency ile Paralel İşlemler İki bağımsız network isteğini paralel yapmak için async let kullanın:\n1 2 3 4 5 6 7 8 9 10 11 12 func loadDashboard() async throws -\u0026gt; Dashboard { async let user = fetchUser(id: currentUserID) async let notifications = fetchNotifications() async let feed = fetchFeed() // Üçü paralel çalışır, hepsi bitince devam eder return Dashboard( user: try await user, notifications: try await notifications, feed: try await feed ) } TaskGroup ile Dinamik Paralellik 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func fetchAllPosts(ids: [String]) async throws -\u0026gt; [Post] { try await withThrowingTaskGroup(of: Post.self) { group in for id in ids { group.addTask { try await self.fetchPost(id: id) } } var posts: [Post] = [] for try await post in group { posts.append(post) } return posts } } Actor ile Thread Safety Shared mutable state\u0026rsquo;i güvenli hale getirmek için actor kullanın:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 actor UserCache { private var cache: [String: User] = [:] func user(for id: String) -\u0026gt; User? { cache[id] } func store(_ user: User, for id: String) { cache[id] = user } } // Kullanım let cache = UserCache() await cache.store(user, for: user.id) let cachedUser = await cache.user(for: id) MainActor ile UI Güncellemeleri 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @MainActor class ProfileViewModel: ObservableObject { @Published var user: User? @Published var isLoading = false func loadProfile() async { isLoading = true defer { isLoading = false } do { user = try await userService.fetchCurrentUser() } catch { // Hata yönetimi } } } Sonuç Swift Concurrency, iOS kodunu hem okunabilir hem de hata yapmaya karşı dayanıklı hale getiriyor. async/await, actor ve TaskGroup üçlüsünü kullanarak modern, güvenli ve performanslı uygulamalar yazabilirsiniz.\nBir sonraki yazıda AsyncStream ve AsyncSequence konularına değineceğiz.\n","permalink":"https://kemalturk.com/blog/swift-concurrency-async-await/","summary":"\u003ch2 id=\"giriş\"\u003eGiriş\u003c/h2\u003e\n\u003cp\u003eSwift 5.5 ile birlikte gelen \u003ccode\u003easync/await\u003c/code\u003e, iOS geliştirme dünyasında callback ve completion handler cehenneminden kurtulmanın en temiz yolu haline geldi. Bu yazıda gerçek dünya senaryolarıyla Swift Concurrency\u0026rsquo;yi ele alacağız.\u003c/p\u003e\n\u003ch2 id=\"neden-asyncawait\"\u003eNeden async/await?\u003c/h2\u003e\n\u003cp\u003eKlasik completion handler yaklaşımının sorunları:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e 1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e 2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e 3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-4\"\u003e 4\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-5\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-5\"\u003e 5\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-6\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-6\"\u003e 6\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-7\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-7\"\u003e 7\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-8\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-8\"\u003e 8\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-9\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-9\"\u003e 9\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-10\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-10\"\u003e10\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-11\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-11\"\u003e11\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-12\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-12\"\u003e12\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-13\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-13\"\u003e13\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-14\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-14\"\u003e14\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-15\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-15\"\u003e15\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-16\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-16\"\u003e16\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-17\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-17\"\u003e17\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-18\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-18\"\u003e18\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// ❌ Eski yöntem — callback cehennemi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003efetchUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e@\u003c/span\u003e\u003cspan class=\"n\"\u003eescaping\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eResult\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eError\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;)\u003c/span\u003e \u003cspan class=\"p\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003eVoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003enetworkService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kr\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/users/\u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eswitch\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esuccess\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003euser\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"n\"\u003eJSONDecoder\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efrom\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efetchPosts\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003epostsResult\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"c1\"\u003e// Bir kat daha içeriye...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003ecatch\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003efailure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efailure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ecompletion\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003efailure\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e 1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-2\"\u003e 2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-3\"\u003e 3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-4\"\u003e 4\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-5\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-5\"\u003e 5\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-6\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-6\"\u003e 6\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-7\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-7\"\u003e 7\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-8\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-8\"\u003e 8\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-9\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-9\"\u003e 9\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-10\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-10\"\u003e10\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-11\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-11\"\u003e11\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// ✅ async/await ile temiz kod\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003efetchUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003easync\u003c/span\u003e \u003cspan class=\"kr\"\u003ethrows\u003c/span\u003e \u003cspan class=\"p\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eUser\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003edata\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"n\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003enetworkService\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kr\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/users/\u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"n\"\u003eJSONDecoder\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kc\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003efrom\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e \u003cspan class=\"nf\"\u003eloadUserWithPosts\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"n\"\u003easync\u003c/span\u003e \u003cspan class=\"kr\"\u003ethrows\u003c/span\u003e \u003cspan class=\"p\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eUser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ePost\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003euser\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"n\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003efetchUser\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003eposts\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"n\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003efetchPosts\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eposts\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"structured-concurrency-ile-paralel-işlemler\"\u003eStructured Concurrency ile Paralel İşlemler\u003c/h2\u003e\n\u003cp\u003eİki bağımsız network isteğini paralel yapmak için \u003ccode\u003easync let\u003c/code\u003e kullanın:\u003c/p\u003e","title":"Swift Concurrency: async/await ile Modern Eşzamanlılık"},{"content":"Proje Hakkında Tunnel Surf, iPhone ve iPad kullanıcıları için geliştirdiğim native iOS VPN uygulaması. Kullanıcıları çevrimiçi takipten, veri sızıntısından ve güvensiz ağlardan korurken tek dokunuşla bağlantı deneyimi sunuyor.\nÖzellikler AES-256 Şifreleme — Kafe, havalimanı ve otel gibi herkese açık ağlarda tüm trafiği şifreler 50+ Sunucu Lokasyonu — Düzinelerce ülkede düşük gecikmeli sunucular Sıfır Log Politikası — Kullanıcı aktivitesi hiçbir şekilde kaydedilmez Kill Switch — VPN bağlantısı kesilirse internet otomatik olarak duraklatılır, gerçek IP hiç açığa çıkmaz Tek Dokunuşla Bağlantı — Native iOS tasarımı, sade arayüz, anında bağlantı Otomatik Sunucu Seçimi — En hızlı sunucu otomatik olarak belirlenir İstatistikler Sunucu Lokasyonu 50+ Şifreleme AES-256 Log Politikası Sıfır Minimum iOS iOS 15 Teknoloji Stack Katman Teknoloji UI SwiftUI Backend Golang VPN NetworkExtension Platform iPhone \u0026amp; iPad Dağıtım App Store Bağlantılar Website: tunnelsurfvpn.com App Store: Tunnel Surf – VPN Total Privacy ","permalink":"https://kemalturk.com/projects/tunnel-surf-vpn/","summary":"\u003ch2 id=\"proje-hakkında\"\u003eProje Hakkında\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eTunnel Surf\u003c/strong\u003e, iPhone ve iPad kullanıcıları için geliştirdiğim native iOS VPN uygulaması. Kullanıcıları çevrimiçi takipten, veri sızıntısından ve güvensiz ağlardan korurken tek dokunuşla bağlantı deneyimi sunuyor.\u003c/p\u003e\n\u003ch2 id=\"özellikler\"\u003eÖzellikler\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eAES-256 Şifreleme\u003c/strong\u003e — Kafe, havalimanı ve otel gibi herkese açık ağlarda tüm trafiği şifreler\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e50+ Sunucu Lokasyonu\u003c/strong\u003e — Düzinelerce ülkede düşük gecikmeli sunucular\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSıfır Log Politikası\u003c/strong\u003e — Kullanıcı aktivitesi hiçbir şekilde kaydedilmez\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eKill Switch\u003c/strong\u003e — VPN bağlantısı kesilirse internet otomatik olarak duraklatılır, gerçek IP hiç açığa çıkmaz\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTek Dokunuşla Bağlantı\u003c/strong\u003e — Native iOS tasarımı, sade arayüz, anında bağlantı\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eOtomatik Sunucu Seçimi\u003c/strong\u003e — En hızlı sunucu otomatik olarak belirlenir\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"istatistikler\"\u003eİstatistikler\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSunucu Lokasyonu\u003c/td\u003e\n          \u003ctd\u003e50+\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eŞifreleme\u003c/td\u003e\n          \u003ctd\u003eAES-256\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eLog Politikası\u003c/td\u003e\n          \u003ctd\u003eSıfır\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMinimum iOS\u003c/td\u003e\n          \u003ctd\u003eiOS 15\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"teknoloji-stack\"\u003eTeknoloji Stack\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eKatman\u003c/th\u003e\n          \u003cth\u003eTeknoloji\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eUI\u003c/td\u003e\n          \u003ctd\u003eSwiftUI\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBackend\u003c/td\u003e\n          \u003ctd\u003eGolang\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eVPN\u003c/td\u003e\n          \u003ctd\u003eNetworkExtension\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePlatform\u003c/td\u003e\n          \u003ctd\u003eiPhone \u0026amp; iPad\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDağıtım\u003c/td\u003e\n          \u003ctd\u003eApp Store\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"bağlantılar\"\u003eBağlantılar\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eWebsite:\u003c/strong\u003e \u003ca href=\"https://tunnelsurfvpn.com\"\u003etunnelsurfvpn.com\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eApp Store:\u003c/strong\u003e \u003ca href=\"https://apps.apple.com/tr/app/tunnel-surf-vpn-total-privacy/id1636554440\"\u003eTunnel Surf – VPN Total Privacy\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","title":"Tunnel Surf — VPN Total Privacy"},{"content":"TCA Nedir? The Composable Architecture (TCA), Brandon Williams ve Stephen Celis tarafından geliştirilen, SwiftUI için tasarlanmış bir uygulama mimarisidir. Unidirectional data flow prensibine dayanır ve test edilebilirliği birinci sınıf vatandaş olarak ele alır.\nTemel Kavramlar TCA\u0026rsquo;nın beş temel bileşeni vardır:\nBileşen Görev State Ekranın tüm verisi Action Gerçekleşen olaylar Reducer State + Action → yeni State Store Runtime: state tutar, action gönderir Effect Yan etkiler (network, timer vb.) Basit Bir Örnek: Sayaç 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import ComposableArchitecture import SwiftUI // 1. State struct CounterFeature: Reducer { struct State: Equatable { var count = 0 var isLoading = false } // 2. Action enum Action { case incrementTapped case decrementTapped case factButtonTapped case factResponse(String) } // 3. Reducer var body: some ReducerOf\u0026lt;Self\u0026gt; { Reduce { state, action in switch action { case .incrementTapped: state.count += 1 return .none case .decrementTapped: state.count -= 1 return .none case .factButtonTapped: state.isLoading = true return .run { [count = state.count] send in let fact = try await fetchFact(for: count) await send(.factResponse(fact)) } case let .factResponse(fact): state.isLoading = false // fact\u0026#39;ı state\u0026#39;e kaydet return .none } } } } View Katmanı 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 struct CounterView: View { let store: StoreOf\u0026lt;CounterFeature\u0026gt; var body: some View { WithViewStore(store, observe: { $0 }) { viewStore in VStack(spacing: 20) { Text(\u0026#34;\\(viewStore.count)\u0026#34;) .font(.largeTitle.bold()) HStack { Button(\u0026#34;-\u0026#34;) { viewStore.send(.decrementTapped) } .buttonStyle(.bordered) Button(\u0026#34;+\u0026#34;) { viewStore.send(.incrementTapped) } .buttonStyle(.borderedProminent) } if viewStore.isLoading { ProgressView() } else { Button(\u0026#34;Fakt Al\u0026#34;) { viewStore.send(.factButtonTapped) } } } } } } Test Yazımı TCA\u0026rsquo;nın en güçlü yanı test edilebilirlik:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @MainActor final class CounterFeatureTests: XCTestCase { func testIncrement() async { let store = TestStore(initialState: CounterFeature.State()) { CounterFeature() } await store.send(.incrementTapped) { $0.count = 1 } await store.send(.incrementTapped) { $0.count = 2 } await store.send(.decrementTapped) { $0.count = 1 } } } Composability: Feature\u0026rsquo;ları Birleştirme TCA\u0026rsquo;nın adındaki \u0026ldquo;Composable\u0026rdquo; özelliği, küçük feature\u0026rsquo;ları büyük uygulamalarda birleştirmeye yarar:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 struct AppFeature: Reducer { struct State { var tab1 = CounterFeature.State() var tab2 = ProfileFeature.State() } enum Action { case tab1(CounterFeature.Action) case tab2(ProfileFeature.Action) } var body: some ReducerOf\u0026lt;Self\u0026gt; { Scope(state: \\.tab1, action: /Action.tab1) { CounterFeature() } Scope(state: \\.tab2, action: /Action.tab2) { ProfileFeature() } } } Sonuç TCA, büyük ölçekli SwiftUI uygulamaları için güçlü bir mimari sunuyor. Test edilebilirlik, öngörülebilirlik ve bileşen yeniden kullanımı açısından rakipsiz. Öğrenme eğrisi dik olsa da, orta ve büyük projelerde kesinlikle değer.\n","permalink":"https://kemalturk.com/blog/swiftui-tca-mimarisi/","summary":"\u003ch2 id=\"tca-nedir\"\u003eTCA Nedir?\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eThe Composable Architecture (TCA)\u003c/strong\u003e, Brandon Williams ve Stephen Celis tarafından geliştirilen, SwiftUI için tasarlanmış bir uygulama mimarisidir. Unidirectional data flow prensibine dayanır ve test edilebilirliği birinci sınıf vatandaş olarak ele alır.\u003c/p\u003e\n\u003ch2 id=\"temel-kavramlar\"\u003eTemel Kavramlar\u003c/h2\u003e\n\u003cp\u003eTCA\u0026rsquo;nın beş temel bileşeni vardır:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eBileşen\u003c/th\u003e\n          \u003cth\u003eGörev\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eState\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eEkranın tüm verisi\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eAction\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eGerçekleşen olaylar\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eReducer\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eState + Action → yeni State\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eStore\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eRuntime: state tutar, action gönderir\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eEffect\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eYan etkiler (network, timer vb.)\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"basit-bir-örnek-sayaç\"\u003eBasit Bir Örnek: Sayaç\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e 1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e 2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e 3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-4\"\u003e 4\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-5\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-5\"\u003e 5\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-6\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-6\"\u003e 6\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-7\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-7\"\u003e 7\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-8\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-8\"\u003e 8\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-9\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-9\"\u003e 9\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-10\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-10\"\u003e10\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-11\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-11\"\u003e11\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-12\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-12\"\u003e12\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-13\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-13\"\u003e13\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-14\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-14\"\u003e14\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-15\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-15\"\u003e15\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-16\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-16\"\u003e16\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-17\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-17\"\u003e17\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-18\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-18\"\u003e18\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-19\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-19\"\u003e19\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-20\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-20\"\u003e20\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-21\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-21\"\u003e21\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-22\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-22\"\u003e22\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-23\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-23\"\u003e23\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-24\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-24\"\u003e24\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-25\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-25\"\u003e25\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-26\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-26\"\u003e26\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-27\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-27\"\u003e27\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-28\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-28\"\u003e28\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-29\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-29\"\u003e29\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-30\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-30\"\u003e30\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-31\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-31\"\u003e31\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-32\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-32\"\u003e32\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-33\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-33\"\u003e33\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-34\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-34\"\u003e34\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-35\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-35\"\u003e35\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-36\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-36\"\u003e36\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-37\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-37\"\u003e37\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-38\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-38\"\u003e38\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-39\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-39\"\u003e39\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-40\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-40\"\u003e40\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-41\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-41\"\u003e41\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-42\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-42\"\u003e42\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-43\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-43\"\u003e43\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-44\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-44\"\u003e44\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-45\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-45\"\u003e45\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"nc\"\u003eComposableArchitecture\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003eimport\u003c/span\u003e \u003cspan class=\"nc\"\u003eSwiftUI\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// 1. State\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"nc\"\u003eCounterFeature\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eReducer\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"nc\"\u003eState\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eEquatable\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ecount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003eisLoading\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 2. Action\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003eenum\u003c/span\u003e \u003cspan class=\"nc\"\u003eAction\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"n\"\u003eincrementTapped\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"n\"\u003edecrementTapped\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"n\"\u003efactButtonTapped\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"n\"\u003efactResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 3. Reducer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003esome\u003c/span\u003e \u003cspan class=\"n\"\u003eReducerOf\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kc\"\u003eSelf\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eReduce\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eaction\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eswitch\u003c/span\u003e \u003cspan class=\"n\"\u003eaction\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eincrementTapped\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003ecount\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kr\"\u003enone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edecrementTapped\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003ecount\u003c/span\u003e \u003cspan class=\"o\"\u003e-=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kr\"\u003enone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efactButtonTapped\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eisLoading\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erun\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"bp\"\u003ecount\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003ecount\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"n\"\u003esend\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003efact\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"n\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003efetchFact\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"bp\"\u003ecount\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003efactResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efact\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efactResponse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efact\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003estate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eisLoading\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"c1\"\u003e// fact\u0026#39;ı state\u0026#39;e kaydet\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"kr\"\u003enone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"view-katmanı\"\u003eView Katmanı\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e 1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-2\"\u003e 2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-3\"\u003e 3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-4\"\u003e 4\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-5\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-5\"\u003e 5\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-6\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-6\"\u003e 6\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-7\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-7\"\u003e 7\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-8\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-8\"\u003e 8\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-9\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-9\"\u003e 9\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-10\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-10\"\u003e10\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-11\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-11\"\u003e11\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-12\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-12\"\u003e12\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-13\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-13\"\u003e13\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-14\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-14\"\u003e14\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-15\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-15\"\u003e15\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-16\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-16\"\u003e16\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-17\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-17\"\u003e17\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-18\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-18\"\u003e18\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-19\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-19\"\u003e19\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-20\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-20\"\u003e20\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-21\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-21\"\u003e21\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-22\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-22\"\u003e22\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-23\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-23\"\u003e23\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-24\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-24\"\u003e24\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-25\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-25\"\u003e25\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-26\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-26\"\u003e26\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-27\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-27\"\u003e27\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-28\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-28\"\u003e28\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-29\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-29\"\u003e29\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-30\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-30\"\u003e30\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-31\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-31\"\u003e31\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-32\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-32\"\u003e32\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-swift\" data-lang=\"swift\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003estruct\u003c/span\u003e \u003cspan class=\"nc\"\u003eCounterView\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eView\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nv\"\u003estore\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eStoreOf\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eCounterFeature\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nv\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003esome\u003c/span\u003e \u003cspan class=\"n\"\u003eView\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eWithViewStore\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estore\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eobserve\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nv\"\u003e$0\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eviewStore\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eVStack\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003espacing\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eText\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e\\(\u003c/span\u003e\u003cspan class=\"n\"\u003eviewStore\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"bp\"\u003ecount\u003c/span\u003e\u003cspan class=\"si\"\u003e)\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efont\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003elargeTitle\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebold\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eHStack\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eButton\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;-\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        \u003cspan class=\"n\"\u003eviewStore\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003edecrementTapped\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebuttonStyle\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003ebordered\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eButton\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;+\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        \u003cspan class=\"n\"\u003eviewStore\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003eincrementTapped\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebuttonStyle\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003eborderedProminent\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eviewStore\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eisLoading\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eProgressView\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eButton\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Fakt Al\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        \u003cspan class=\"n\"\u003eviewStore\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(.\u003c/span\u003e\u003cspan class=\"n\"\u003efactButtonTapped\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"test-yazımı\"\u003eTest Yazımı\u003c/h2\u003e\n\u003cp\u003eTCA\u0026rsquo;nın en güçlü yanı test edilebilirlik:\u003c/p\u003e","title":"SwiftUI'da TCA (The Composable Architecture) ile Ölçeklenebilir Mimari"},{"content":"Giriş Kullanıcılar düşük frame rate\u0026rsquo;e, yavaş açılış sürelerine ve yüksek bellek kullanımına karşı acımasızdır. Bu rehberde Xcode Instruments ile performans sorunlarını sistematik olarak nasıl bulacağınızı ve düzelteceğinizi göreceğiz.\nInstruments\u0026rsquo;a Giriş Instruments\u0026rsquo;ı açmak için: Xcode → Product → Profile (⌘I)\nYaygın kullandığım template\u0026rsquo;ler:\nTime Profiler — CPU darboğazları Allocations — Bellek kullanımı ve leak\u0026rsquo;ler Core Animation — Frame drop\u0026rsquo;ları Network — Ağ isteklerinin analizi 1. Frame Drop\u0026rsquo;larını Tespit Etme Hedef: 60 FPS (16.67ms/frame) veya ProMotion için 120 FPS.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // ❌ Main thread\u0026#39;de ağır iş func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u0026gt; UITableViewCell { let cell = tableView.dequeueReusableCell(...) let image = UIImage(contentsOfFile: heavyImagePath) // Disk IO — main thread! cell.imageView?.image = image return cell } // ✅ Background thread\u0026#39;de yükle func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u0026gt; UITableViewCell { let cell = tableView.dequeueReusableCell(...) Task { let image = await loadImageAsync(from: heavyImagePath) await MainActor.run { cell.imageView?.image = image } } return cell } 2. Bellek Leak\u0026rsquo;lerini Bulma En yaygın bellek leak kaynağı: retain cycle.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // ❌ Retain cycle class ProfileViewController: UIViewController { var viewModel: ProfileViewModel! override func viewDidLoad() { viewModel.onUpdate = { self.updateUI() // self\u0026#39;i strong olarak tutar! } } } // ✅ Weak self kullan override func viewDidLoad() { viewModel.onUpdate = { [weak self] in self?.updateUI() } } Combine\u0026rsquo;da da dikkat:\n1 2 3 4 5 6 7 8 9 // ❌ Sink\u0026#39;te strong self cancellable = publisher.sink { value in self.process(value) } // ✅ cancellable = publisher.sink { [weak self] value in self?.process(value) } 3. Launch Time Optimizasyonu App Store verilerine göre, 2 saniyenin üzerindeki launch time kullanıcı kaybına yol açıyor.\nPre-main süresini kısaltmak için:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // ❌ AppDelegate\u0026#39;de senkron yükleme func application(_ application: UIApplication, didFinishLaunchingWithOptions...) -\u0026gt; Bool { Database.shared.setup() // 300ms Analytics.shared.start() // 200ms RemoteConfig.fetch() // Network! return true } // ✅ Lazy ve async yükleme func application(...) -\u0026gt; Bool { Task { await Analytics.shared.startAsync() await RemoteConfig.fetchAsync() } return true } 4. SwiftUI Performansı SwiftUI\u0026rsquo;da gereksiz re-render\u0026rsquo;ları önlemek:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // ❌ Tüm parent yeniden render olduğunda child da render olur struct ParentView: View { @StateObject var viewModel = HeavyViewModel() var body: some View { ChildView(data: viewModel.data) } } // ✅ Equatable conformance ile optimize et struct ChildView: View, Equatable { let data: DisplayData static func == (lhs: ChildView, rhs: ChildView) -\u0026gt; Bool { lhs.data == rhs.data } var body: some View { ... } } Core Animation Profiling CALayer animasyonlarında offscreen rendering büyük sorun:\n1 2 3 4 5 6 7 8 9 10 // ❌ Pahalı: GPU\u0026#39;da offscreen buffer oluşturur layer.cornerRadius = 12 layer.masksToBounds = true layer.shadow... // Aynı anda hem cornerRadius hem shadow // ✅ UIBezierPath ile manual corner let maskPath = UIBezierPath(roundedRect: bounds, cornerRadius: 12) let maskLayer = CAShapeLayer() maskLayer.path = maskPath.cgPath layer.mask = maskLayer Sonuç Kontrol Listesi Main thread sadece UI işlemleri için kullanılıyor weak self closure\u0026rsquo;larda doğru kullanılıyor Görseller uygun boyutta yüklenip cache\u0026rsquo;leniyor Launch time 2 saniyenin altında Instruments\u0026rsquo;ta bellek profili sabit seyrediyor (leak yok) 60 FPS scroll performansı sağlanıyor Performans optimizasyonu bir kere yapılıp bırakılacak bir iş değil, sürekli bir pratik. Instruments\u0026rsquo;ı her feature development\u0026rsquo;ında çalıştırın.\n","permalink":"https://kemalturk.com/blog/ios-performance-ipuclari/","summary":"\u003ch2 id=\"giriş\"\u003eGiriş\u003c/h2\u003e\n\u003cp\u003eKullanıcılar düşük frame rate\u0026rsquo;e, yavaş açılış sürelerine ve yüksek bellek kullanımına karşı acımasızdır. Bu rehberde Xcode Instruments ile performans sorunlarını sistematik olarak nasıl bulacağınızı ve düzelteceğinizi göreceğiz.\u003c/p\u003e\n\u003ch2 id=\"instrumentsa-giriş\"\u003eInstruments\u0026rsquo;a Giriş\u003c/h2\u003e\n\u003cp\u003eInstruments\u0026rsquo;ı açmak için: \u003cstrong\u003eXcode → Product → Profile\u003c/strong\u003e (\u003ccode\u003e⌘I\u003c/code\u003e)\u003c/p\u003e\n\u003cp\u003eYaygın kullandığım template\u0026rsquo;ler:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eTime Profiler\u003c/strong\u003e — CPU darboğazları\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAllocations\u003c/strong\u003e — Bellek kullanımı ve leak\u0026rsquo;ler\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCore Animation\u003c/strong\u003e — Frame drop\u0026rsquo;ları\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eNetwork\u003c/strong\u003e — Ağ isteklerinin analizi\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1-frame-droplarını-tespit-etme\"\u003e1. Frame Drop\u0026rsquo;larını Tespit Etme\u003c/h2\u003e\n\u003cp\u003eHedef: 60 FPS (16.67ms/frame) veya ProMotion için 120 FPS.\u003c/p\u003e","title":"iOS Performans Optimizasyonu: Instruments ile Bottleneck Tespiti"},{"content":"Merhaba, ben Kemal 👋 Sr. iOS Developer olarak çalışmaktayım. Swift, SwiftUI ve UIKit ile kullanıcı odaklı mobil uygulamalar geliştiriyorum. Kod yazmayı ve her gün yeni şeyler öğrenmeyi seviyorum.\nUzmanlık Alanları Diller: Swift, Golang, Javascript UI Frameworks: SwiftUI, UIKit, React Mimari Desenler: MVVM, MVI, Clean Architecture, Viper Araçlar \u0026amp; Kütüphaneler: RxSwift, Combine, Async/Await, XCTest, Alamofire CI/CD: Fastlane, GitHub Actions, Xcode Cloud Backend Entegrasyon: REST, GraphQL, WebSocket, gRPC, QUIC Araçlar: Instruments, Xcode, RocketSim, Git İletişim GitHub: github.com/kemalturk LinkedIn: linkedin.com/in/kemal-turk Stack Overflow: stackoverflow.com/users/6533179 Medium: medium.com/@kmltrk07 E-posta: contact@kemalturk.com ","permalink":"https://kemalturk.com/about/","summary":"about","title":"Hakkımda"}]