{"id":35563,"date":"2025-12-01T05:31:15","date_gmt":"2025-12-01T02:31:15","guid":{"rendered":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/"},"modified":"2025-12-01T05:31:15","modified_gmt":"2025-12-01T02:31:15","slug":"swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik","status":"publish","type":"post","link":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/","title":{"rendered":"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik"},"content":{"rendered":"<p><body><\/p>\n<p>Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test edilebilirli\u011fi kritik \u00f6nem ta\u015f\u0131r. Ancak, geli\u015ftirme s\u00fcrecinde h\u0131zla b\u00fcy\u00fcyen projelerde, tek bir g\u00f6r\u00fcn\u00fcm (View) dosyas\u0131na y\u0131\u011f\u0131lan i\u015f mant\u0131\u011f\u0131 (business logic), durum y\u00f6netimi (state management) ve veri i\u015fleme g\u00f6revleri, k\u0131sa s\u00fcrede b\u00fcy\u00fck bir karma\u015faya yol a\u00e7abilir. Bu durum, hem yeni \u00f6zellik eklemeyi zorla\u015ft\u0131r\u0131r hem de hatalar\u0131n tespit edilmesini ve d\u00fczeltilmesini imkans\u0131z hale getirebilir. \u0130\u015fte tam da bu noktada, \u201cYeniden Kullan\u0131labilir ViewModel Mimarisi\u201d devreye girerek, SwiftUI uygulamalar\u0131n\u0131z\u0131 daha organize, esnek ve \u00f6l\u00e7eklenebilir hale getirmenin anahtar\u0131n\u0131 sunar.<\/p>\n<p>Peki, uygulamalar\u0131n\u0131zda s\u0131k\u00e7a kar\u015f\u0131la\u015ft\u0131\u011f\u0131n\u0131z bu karma\u015fay\u0131 nas\u0131l a\u015fabilir, her bir bile\u015fenin kendi sorumluluklar\u0131n\u0131 \u00fcstlendi\u011fi, temiz ve mod\u00fcler bir yap\u0131 in\u015fa edebilirsiniz? Cevap, View ve i\u015f mant\u0131\u011f\u0131n\u0131 birbirinden net bir \u015fekilde ay\u0131ran ViewModel tasar\u0131m deseninde yat\u0131yor. Bu makalede, ViewModel mimarisinin temellerinden ba\u015flayarak, SwiftUI ile nas\u0131l entegre edilece\u011fini, yeniden kullan\u0131labilirli\u011fi art\u0131rmak i\u00e7in protokollerin ve jeneriklerin nas\u0131l kullan\u0131laca\u011f\u0131n\u0131, test edilebilirli\u011fi maksimuma \u00e7\u0131karmak i\u00e7in ba\u011f\u0131ml\u0131l\u0131k enjeksiyonunun (Dependency Injection) g\u00fcc\u00fcn\u00fc ve daha pek \u00e7ok ileri d\u00fczey konuyu derinlemesine inceleyece\u011fiz. Amac\u0131m\u0131z, s\u0131f\u0131rdan ba\u015flayarak, SwiftUI projelerinizde profesyonel ve s\u00fcrd\u00fcr\u00fclebilir bir mimari kurman\u0131z\u0131 sa\u011flayacak bilgi ve ara\u00e7lar\u0131 sunmakt\u0131r.<\/p>\n<p>Bu yakla\u015f\u0131m, geli\u015ftirme ekibinizin verimlili\u011fini art\u0131rman\u0131n yan\u0131 s\u0131ra, uygulama performans\u0131n\u0131 optimize etmenize ve hata ay\u0131klama s\u00fcre\u00e7lerini basitle\u015ftirmenize de yard\u0131mc\u0131 olacakt\u0131r. \u00d6zellikle b\u00fcy\u00fck ve karma\u015f\u0131k projelerde, iyi tasarlanm\u0131\u015f bir ViewModel katman\u0131, uygulaman\u0131z\u0131n gelecekteki de\u011fi\u015fikliklere daha kolay adapte olmas\u0131n\u0131 sa\u011flayarak, uzun vadede size \u00f6nemli avantajlar sunar. \u015eimdi, bu g\u00fc\u00e7l\u00fc mimarinin detaylar\u0131na inerek, SwiftUI geli\u015ftirme yolculu\u011funuzda yeni bir sayfa a\u00e7maya haz\u0131r m\u0131s\u0131n\u0131z?<\/p>\n<h2>ViewModel Nedir ve Neden SwiftUI ile Bu Kadar \u00d6nemli Hale Geldi?<\/h2>\n<p>ViewModel, genellikle Model-View-ViewModel (MVVM) mimari deseninin temel bir bile\u015feni olarak kar\u015f\u0131m\u0131za \u00e7\u0131kar. MVVM, kullan\u0131c\u0131 aray\u00fcz\u00fcn\u00fc (View) i\u015f mant\u0131\u011f\u0131ndan (Model) ay\u0131rarak, kod taban\u0131n\u0131 daha d\u00fczenli, test edilebilir ve s\u00fcrd\u00fcr\u00fclebilir hale getirmeyi ama\u00e7lar. Peki, ViewModel tam olarak ne i\u015f yapar? K\u0131saca, ViewModel, View ile Model aras\u0131nda bir k\u00f6pr\u00fc g\u00f6revi g\u00f6r\u00fcr. View&#8217;in g\u00f6sterim mant\u0131\u011f\u0131n\u0131 (presentation logic) ve durumunu (state) y\u00f6netirken, Model&#8217;den ald\u0131\u011f\u0131 verileri View&#8217;in kolayca kullanabilece\u011fi bir formata d\u00f6n\u00fc\u015ft\u00fcr\u00fcr ve kullan\u0131c\u0131 etkile\u015fimlerini Model&#8217;e iletir.<\/p>\n<p>SwiftUI\u2019nin reaktif ve deklaratif do\u011fas\u0131, ViewModel desenini daha da cazip hale getirmi\u015ftir. SwiftUI ile bir View tan\u0131mlad\u0131\u011f\u0131n\u0131zda, genellikle bu View\u2019in do\u011frudan i\u015f mant\u0131\u011f\u0131yla ilgilenmesini istemeyiz. View\u2019ler, sadece veriyi g\u00f6rselle\u015ftirmek ve kullan\u0131c\u0131 etkile\u015fimlerini yakalamakla sorumlu olmal\u0131d\u0131r. \u0130\u015f mant\u0131\u011f\u0131, a\u011f istekleri, veritaban\u0131 i\u015flemleri, veri formatlama gibi karma\u015f\u0131k g\u00f6revler ViewModel\u2019in sorumlulu\u011funda olmal\u0131d\u0131r. Bu ayr\u0131m, View\u2019lerinizi daha basit, daha okunabilir ve daha az hata e\u011filimli hale getirirken, ViewModel\u2019lerinizi de ba\u011f\u0131ms\u0131z olarak test edilebilir k\u0131lar.<\/p>\n<p>SwiftUI\u2019deki <code>@ObservableObject<\/code> protokol\u00fc ve <code>@Published<\/code> \u00f6zellik sarmalay\u0131c\u0131s\u0131 (property wrapper), ViewModel\u2019leri SwiftUI View\u2019leri ile sorunsuz bir \u015fekilde entegre etmemizi sa\u011flar. Bir ViewModel, <code>@ObservableObject<\/code> protokol\u00fcne uydu\u011funda ve View\u2019in g\u00f6zlemlemesi gereken \u00f6zellikleri <code>@Published<\/code> ile i\u015faretledi\u011finde, bu \u00f6zelliklerdeki herhangi bir de\u011fi\u015fiklik, otomatik olarak ilgili SwiftUI View\u2019lerinin yeniden \u00e7izilmesini (re-render) tetikler. Bu reaktif ba\u011flama, manuel olarak UI g\u00fcncellemeleri yapma ihtiyac\u0131n\u0131 ortadan kald\u0131r\u0131r ve geli\u015ftirme s\u00fcrecini \u00f6nemli \u00f6l\u00e7\u00fcde h\u0131zland\u0131r\u0131r. B\u00f6ylece, veri ak\u0131\u015f\u0131 daha \u00f6ng\u00f6r\u00fclebilir hale gelir ve durum y\u00f6netimi karma\u015fas\u0131 azal\u0131r.<\/p>\n<p>ViewModel kullan\u0131m\u0131n\u0131n bir di\u011fer kritik avantaj\u0131 da test edilebilirli\u011fidir. View\u2019ler genellikle bir i\u015fletim sistemi \u00e7er\u00e7evesine (iOS, macOS vb.) ba\u011fl\u0131 oldu\u011fundan, do\u011frudan test edilmesi zordur ve genellikle UI testleri gibi daha yava\u015f ve k\u0131r\u0131lgan y\u00f6ntemler gerektirir. Ancak ViewModel\u2019ler, platformdan ba\u011f\u0131ms\u0131z Swift s\u0131n\u0131flar\u0131d\u0131r. Bu sayede, i\u015f mant\u0131\u011f\u0131n\u0131z\u0131 ve durum y\u00f6netimini i\u00e7eren ViewModel\u2019leri, h\u0131zl\u0131 ve g\u00fcvenilir bir \u015fekilde birim testleri (unit tests) ile test edebilirsiniz. Bu, kod kalitesini art\u0131r\u0131r, hatalar\u0131 erken a\u015famada yakalaman\u0131z\u0131 sa\u011flar ve uygulama g\u00fcvenilirli\u011fini \u00f6nemli \u00f6l\u00e7\u00fcde y\u00fckseltir. Sonu\u00e7 olarak, ViewModel mimarisi, modern SwiftUI uygulamalar\u0131 geli\u015ftiren her geli\u015ftiricinin benimsemesi gereken g\u00fc\u00e7l\u00fc bir ara\u00e7t\u0131r.<\/p>\n<h2>Temel Bir ViewModel Nas\u0131l Olu\u015fturulur? Ad\u0131m Ad\u0131m Rehber<\/h2>\n<p>ViewModel mimarisini anlaman\u0131n en iyi yolu, basit bir \u00f6rnek \u00fczerinden ad\u0131m ad\u0131m uygulamakt\u0131r. Bu b\u00f6l\u00fcmde, kullan\u0131c\u0131dan bir isim al\u0131p bir kar\u015f\u0131lama mesaj\u0131 g\u00f6steren temel bir senaryo i\u00e7in nas\u0131l bir ViewModel olu\u015fturaca\u011f\u0131m\u0131z\u0131 ve bunu SwiftUI View&#8217;i ile nas\u0131l entegre edece\u011fimizi inceleyece\u011fiz. Bu \u00f6rnek, <code>ObservableObject<\/code> ve <code>@Published<\/code> kavramlar\u0131n\u0131 peki\u015ftirmenize yard\u0131mc\u0131 olacakt\u0131r.<\/p>\n<h3>Ad\u0131m 1: ViewModel S\u0131n\u0131f\u0131n\u0131 Tan\u0131mlamak<\/h3>\n<p>\u0130lk olarak, <code>GreetingViewModel<\/code> ad\u0131nda bir s\u0131n\u0131f olu\u015fturaca\u011f\u0131z. Bu s\u0131n\u0131f, kullan\u0131c\u0131n\u0131n ad\u0131n\u0131 tutacak, bir kar\u015f\u0131lama mesaj\u0131 \u00fcretecek ve bu mesaj\u0131 View&#8217;e bildirecektir. <code>ObservableObject<\/code> protokol\u00fcn\u00fc benimsemesi ve View&#8217;in g\u00f6zlemlemesi gereken \u00f6zellikleri <code>@Published<\/code> ile i\u015faretlemesi \u00f6nemlidir.<\/p>\n<pre><code class=\"language-swift\">\nimport Foundation\nimport Combine\n\nclass GreetingViewModel: ObservableObject {\n    @Published var name: String = \"\" {\n        didSet {\n            updateGreetingMessage()\n        }\n    }\n    @Published var greetingMessage: String = \"L\u00fctfen isminizi girin.\"\n\n    init() {\n        \/\/ \u0130sterseniz ba\u015flang\u0131\u00e7ta ba\u015fka mant\u0131klar ekleyebilirsiniz\n    }\n\n    private func updateGreetingMessage() {\n        if name.isEmpty {\n            greetingMessage = \"L\u00fctfen isminizi girin.\"\n        } else {\n            greetingMessage = \"Merhaba, \\(name)!\"\n        }\n    }\n\n    func resetName() {\n        name = \"\"\n    }\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Yukar\u0131daki kod blo\u011funda, <code>name<\/code> \u00f6zelli\u011fi <code>@Published<\/code> ile i\u015faretlenmi\u015ftir. Bu, <code>name<\/code> de\u011feri her de\u011fi\u015fti\u011finde, ViewModel'i g\u00f6zlemleyen t\u00fcm View'lerin otomatik olarak g\u00fcncellenece\u011fi anlam\u0131na gelir. <code>didSet<\/code> blo\u011fu sayesinde, <code>name<\/code> de\u011fi\u015fti\u011finde <code>updateGreetingMessage()<\/code> fonksiyonu \u00e7a\u011fr\u0131larak <code>greetingMessage<\/code> da g\u00fcncellenir. <code>greetingMessage<\/code> da <code>@Published<\/code> oldu\u011fu i\u00e7in, bu mesaj View'e yans\u0131t\u0131l\u0131r.<\/p>\n<h3>Ad\u0131m 2: SwiftUI View'i ile ViewModel'i Entegre Etmek<\/h3>\n<p>\u015eimdi, bu ViewModel'i kullanacak bir SwiftUI View olu\u015ftural\u0131m. View i\u00e7inde ViewModel'i ba\u015flatmak ve g\u00f6zlemlemek i\u00e7in <code>@StateObject<\/code> veya <code>@ObservedObject<\/code> \u00f6zellik sarmalay\u0131c\u0131lar\u0131n\u0131 kullan\u0131r\u0131z. Genellikle, bir View'in ya\u015fam d\u00f6ng\u00fcs\u00fcne ba\u011fl\u0131 olarak ViewModel'i ba\u015flatan ve onun \u00f6mr\u00fcn\u00fc y\u00f6neten <code>@StateObject<\/code> tercih edilir.<\/p>\n<pre><code class=\"language-swift\">\nimport SwiftUI\n\nstruct GreetingView: View {\n    @StateObject var viewModel = GreetingViewModel()\n\n    var body: some View {\n        VStack(spacing: 20) {\n            TextField(\"Ad\u0131n\u0131z\u0131 girin\", text: $viewModel.name)\n                .textFieldStyle(RoundedBorderTextFieldStyle())\n                .padding()\n\n            Text(viewModel.greetingMessage)\n                .font(.title)\n                .foregroundColor(.blue)\n\n            Button(\"Ad\u0131 Temizle\") {\n                viewModel.resetName()\n            }\n            .padding()\n            .background(Color.red)\n            .foregroundColor(.white)\n            .cornerRadius(8)\n        }\n        .padding()\n    }\n}\n\nstruct GreetingView_Previews: PreviewProvider {\n    static var previews: some View {\n        GreetingView()\n    }\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Bu SwiftUI View'inde, <code>@StateObject var viewModel = GreetingViewModel()<\/code> sat\u0131r\u0131 ile ViewModel'in bir \u00f6rne\u011fini olu\u015fturup View'in ya\u015fam d\u00f6ng\u00fcs\u00fcne ba\u011fl\u0131yoruz. <code>TextField<\/code>'\u0131n <code>text<\/code> parametresini <code>$viewModel.name<\/code> ile iki y\u00f6nl\u00fc ba\u011flayarak, kullan\u0131c\u0131 inputunu do\u011frudan ViewModel'in <code>name<\/code> \u00f6zelli\u011fine iletiyoruz. <code>Text(viewModel.greetingMessage)<\/code> ise ViewModel'den gelen g\u00fcncel kar\u015f\u0131lama mesaj\u0131n\u0131 g\u00f6steriyor. <code>Button<\/code> ile de ViewModel'deki <code>resetName()<\/code> fonksiyonunu \u00e7a\u011f\u0131rarak View'den ViewModel'e bir eylem iletiyoruz.<\/p>\n<div class=\"expert-tip\">\n    Uzman \u0130pucu: <code>@StateObject<\/code> bir View hiyerar\u015fisinde ViewModel'i ba\u015flatan View i\u00e7in kullan\u0131l\u0131r ve ViewModel'in View'in ya\u015fam d\u00f6ng\u00fcs\u00fc boyunca hayatta kalmas\u0131n\u0131 sa\u011flar. E\u011fer bir ViewModel'i alt View'lere aktar\u0131yorsan\u0131z, <code>@ObservedObject<\/code> veya <code>environmentObject<\/code> kullanmay\u0131 d\u00fc\u015f\u00fcnebilirsiniz. <code>@ObservedObject<\/code> ana View'den gelen bir referans\u0131 izlerken, <code>environmentObject<\/code> View hiyerar\u015fisinde daha derinlere enjekte etmek i\u00e7in kullan\u0131\u015fl\u0131d\u0131r.\n  <\/div>\n<p>Bu temel yap\u0131, ViewModel deseninin SwiftUI ile nas\u0131l \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131 net bir \u015fekilde ortaya koymaktad\u0131r. View, sadece UI elementlerini ve layout'u tan\u0131mlarken, t\u00fcm i\u015f mant\u0131\u011f\u0131 ve durum y\u00f6netimi ViewModel taraf\u0131ndan halledilmektedir. Bu sayede, View'iniz temiz kal\u0131r, ViewModel'iniz ba\u011f\u0131ms\u0131z olarak test edilebilir hale gelir ve uygulaman\u0131z\u0131n genel mimarisi daha anla\u015f\u0131l\u0131r bir yap\u0131ya kavu\u015fur. Bu basit \u00f6rnekten yola \u00e7\u0131karak, daha karma\u015f\u0131k senaryolar i\u00e7in ViewModel'ler olu\u015fturmaya ba\u015flayabilir ve uygulaman\u0131z\u0131n \u00f6l\u00e7eklenebilirli\u011fini art\u0131rabilirsiniz.<\/p>\n<h2>Yeniden Kullan\u0131labilirlik Nas\u0131l Sa\u011flan\u0131r? Protokoller ve Jenerikler ile G\u00fc\u00e7lendirme<\/h2>\n<p>Uygulamalar b\u00fcy\u00fcd\u00fck\u00e7e, farkl\u0131 ekranlar veya bile\u015fenler aras\u0131nda benzer i\u015f mant\u0131klar\u0131yla kar\u015f\u0131la\u015fmak ka\u00e7\u0131n\u0131lmazd\u0131r. \u00d6rne\u011fin, veri y\u00fckleme, form do\u011frulama, liste filtreleme gibi g\u00f6revler bir\u00e7ok farkl\u0131 View i\u00e7in ortak olabilir. Bu t\u00fcr tekrarlanan mant\u0131klar\u0131 her seferinde yeniden yazmak yerine, onlar\u0131 yeniden kullan\u0131labilir par\u00e7alar halinde tasarlamak, hem kod miktar\u0131n\u0131 azalt\u0131r hem de tutarl\u0131l\u0131\u011f\u0131 art\u0131r\u0131r. \u0130\u015fte tam da bu noktada Swift'in g\u00fc\u00e7l\u00fc protokolleri ve jenerikleri devreye girer ve ViewModel mimarinizi daha esnek ve g\u00fc\u00e7l\u00fc hale getirir.<\/p>\n<h3>Protokoller ile Ortak Aray\u00fczler Tan\u0131mlama<\/h3>\n<p>Protokoller, belirli bir i\u015flevi yerine getiren s\u0131n\u0131flar\u0131n veya yap\u0131n\u0131n sahip olmas\u0131 gereken metodlar\u0131 ve \u00f6zellikleri tan\u0131mlamam\u0131z\u0131 sa\u011flar. ViewModel ba\u011flam\u0131nda, protokoller, farkl\u0131 ViewModel'lerin payla\u015fabilece\u011fi ortak davran\u0131\u015flar\u0131 veya durumlar\u0131 soyutlamak i\u00e7in idealdir. \u00d6rne\u011fin, bir\u00e7ok ViewModel bir API'den veri \u00e7ekme ve bir y\u00fckleme durumu g\u00f6sterme ihtiyac\u0131 duyabilir. Bu ortak davran\u0131\u015f\u0131 bir protokolle tan\u0131mlayabiliriz:<\/p>\n<pre><code class=\"language-swift\">\nimport Foundation\nimport Combine\n\nprotocol LoadableObject: ObservableObject {\n    associatedtype Output\n    var state: LoadingState<Output> { get }\n    func load()\n}\n\nenum LoadingState<Value> {\n    case idle\n    case loading\n    case failed(Error)\n    case loaded(Value)\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Bu <code>LoadableObject<\/code> protokol\u00fc, herhangi bir ViewModel'in bir <code>state<\/code> \u00f6zelli\u011fi (y\u00fckleme durumunu belirten) ve bir <code>load()<\/code> metodu olmas\u0131 gerekti\u011fini belirtir. B\u00f6ylece, veri y\u00fckleyen her ViewModel bu protokole uyarak belirli bir aray\u00fcz\u00fc sa\u011flam\u0131\u015f olur. Bir SwiftUI View'i de bu protokole uyan herhangi bir ViewModel'i kullanarak veri y\u00fckleme durumunu dinamik olarak g\u00f6sterebilir.<\/p>\n<h3>Jenerikler ile Farkl\u0131 Veri Tiplerini Destekleme<\/h3>\n<p>Jenerikler, esnek ve yeniden kullan\u0131labilir fonksiyonlar ve tipler yazmam\u0131z\u0131 sa\u011flayan g\u00fc\u00e7l\u00fc bir Swift \u00f6zelli\u011fidir. Bir ViewModel'i jenerik hale getirerek, farkl\u0131 Model tipleriyle \u00e7al\u0131\u015fmas\u0131n\u0131 sa\u011flayabiliriz. \u00d6rne\u011fin, yukar\u0131daki <code>LoadingState<\/code> enum'\u0131 jenerik bir <code>Value<\/code> tipi alarak, y\u00fcklenecek verinin tipinden ba\u011f\u0131ms\u0131z hale gelmi\u015ftir. \u015eimdi bu protokol\u00fc ve enum'\u0131 kullanarak jenerik bir veri y\u00fckleme ViewModel'i olu\u015ftural\u0131m:<\/p>\n<pre><code class=\"language-swift\">\nimport Foundation\nimport Combine\n\nclass GenericDataLoaderViewModel<T>: ObservableObject, LoadableObject {\n    typealias Output = T\n\n    @Published var state: LoadingState<T> = .idle\n\n    private var cancellables = Set<AnyCancellable>()\n    private let dataProvider: any DataProvider<T> \/\/ Jenerik bir veri sa\u011flay\u0131c\u0131\n\n    init(dataProvider: any DataProvider<T>) {\n        self.dataProvider = dataProvider\n    }\n\n    func load() {\n        guard case .loading = state else { return } \/\/ Zaten y\u00fckleniyorsa tekrar y\u00fckleme\n        state = .loading\n        dataProvider.fetchData()\n            .receive(on: DispatchQueue.main)\n            .sink { [weak self] completion in\n                if case .failure(let error) = completion {\n                    self?.state = .failed(error)\n                }\n            } receiveValue: { [weak self] data in\n                self?.state = .loaded(data)\n            }\n            .store(in: &cancellables)\n    }\n}\n\n\/\/ DataProvider protokol\u00fc, farkl\u0131 veri kaynaklar\u0131 i\u00e7in soyutlama sa\u011flar\nprotocol DataProvider<T> {\n    func fetchData() -> AnyPublisher<T, Error>\n}\n\n\/\/ \u00d6rnek bir kullan\u0131c\u0131 veri sa\u011flay\u0131c\u0131s\u0131\nclass UserAPIDataProvider: DataProvider<[User]> {\n    func fetchData() -> AnyPublisher<[User], Error> {\n        \/\/ API \u00e7a\u011fr\u0131s\u0131 sim\u00fclasyonu\n        Just([User(id: 1, name: \"Alice\"), User(id: 2, name: \"Bob\")])\n            .setFailureType(to: Error.self)\n            .eraseToAnyPublisher()\n    }\n}\n\nstruct User: Identifiable, Codable {\n    let id: Int\n    let name: String\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Bu \u00f6rnekte, <code>GenericDataLoaderViewModel<\/code> jenerik bir <code>T<\/code> tipi al\u0131r ve <code>LoadableObject<\/code> protokol\u00fcne uyar. <code>dataProvider<\/code> ba\u011f\u0131ml\u0131l\u0131\u011f\u0131 sayesinde, bu ViewModel farkl\u0131 t\u00fcrde verileri (kullan\u0131c\u0131lar, \u00fcr\u00fcnler, makaleler vb.) y\u00fcklemek i\u00e7in kullan\u0131labilir, yeter ki ilgili <code>DataProvider<\/code> protokol\u00fcne uyan bir nesne sa\u011flans\u0131n. Bu tasar\u0131m, ayn\u0131 y\u00fckleme mant\u0131\u011f\u0131n\u0131 farkl\u0131 veri tipleri ve API'ler i\u00e7in yeniden kullanman\u0131z\u0131 sa\u011flar.<\/p>\n<h3>Vaka Analizi: Farkl\u0131 API'lerden Veri \u00c7eken Ekranlar<\/h3>\n<p>Bir e-ticaret uygulamas\u0131nda hem \u00fcr\u00fcn listesini hem de blog yaz\u0131lar\u0131n\u0131 g\u00f6steren iki ayr\u0131 ekran\u0131n\u0131z oldu\u011funu varsayal\u0131m. Her iki ekran da veriyi bir API'den \u00e7ekmeli, bir y\u00fckleme g\u00f6stergesi sunmal\u0131, hata durumlar\u0131n\u0131 y\u00f6netmeli ve veriler y\u00fcklendi\u011finde bunlar\u0131 g\u00f6stermelidir. <code>GenericDataLoaderViewModel<\/code> ve <code>LoadableObject<\/code> protokol\u00fc sayesinde, her ekran i\u00e7in ayr\u0131 bir y\u00fckleme mant\u0131\u011f\u0131 yazmak yerine, bu jenerik ViewModel'i kullanabiliriz:<\/p>\n<ul>\n<li><code>ProductListViewModel: GenericDataLoaderViewModel<[Product]><\/code><\/li>\n<li><code>BlogPostsViewModel: GenericDataLoaderViewModel<[BlogPost]><\/code><\/li>\n<\/ul>\n<p>Her iki ViewModel de ayn\u0131 temel y\u00fckleme i\u015flevselli\u011fini payla\u015f\u0131r, ancak farkl\u0131 <code>DataProvider<\/code> implementasyonlar\u0131 sayesinde kendi Model tipleriyle \u00e7al\u0131\u015f\u0131rlar. Bu, kod tekrar\u0131n\u0131 \u00f6nler, bak\u0131m\u0131 kolayla\u015ft\u0131r\u0131r ve yeni bir liste tabanl\u0131 ekran eklemeniz gerekti\u011finde geli\u015ftirme s\u00fcrecini h\u0131zland\u0131r\u0131r. Protokoller ve jenerikler, ViewModel'lerinizi sadece veri tutucu olmaktan \u00e7\u0131kar\u0131p, g\u00fc\u00e7l\u00fc, esnek ve ger\u00e7ekten yeniden kullan\u0131labilir bile\u015fenler haline getirir.<\/p>\n<div class=\"expert-tip\">\n    Uzman \u0130pucu: Protokolleri ve jenerikleri bir arada kullanarak, sadece ViewModel'lerinizi de\u011fil, ayn\u0131 zamanda View'lerinizi de daha yeniden kullan\u0131labilir hale getirebilirsiniz. \u00d6rne\u011fin, <code>LoadableObject<\/code> protokol\u00fcne uyan herhangi bir ViewModel'i alabilen jenerik bir <code>LoadingView<\/code> olu\u015fturarak, y\u00fckleme durumunu g\u00f6steren UI mant\u0131\u011f\u0131n\u0131 da tek bir yerde toplayabilirsiniz.\n  <\/div>\n<h2>Dependency Injection (Ba\u011f\u0131ml\u0131l\u0131k Enjeksiyonu) ile Test Edilebilir ve Esnek ViewModel'ler<\/h2>\n<p>B\u00fcy\u00fck ve karma\u015f\u0131k uygulamalar geli\u015ftirirken, kodun par\u00e7alar\u0131 aras\u0131ndaki ba\u011f\u0131ml\u0131l\u0131klar ka\u00e7\u0131n\u0131lmazd\u0131r. Bir ViewModel'in, bir a\u011f iste\u011fi servisine, bir veritaban\u0131 y\u00f6neticisine veya ba\u015fka bir yard\u0131mc\u0131 servise ihtiyac\u0131 olabilir. Bu ba\u011f\u0131ml\u0131l\u0131klar\u0131 do\u011frudan ViewModel i\u00e7inde olu\u015fturmak, test edilebilirli\u011fi zorla\u015ft\u0131r\u0131r ve kodun esnekli\u011fini azalt\u0131r. \u0130\u015fte bu noktada, Ba\u011f\u0131ml\u0131l\u0131k Enjeksiyonu (Dependency Injection - DI) devreye girer. DI, bir nesnenin ihtiya\u00e7 duydu\u011fu ba\u011f\u0131ml\u0131l\u0131klar\u0131 kendisi olu\u015fturmak yerine, d\u0131\u015far\u0131dan sa\u011flanmas\u0131 prensibidir.<\/p>\n<h3>Ba\u011f\u0131ml\u0131l\u0131k Enjeksiyonu Nedir ve Neden Gereklidir?<\/h3>\n<p>Ba\u011f\u0131ml\u0131l\u0131k Enjeksiyonu (DI), yaz\u0131l\u0131m tasar\u0131m desenlerinden biridir ve bir nesnenin (ba\u011f\u0131ml\u0131) di\u011fer nesnelerden (ba\u011f\u0131ml\u0131l\u0131klar) sorumlu olmas\u0131n\u0131 sa\u011flamak yerine, bu ba\u011f\u0131ml\u0131l\u0131klar\u0131n d\u0131\u015far\u0131dan (enjekt\u00f6r taraf\u0131ndan) sa\u011flanmas\u0131n\u0131 ifade eder. ViewModel ba\u011flam\u0131nda, bu, ViewModel'in kendi servislerini (\u00f6rne\u011fin, bir API istemcisi) olu\u015fturmak yerine, bu servislerin ViewModel'e d\u0131\u015far\u0131dan iletilmesi anlam\u0131na gelir.<\/p>\n<p>DI'nin temel faydalar\u0131 \u015funlard\u0131r:<\/p>\n<ul>\n<li><strong>Test Edilebilirlik:<\/strong> Ba\u011f\u0131ml\u0131l\u0131klar\u0131 d\u0131\u015far\u0131dan enjekte ederek, testler s\u0131ras\u0131nda ger\u00e7ek servisler yerine sahte (mock) veya hayali (stub) nesneleri kullanabiliriz. Bu, ViewModel'in izole bir \u015fekilde test edilmesini sa\u011flar.<\/li>\n<li><strong>Esneklik ve Mod\u00fclerlik:<\/strong> Ba\u011f\u0131ml\u0131l\u0131klar soyutlamalar (protokoller) \u00fczerinden sa\u011fland\u0131\u011f\u0131nda, bir ba\u011f\u0131ml\u0131l\u0131\u011f\u0131n implementasyonunu de\u011fi\u015ftirmek, ViewModel kodunda herhangi bir de\u011fi\u015fiklik yapmay\u0131 gerektirmez. Bu da uygulaman\u0131n daha mod\u00fcler ve esnek olmas\u0131n\u0131 sa\u011flar.<\/li>\n<li><strong>Kod Tekrar\u0131n\u0131n Azalmas\u0131:<\/strong> Ba\u011f\u0131ml\u0131l\u0131klar\u0131 tek bir yerden y\u00f6netmek, ayn\u0131 servis nesnesini bir\u00e7ok farkl\u0131 yere iletme ihtiyac\u0131n\u0131 ortadan kald\u0131r\u0131r.<\/li>\n<li><strong>Daha Az S\u0131k\u0131 Ba\u011fl\u0131l\u0131k:<\/strong> Nesneler aras\u0131ndaki s\u0131k\u0131 ba\u011fl\u0131l\u0131\u011f\u0131 azalt\u0131r, bu da kodun bak\u0131m\u0131n\u0131 ve evrimini kolayla\u015ft\u0131r\u0131r.<\/li>\n<\/ul>\n<h3>Farkl\u0131 Enjeksiyon Y\u00f6ntemleri<\/h3>\n<p>Ba\u011f\u0131ml\u0131l\u0131k enjeksiyonunu uygulaman\u0131n birka\u00e7 yayg\u0131n yolu vard\u0131r:<\/p>\n<ol>\n<li><strong>Initializer Enjeksiyonu (Constructor Injection):<\/strong> Ba\u011f\u0131ml\u0131l\u0131klar, bir nesne olu\u015fturuldu\u011funda initializer (ba\u015flat\u0131c\u0131) arac\u0131l\u0131\u011f\u0131yla sa\u011flan\u0131r. Bu en yayg\u0131n ve tercih edilen y\u00f6ntemdir \u00e7\u00fcnk\u00fc ba\u011f\u0131ml\u0131l\u0131klar\u0131n olu\u015fturulur olu\u015fturulmaz mevcut olmas\u0131n\u0131 garanti eder.<\/li>\n<li><strong>Property Enjeksiyonu (Setter Injection):<\/strong> Ba\u011f\u0131ml\u0131l\u0131klar, nesnenin bir \u00f6zelli\u011fi (property) arac\u0131l\u0131\u011f\u0131yla enjekte edilir. Genellikle opsiyonel ba\u011f\u0131ml\u0131l\u0131klar veya varsay\u0131lan de\u011ferleri olan ba\u011f\u0131ml\u0131l\u0131klar i\u00e7in kullan\u0131l\u0131r.<\/li>\n<li><strong>Method Enjeksiyonu:<\/strong> Ba\u011f\u0131ml\u0131l\u0131klar, bir metoda parametre olarak iletilir. Sadece belirli bir metodun o ba\u011f\u0131ml\u0131l\u0131\u011fa ihtiyac\u0131 oldu\u011funda kullan\u0131\u015fl\u0131d\u0131r.<\/li>\n<\/ol>\n<h3>Kod \u00d6rne\u011fi: <code>UserService<\/code> Ba\u011f\u0131ml\u0131l\u0131\u011f\u0131 Olan Bir ViewModel<\/h3>\n<p>Bir kullan\u0131c\u0131 listesini \u00e7eken ve g\u00f6r\u00fcnt\u00fcleyen bir ViewModel d\u00fc\u015f\u00fcnelim. Bu ViewModel'in bir <code>UserService<\/code>'e ihtiyac\u0131 var. <code>UserService<\/code>'i do\u011frudan ViewModel i\u00e7inde olu\u015fturmak yerine, initializer arac\u0131l\u0131\u011f\u0131yla enjekte edece\u011fiz.<\/p>\n<pre><code class=\"language-swift\">\nimport Foundation\nimport Combine\n\n\/\/ 1. UserService i\u00e7in bir protokol tan\u0131mla\nprotocol UserServiceProtocol {\n    func fetchUsers() -> AnyPublisher<[User], Error>\n}\n\n\/\/ 2. Ger\u00e7ek UserService implementasyonu\nclass APIService: UserServiceProtocol {\n    func fetchUsers() -> AnyPublisher<[User], Error> {\n        \/\/ Burada ger\u00e7ek bir a\u011f iste\u011fi yap\u0131l\u0131rd\u0131\n        return Just([\n            User(id: 1, name: \"Ahmet\"),\n            User(id: 2, name: \"Ay\u015fe\"),\n            User(id: 3, name: \"Mehmet\")\n        ])\n        .delay(for: .seconds(1), scheduler: DispatchQueue.main) \/\/ A\u011f gecikmesi sim\u00fclasyonu\n        .setFailureType(to: Error.self)\n        .eraseToAnyPublisher()\n    }\n}\n\n\/\/ 3. UserViewModel, UserServiceProtocol ba\u011f\u0131ml\u0131l\u0131\u011f\u0131n\u0131 initializer \u00fczerinden al\u0131r\nclass UserListViewModel: ObservableObject {\n    @Published var users: [User] = []\n    @Published var isLoading: Bool = false\n    @Published var errorMessage: String?\n\n    private var cancellables = Set<AnyCancellable>()\n    private let userService: UserServiceProtocol \/\/ Ba\u011f\u0131ml\u0131l\u0131k\n\n    init(userService: UserServiceProtocol) { \/\/ Enjeksiyon burada ger\u00e7ekle\u015fiyor\n        self.userService = userService\n    }\n\n    func loadUsers() {\n        isLoading = true\n        errorMessage = nil\n\n        userService.fetchUsers()\n            .receive(on: DispatchQueue.main)\n            .sink { [weak self] completion in\n                self?.isLoading = false\n                if case .failure(let error) = completion {\n                    self?.errorMessage = error.localizedDescription\n                }\n            } receiveValue: { [weak self] fetchedUsers in\n                self?.users = fetchedUsers\n            }\n            .store(in: &cancellables)\n    }\n}\n\nstruct User: Identifiable, Codable {\n    let id: Int\n    let name: String\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Bu yap\u0131da, <code>UserListViewModel<\/code> art\u0131k hangi <code>UserService<\/code> implementasyonunu kulland\u0131\u011f\u0131n\u0131 bilmiyor veya umursam\u0131yor. Sadece <code>UserServiceProtocol<\/code>'e uyan bir nesneye ihtiyac\u0131 oldu\u011funu biliyor. Bu sayede, ViewModel'i ba\u015flatt\u0131\u011f\u0131n\u0131zda ger\u00e7ek <code>APIService<\/code>'i veya bir test s\u0131ras\u0131nda sahte bir servisi enjekte edebilirsiniz.<\/p>\n<h3>Vaka Analizi: Mock Servislerle ViewModel'i Test Etme<\/h3>\n<p>DI'nin en b\u00fcy\u00fck faydalar\u0131ndan biri de test edilebilirli\u011fi art\u0131rmas\u0131d\u0131r. <code>UserListViewModel<\/code>'i test etmek i\u00e7in, ger\u00e7ek bir API iste\u011fi yapmak yerine, sahte bir <code>UserService<\/code> olu\u015fturabiliriz:<\/p>\n<pre><code class=\"language-swift\">\nimport XCTest\nimport Combine\n@testable import YourAppName \/\/ Uygulaman\u0131z\u0131n ad\u0131n\u0131 buraya yaz\u0131n\n\n\/\/ Sahte UserService implementasyonu\nclass MockUserService: UserServiceProtocol {\n    var shouldReturnError = false\n    var mockUsers: [User] = []\n\n    func fetchUsers() -> AnyPublisher<[User], Error> {\n        if shouldReturnError {\n            return Fail(error: NSError(domain: \"MockError\", code: 0, userInfo: nil))\n                .eraseToAnyPublisher()\n        } else {\n            return Just(mockUsers)\n                .setFailureType(to: Error.self)\n                .eraseToAnyPublisher()\n        }\n    }\n}\n\nclass UserListViewModelTests: XCTestCase {\n    var cancellables: Set<AnyCancellable>!\n\n    override func setUp() {\n        super.setUp()\n        cancellables = Set<AnyCancellable>()\n    }\n\n    override func tearDown() {\n        cancellables = nil\n        super.tearDown()\n    }\n\n    func testLoadUsersSuccessfully() {\n        let mockService = MockUserService()\n        mockService.mockUsers = [User(id: 1, name: \"Test User 1\"), User(id: 2, name: \"Test User 2\")]\n        let viewModel = UserListViewModel(userService: mockService)\n\n        let expectation = XCTestExpectation(description: \"Users loaded successfully\")\n\n        viewModel.$users\n            .dropFirst() \/\/ Initial empty array\n            .sink { users in\n                XCTAssertEqual(users.count, 2)\n                XCTAssertEqual(users.first?.name, \"Test User 1\")\n                expectation.fulfill()\n            }\n            .store(in: &cancellables)\n\n        viewModel.loadUsers()\n        wait(for: [expectation], timeout: 2.0)\n        XCTAssertFalse(viewModel.isLoading)\n        XCTAssertNil(viewModel.errorMessage)\n    }\n\n    func testLoadUsersFailure() {\n        let mockService = MockUserService()\n        mockService.shouldReturnError = true\n        let viewModel = UserListViewModel(userService: mockService)\n\n        let expectation = XCTestExpectation(description: \"Users failed to load\")\n\n        viewModel.$errorMessage\n            .dropFirst() \/\/ Initial nil\n            .sink { errorMessage in\n                XCTAssertNotNil(errorMessage)\n                XCTAssertFalse(errorMessage!.isEmpty)\n                expectation.fulfill()\n            }\n            .store(in: &cancellables)\n\n        viewModel.loadUsers()\n        wait(for: [expectation], timeout: 2.0)\n        XCTAssertFalse(viewModel.isLoading)\n        XCTAssertTrue(viewModel.users.isEmpty)\n    }\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Bu testler, ViewModel'in <code>UserService<\/code>'e olan ba\u011f\u0131ml\u0131l\u0131\u011f\u0131n\u0131 enjekte ederek, farkl\u0131 senaryolar\u0131 (ba\u015far\u0131l\u0131 y\u00fckleme, hata durumu) kolayca test etmemizi sa\u011flar. Ger\u00e7ek bir a\u011f ba\u011flant\u0131s\u0131na veya veritaban\u0131na ihtiya\u00e7 duymadan ViewModel'in davran\u0131\u015f\u0131n\u0131 do\u011frulayabiliriz. Bu, ViewModel'lerinizi g\u00fcvenilir, bak\u0131m\u0131 kolay ve hatalardan ar\u0131nd\u0131r\u0131lm\u0131\u015f hale getirmenin temelidir.<\/p>\n<div class=\"expert-tip\">\n    Uzman \u0130pucu: Daha b\u00fcy\u00fck projelerde, ba\u011f\u0131ml\u0131l\u0131k enjeksiyonunu y\u00f6netmek i\u00e7in Swinject veya Needle gibi bir DI konteyneri kullanmay\u0131 d\u00fc\u015f\u00fcnebilirsiniz. Bu ara\u00e7lar, ba\u011f\u0131ml\u0131l\u0131k grafi\u011fini olu\u015fturma ve y\u00f6netme s\u00fcrecini otomatikle\u015ftirebilir.\n  <\/div>\n<h2>\u0130leri D\u00fczey Konular: Combine, Hata Y\u00f6netimi ve Kaps\u00fclleme<\/h2>\n<p>ViewModel mimarisini temel d\u00fczeyde anlad\u0131ktan sonra, uygulamalar\u0131n\u0131z\u0131n daha sa\u011flam, esnek ve hatas\u0131z olmas\u0131n\u0131 sa\u011flayacak baz\u0131 ileri d\u00fczey konulara dalmak faydal\u0131 olacakt\u0131r. Bu b\u00f6l\u00fcmde, Swift'in reaktif programlama \u00e7er\u00e7evesi Combine'\u0131 ViewModel'lerde daha etkin kullanmay\u0131, g\u00fc\u00e7l\u00fc hata y\u00f6netimi stratejileri geli\u015ftirmeyi ve ViewModel'in i\u00e7 durumunu do\u011fru bir \u015fekilde kaps\u00fcllemeyi ele alaca\u011f\u0131z.<\/p>\n<h3>Combine ile Reaktif Veri Ak\u0131\u015flar\u0131<\/h3>\n<p>Combine, Apple'\u0131n reaktif programlama \u00e7er\u00e7evesidir ve zaman i\u00e7inde de\u011ferleri yayan, i\u015fleyen ve alan Publisher\/Subscriber modelini kullanarak e\u015f zamans\u0131z olaylar\u0131 y\u00f6netmeyi sa\u011flar. ViewModel'ler genellikle a\u011f istekleri, kullan\u0131c\u0131 etkile\u015fimleri veya sistem bildirimleri gibi e\u015f zamans\u0131z olaylarla ilgilendi\u011fi i\u00e7in Combine, bu olaylar\u0131 zarif ve okunabilir bir \u015fekilde ele almak i\u00e7in m\u00fckemmel bir ara\u00e7t\u0131r.<\/p>\n<p>\u00d6nceki \u00f6rneklerimizde <code>@Published<\/code> ve <code>sink<\/code> operat\u00f6r\u00fcn\u00fc zaten g\u00f6rd\u00fck. Ancak Combine'\u0131n g\u00fcc\u00fc, veri ak\u0131\u015flar\u0131n\u0131 birle\u015ftirmek, d\u00f6n\u00fc\u015ft\u00fcrmek ve filtrelemek i\u00e7in bir dizi operat\u00f6r sunmas\u0131nda yatar. \u00d6rne\u011fin, bir metin alan\u0131na girilen metnin belirli bir s\u00fcre sonra (debounce) bir arama iste\u011fini tetiklemesini istiyorsak:<\/p>\n<pre><code class=\"language-swift\">\nimport Foundation\nimport Combine\n\nclass SearchViewModel: ObservableObject {\n    @Published var searchText: String = \"\"\n    @Published var searchResults: [String] = []\n    @Published var isLoading: Bool = false\n    @Published var errorMessage: String?\n\n    private var cancellables = Set<AnyCancellable>()\n\n    init() {\n        \/\/ searchText'teki de\u011fi\u015fiklikleri izle\n        $searchText\n            .dropFirst() \/\/ Ba\u015flang\u0131\u00e7taki bo\u015f de\u011feri atla\n            .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main) \/\/ Yar\u0131m saniye beklet\n            .removeDuplicates() \/\/ Ard\u0131\u015f\u0131k ayn\u0131 de\u011ferleri atla\n            .flatMap { [weak self] text -> AnyPublisher<[String], Error> in\n                self?.isLoading = true\n                self?.errorMessage = nil\n                if text.isEmpty {\n                    return Just([]).setFailureType(to: Error.self).eraseToAnyPublisher()\n                }\n                return self?.performSearch(query: text) ?? Empty().eraseToAnyPublisher()\n            }\n            .receive(on: DispatchQueue.main)\n            .sink { [weak self] completion in\n                self?.isLoading = false\n                if case .failure(let error) = completion {\n                    self?.errorMessage = error.localizedDescription\n                    self?.searchResults = []\n                }\n            } receiveValue: { [weak self] results in\n                self?.searchResults = results\n            }\n            .store(in: &cancellables)\n    }\n\n    private func performSearch(query: String) -> AnyPublisher<[String], Error> {\n        \/\/ Ger\u00e7ek bir arama servisi \u00e7a\u011fr\u0131s\u0131 burada olurdu\n        print(\"Searching for: \\(query)\")\n        let mockResults = [\"Result for \\(query) 1\", \"Result for \\(query) 2\"]\n            .filter { $0.contains(query) } \/\/ Basit filtreleme\n        return Just(mockResults)\n            .delay(for: .seconds(0.3), scheduler: DispatchQueue.main) \/\/ API gecikmesi sim\u00fclasyonu\n            .setFailureType(to: Error.self)\n            .eraseToAnyPublisher()\n    }\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Bu \u00f6rnekte, <code>debounce<\/code> ve <code>removeDuplicates<\/code> gibi operat\u00f6rler sayesinde, kullan\u0131c\u0131 yazmay\u0131 bitirene kadar gereksiz API \u00e7a\u011fr\u0131lar\u0131 yapmaktan ka\u00e7\u0131n\u0131l\u0131r. <code>flatMap<\/code> ise her yeni arama metni i\u00e7in yeni bir publisher (arama iste\u011fi) olu\u015fturur ve sonucunu <code>searchResults<\/code>'e aktar\u0131r. Combine, bu t\u00fcr karma\u015f\u0131k reaktif ak\u0131\u015flar\u0131 kolayca y\u00f6netmenizi sa\u011flar.<\/p>\n<h3>Hata Y\u00f6netimi<\/h3>\n<p>Sa\u011flam bir uygulama, hata durumlar\u0131n\u0131 etkili bir \u015fekilde y\u00f6netebilmelidir. ViewModel'lerde hata y\u00f6netimi, View'e kullan\u0131c\u0131ya g\u00f6sterilebilecek anlaml\u0131 hata mesajlar\u0131 sa\u011flamakla ilgilidir. Combine ile Publisher'lar bir <code>Failure<\/code> t\u00fcr\u00fc tan\u0131mlad\u0131\u011f\u0131 i\u00e7in, hatalar\u0131 yakalamak ve i\u015flemek nispeten kolayd\u0131r. Yukar\u0131daki <code>SearchViewModel<\/code> \u00f6rne\u011finde, <code>errorMessage<\/code> \u00f6zelli\u011fi arac\u0131l\u0131\u011f\u0131yla hatalar\u0131 View'e bildiriyoruz.<\/p>\n<p>Daha sofistike bir hata y\u00f6netimi i\u00e7in, belirli hata t\u00fcrlerini yakalay\u0131p bunlara g\u00f6re farkl\u0131 aksiyonlar alabilirsiniz. \u00d6rne\u011fin, bir <code>mapError<\/code> operat\u00f6r\u00fc kullanarak \u00f6zel hata tiplerinizi d\u00f6n\u00fc\u015ft\u00fcrebilir veya <code>catch<\/code> operat\u00f6r\u00fc ile hatalar\u0131 yakalay\u0131p bir yedek Publisher sa\u011flayabilirsiniz.<\/p>\n<pre><code class=\"language-swift\">\n\/\/ \u00d6rnek bir custom error\nenum AppError: LocalizedError {\n    case networkError(Error)\n    case invalidData\n    case customError(String)\n\n    var errorDescription: String? {\n        switch self {\n        case .networkError(let error): return \"A\u011f hatas\u0131 olu\u015ftu: \\(error.localizedDescription)\"\n        case .invalidData: return \"Ge\u00e7ersiz veri format\u0131.\"\n        case .customError(let message): return message\n        }\n    }\n}\n\n\/\/ ViewModel i\u00e7inde hata i\u015fleme\n\/\/ ...\n    .mapError { error in\n        \/\/ Hata t\u00fcr\u00fcne g\u00f6re AppError'a d\u00f6n\u00fc\u015ft\u00fcr\n        if let urlError = error as? URLError {\n            return AppError.networkError(urlError)\n        }\n        return AppError.customError(\"Beklenmeyen bir hata olu\u015ftu: \\(error.localizedDescription)\")\n    }\n    .catch { error in\n        \/\/ Hata durumunda bir yedek de\u011fer d\u00f6nd\u00fcr (\u00f6rne\u011fin bo\u015f bir dizi) veya hi\u00e7 de\u011fer d\u00f6nd\u00fcrme\n        Just([]).setFailureType(to: AppError.self)\n    }\n    .sink { [weak self] completion in\n        if case .failure(let error) = completion {\n            self?.errorMessage = error.localizedDescription\n        }\n    } receiveValue: { \/* ... *\/ }\n\/\/ ...\n  <\/pre>\n<p><\/code><\/p>\n<h3>Kaps\u00fclleme (Encapsulation)<\/h3>\n<p>Kaps\u00fclleme, nesneye y\u00f6nelik programlaman\u0131n temel ilkelerinden biridir ve bir nesnenin i\u00e7 durumunu ve davran\u0131\u015f\u0131n\u0131 d\u0131\u015f d\u00fcnyadan gizlemekle ilgilidir. ViewModel'ler i\u00e7in bu, ViewModel'in i\u00e7 i\u015fleyi\u015finin (\u00f6rne\u011fin, \u00f6zel Combine cancellable set'leri, yard\u0131mc\u0131 fonksiyonlar) View veya ba\u015fka bir d\u0131\u015f bile\u015fen taraf\u0131ndan do\u011frudan eri\u015filemez olmas\u0131n\u0131 sa\u011flamak anlam\u0131na gelir.<\/p>\n<ul>\n<li><strong><code>private<\/code> veya <code>fileprivate<\/code> Kullan\u0131m\u0131:<\/strong> ViewModel i\u00e7indeki sadece ViewModel'in kullanmas\u0131 gereken \u00f6zellikleri ve metodlar\u0131 <code>private<\/code> veya <code>fileprivate<\/code> olarak i\u015faretlemek, d\u0131\u015far\u0131dan eri\u015fimi engeller. Bu, ViewModel'in sorumluluklar\u0131n\u0131 net bir \u015fekilde s\u0131n\u0131rlar ve istenmeyen yan etkileri \u00f6nler.<\/li>\n<li><strong><code>@Published<\/code> ile Sadece Gerekli Durumu A\u00e7\u0131\u011fa \u00c7\u0131karma:<\/strong> Yaln\u0131zca View'in g\u00f6zlemlemesi gereken \u00f6zellikleri <code>@Published<\/code> olarak i\u015faretleyin. \u0130\u00e7 mant\u0131kla ilgili ge\u00e7ici durumlar veya yard\u0131mc\u0131 de\u011fi\u015fkenler <code>private<\/code> kalmal\u0131d\u0131r.<\/li>\n<li><strong><code>AnyPublisher<\/code> Kullan\u0131m\u0131:<\/strong> Baz\u0131 durumlarda, ViewModel'den d\u0131\u015far\u0131ya bir Publisher d\u00f6nd\u00fcrmek isteyebilirsiniz (\u00f6rne\u011fin, bir eylemin tamamland\u0131\u011f\u0131n\u0131 bildirmek i\u00e7in). Bu durumda, <code>eraseToAnyPublisher()<\/code> kullanarak Publisher'\u0131n alt\u0131nda yatan somut tipini gizlemek iyi bir uygulamad\u0131r. Bu, ba\u011f\u0131ml\u0131l\u0131klar\u0131 azalt\u0131r ve daha esnek bir aray\u00fcz sa\u011flar.<\/li>\n<\/ul>\n<pre><code class=\"language-swift\">\nclass SecureDataViewModel: ObservableObject {\n    @Published private(set) var data: String? \/\/ Sadece ViewModel de\u011fi\u015ftirebilir\n    @Published var isLoading: Bool = false\n\n    private var internalSecretValue: String = \"Gizli Bilgi\" \/\/ D\u0131\u015far\u0131dan eri\u015filemez\n\n    func loadData() {\n        isLoading = true\n        \/\/ ... Veri y\u00fckleme mant\u0131\u011f\u0131\n        DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in\n            self?.data = \"Y\u00fcklenen Veri: \\(self?.internalSecretValue ?? \"\")\"\n            self?.isLoading = false\n        }\n    }\n}\n  <\/pre>\n<p><\/code><\/p>\n<p>Burada, <code>data<\/code> \u00f6zelli\u011fi d\u0131\u015far\u0131dan sadece okunabilir (<code>private(set)<\/code>), <code>internalSecretValue<\/code> ise tamamen gizlidir. Bu t\u00fcr kaps\u00fclleme pratikleri, ViewModel'lerinizi daha g\u00fcvenli, tutarl\u0131 ve bak\u0131m\u0131 kolay hale getirir. Bu ileri d\u00fczey teknikler, uygulaman\u0131z\u0131n kalitesini ve s\u00fcrd\u00fcr\u00fclebilirli\u011fini \u00f6nemli \u00f6l\u00e7\u00fcde art\u0131racakt\u0131r.<\/p>\n<h2>Ger\u00e7ek D\u00fcnya Senaryosu: Bir E-ticaret Uygulamas\u0131nda \u00dcr\u00fcn Detay Ekran\u0131<\/h2>\n<p>\u015eimdiye kadar edindi\u011fimiz bilgileri ger\u00e7ek bir senaryo \u00fczerinde uygulayal\u0131m: Bir e-ticaret uygulamas\u0131ndaki \"\u00dcr\u00fcn Detay Ekran\u0131\". Bu ekran, kullan\u0131c\u0131n\u0131n bir \u00fcr\u00fcn\u00fcn detaylar\u0131n\u0131 g\u00f6rmesini, sepete eklemesini ve favorilerine kaydetmesini sa\u011flar. Bu i\u015flevsellikler, ViewModel mimarisinin g\u00fcc\u00fcn\u00fc ve esnekli\u011fini g\u00f6stermek i\u00e7in harika bir \u00f6rnektir.<\/p>\n<h3>\u00dcr\u00fcn Detay Ekran\u0131n\u0131n Gereksinimleri ve ViewModel Sorumluluklar\u0131<\/h3>\n<p>Bir \u00fcr\u00fcn detay ekran\u0131 genellikle a\u015fa\u011f\u0131daki i\u015flevleri i\u00e7erir:<\/p>\n<ul>\n<li>Belirli bir \u00fcr\u00fcn\u00fcn ID'sine g\u00f6re detaylar\u0131n\u0131 (isim, fiyat, a\u00e7\u0131klama, resimler) API'den \u00e7ekme.<\/li>\n<li>\u00dcr\u00fcn\u00fcn y\u00fcklenme durumunu (loading, loaded, error) y\u00f6netme.<\/li>\n<li>\"Sepete Ekle\" d\u00fc\u011fmesine bas\u0131ld\u0131\u011f\u0131nda \u00fcr\u00fcn\u00fc sepete ekleme i\u015flemi.<\/li>\n<li>\"Favorilere Ekle\/Kald\u0131r\" d\u00fc\u011fmesine bas\u0131ld\u0131\u011f\u0131nda \u00fcr\u00fcn\u00fc favoriler listesine ekleme veya \u00e7\u0131karma.<\/li>\n<li>Hata durumlar\u0131nda kullan\u0131c\u0131ya uygun mesajlar g\u00f6sterme.<\/li>\n<li>Kullan\u0131c\u0131 etkile\u015fimleri sonucunda View'deki durumu g\u00fcncelleme.<\/li>\n<\/ul>\n<p>Bu gereksinimler do\u011frultusunda, <code>ProductDetailViewModel<\/code>'imiz a\u015fa\u011f\u0131daki sorumluluklar\u0131 \u00fcstlenmelidir:<\/p>\n<pre><code class=\"language-swift\">\n\/\/ Basit Model Tan\u0131mlar\u0131\nstruct Product: Identifiable, Codable {\n    let id: String\n    let name: String\n    let description: String\n    let price: Double\n    let imageUrl: String\n    var isFavorite: Bool = false\n}\n\nstruct CartItem: Identifiable, Codable {\n    let id = UUID()\n    let productId: String\n    let quantity: Int\n}\n\nenum ProductDetailError: LocalizedError {\n    case productNotFound\n    case networkError(Error)\n    case generalError(String)\n\n    var errorDescription: String? {\n        switch self {\n        case .productNotFound: return \"\u00dcr\u00fcn bulunamad\u0131.\"\n        case .networkError(let err): return \"A\u011f hatas\u0131: \\(err.localizedDescription)\"\n        case .generalError(let msg): return \"Hata: \\(msg)\"\n        }\n    }\n}\n\n\/\/ Servis Protokolleri (Ba\u011f\u0131ml\u0131l\u0131k Enjeksiyonu i\u00e7in)\nprotocol ProductServiceProtocol {\n    func fetchProduct(id: String) -> AnyPublisher<Product, Error>\n}\n\nprotocol CartServiceProtocol {\n    func addToCart(productId: String, quantity: Int) -> AnyPublisher<Void, Error>\n}\n\nprotocol FavoriteServiceProtocol {\n    func toggleFavorite(productId: String) -> AnyPublisher<Bool, Error> \/\/ true: favori oldu, false: favori kald\u0131r\u0131ld\u0131\n    func isProductFavorite(productId: String) -> AnyPublisher<Bool, Error>\n}\n\n\/\/ ViewModel Tan\u0131m\u0131\nclass ProductDetailViewModel: ObservableObject {\n    @Published private(set) var product: Product?\n    @Published private(set) var isLoading: Bool = false\n    @Published private(set) var errorMessage: String?\n    @Published private(set) var isAddedToCartSuccessfully: Bool = false\n    @Published private(set) var showAddedToCartConfirmation: Bool = false \/\/ UI i\u00e7in\n\n    private var productId: String\n    private let productService: ProductServiceProtocol\n    private let cartService: CartServiceProtocol\n    private let favoriteService: FavoriteServiceProtocol\n    private var cancellables = Set<AnyCancellable>()\n\n    init(productId: String,\n         productService: ProductServiceProtocol,\n         cartService: CartServiceProtocol,\n         favoriteService: FavoriteServiceProtocol) {\n        self.productId = productId\n        self.productService = productService\n        self.cartService = cartService\n        self.favoriteService = favoriteService\n        setupCombineSubscriptions()\n    }\n\n    private func setupCombineSubscriptions() {\n        \/\/ Favori durumu de\u011fi\u015fti\u011finde \u00fcr\u00fcn\u00fc g\u00fcncelle\n        $product\n            .compactMap { $0?.id }\n            .flatMap { [unowned self] id in\n                self.favoriteService.isProductFavorite(productId: id)\n                    .replaceError(with: false) \/\/ Hata durumunda varsay\u0131lan olarak favori de\u011fil say\n            }\n            .receive(on: DispatchQueue.main)\n            .sink { [weak self] isFavorite in\n                self?.product?.isFavorite = isFavorite\n            }\n            .store(in: &cancellables)\n    }\n\n    func loadProduct() {\n        isLoading = true\n        errorMessage = nil\n        productService.fetchProduct(id: productId)\n            .flatMap { [unowned self] fetchedProduct -> AnyPublisher<Product, Error> in\n                self.favoriteService.isProductFavorite(productId: fetchedProduct.id)\n                    .map { isFavorite in\n                        var p = fetchedProduct\n                        p.isFavorite = isFavorite\n                        return p\n                    }\n                    .mapError { error -> Error in\n                        \/\/ Favori servisi hatas\u0131n\u0131 ProductDetailError'a \u00e7evir\n                        ProductDetailError.networkError(error)\n                    }\n                    .eraseToAnyPublisher()\n            }\n            .receive(on: DispatchQueue.main)\n            .sink { [weak self] completion in\n                self?.isLoading = false\n                if case .failure(let error) = completion {\n                    self?.errorMessage = (error as? ProductDetailError)?.localizedDescription ?? error.localizedDescription\n                    self?.product = nil\n                }\n            } receiveValue: { [weak self] fetchedProduct in\n                self?.product = fetchedProduct\n            }\n            .store(in: &cancellables)\n    }\n\n    func addToCart(quantity: Int) {\n        guard let product = product else {\n            errorMessage = \"\u00dcr\u00fcn bilgisi mevcut de\u011fil.\"\n            return\n        }\n        isAddedToCartSuccessfully = false\n        cartService.addToCart(productId: product.id, quantity: quantity)\n            .receive(on: DispatchQueue.main)\n            .sink { [weak self] completion in\n                if case .failure(let error) = completion {\n                    self?.errorMessage = (error as? ProductDetailError)?.localizedDescription ?? error.localizedDescription\n                } else {\n                    self?.isAddedToCartSuccessfully = true\n                    self?.showAddedToCartConfirmation = true \/\/ UI bildirimi i\u00e7in\n                }\n            } receiveValue: { _ in }\n            .store(in: &cancellables)\n    }\n\n    func toggleFavorite() {\n        guard let product = product else {\n            errorMessage = \"\u00dcr\u00fcn bilgisi mevcut de\u011fil.\"\n            return\n        }\n        favoriteService.toggleFavorite(productId: product.id)\n            .receive(on: DispatchQueue.main)\n            .sink { [weak self] completion in\n                if case .failure(let error) = completion {\n                    self?.errorMessage = (error as? ProductDetailError)?.localizedDescription ?? error.localizedDescription\n                }\n            } receiveValue: { [weak self] isFavorite in\n                self?.product?.isFavorite = isFavorite\n            }\n            .store(in: &cancellables)\n    }\n}\n  <\/pre>\n<p><\/code><\/p>\n<h3>A\u00e7\u0131klama:<\/h3>\n<ul>\n<li><strong>Ba\u011f\u0131ml\u0131l\u0131k Enjeksiyonu:<\/strong> ViewModel, <code>ProductServiceProtocol<\/code>, <code>CartServiceProtocol<\/code> ve <code>FavoriteServiceProtocol<\/code> t\u00fcr\u00fcnden servisleri initializer arac\u0131l\u0131\u011f\u0131yla al\u0131r. Bu, ViewModel'in test edilebilirli\u011fini art\u0131r\u0131r ve farkl\u0131 servis implementasyonlar\u0131 kullan\u0131lmas\u0131na olanak tan\u0131r (\u00f6rne\u011fin, sahte servisler veya farkl\u0131 API s\u00fcr\u00fcmleri).<\/li>\n<li><strong>Combine ile Veri Ak\u0131\u015f\u0131:<\/strong> <code>loadProduct()<\/code> metodu, <code>productService<\/code>'den \u00fcr\u00fcn detaylar\u0131n\u0131 \u00e7eker ve ard\u0131ndan <code>favoriteService<\/code>'den \u00fcr\u00fcn\u00fcn favori olup olmad\u0131\u011f\u0131n\u0131 kontrol eder. Bu iki e\u015f zamans\u0131z i\u015flemi <code>flatMap<\/code> kullanarak birle\u015ftiririz. B\u00f6ylece, View'e tek bir \u00fcr\u00fcn objesi yans\u0131t\u0131lmadan \u00f6nce gerekli t\u00fcm bilgiler toplanm\u0131\u015f olur.<\/li>\n<li><strong>Hata Y\u00f6netimi:<\/strong> Potansiyel hatalar (<code>ProductDetailError<\/code> enum'\u0131 ile tan\u0131mlanm\u0131\u015ft\u0131r) Combine zinciri i\u00e7inde yakalan\u0131r ve <code>errorMessage<\/code> \u00f6zelli\u011fine atanarak View'e bildirim sa\u011flan\u0131r.<\/li>\n<li><strong>Durum Y\u00f6netimi:<\/strong> <code>isLoading<\/code>, <code>errorMessage<\/code>, <code>isAddedToCartSuccessfully<\/code> gibi <code>@Published<\/code> \u00f6zellikler, View'in ViewModel'in durum de\u011fi\u015fikliklerine reaktif olarak tepki vermesini sa\u011flar (\u00f6rne\u011fin, y\u00fcklenirken bir spinner g\u00f6stermek, hata mesaj\u0131 yay\u0131nlamak, sepete ekleme onay\u0131 g\u00f6stermek).<\/li>\n<li><strong>Kaps\u00fclleme:<\/strong> <code>product<\/code>, <code>isLoading<\/code> ve <code>errorMessage<\/code> gibi \u00f6zellikler <code>private(set)<\/code> ile i\u015faretlenerek sadece ViewModel'in i\u00e7inden de\u011fi\u015ftirilebilir olmalar\u0131 sa\u011flan\u0131r, d\u0131\u015far\u0131dan sadece okunabilirler. Bu, ViewModel'in durumunun kontroll\u00fc bir \u015fekilde y\u00f6netilmesini sa\u011flar.<\/li>\n<\/ul>\n<p>Bu ViewModel yap\u0131s\u0131, \u00fcr\u00fcn detay ekran\u0131 gibi karma\u015f\u0131k bir UI i\u00e7in temiz, test edilebilir ve s\u00fcrd\u00fcr\u00fclebilir bir \u00e7\u00f6z\u00fcm sunar. Her bir sorumluluk a\u00e7\u0131k\u00e7a tan\u0131mlanm\u0131\u015ft\u0131r ve ViewModel, View'in sadece g\u00f6rselle\u015ftirmeyle ilgilenmesini sa\u011flarken, t\u00fcm i\u015f mant\u0131\u011f\u0131n\u0131 kendi i\u00e7inde kaps\u00fcller. Bu, \u00f6zellikle b\u00fcy\u00fck ekiplerde ve zamanla geli\u015fen uygulamalarda kod taban\u0131n\u0131n y\u00f6netilmesini \u00f6nemli \u00f6l\u00e7\u00fcde kolayla\u015ft\u0131r\u0131r.<\/p>\n<h2>Sonu\u00e7: Neden Yeniden Kullan\u0131labilir ViewModel Mimarisi SwiftUI Geli\u015ftirmenizi D\u00f6n\u00fc\u015ft\u00fcrecek?<\/h2>\n<p>SwiftUI, deklaratif yap\u0131s\u0131 ve h\u0131zl\u0131 geli\u015ftirme yetenekleriyle mobil uygulama d\u00fcnyas\u0131nda devrim yarat\u0131yor. Ancak bu g\u00fcc\u00fcn tam potansiyelini a\u00e7\u0131\u011fa \u00e7\u0131karmak ve karma\u015f\u0131k projeleri y\u00f6netilebilir k\u0131lmak i\u00e7in sa\u011flam bir mimari temel \u015fartt\u0131r. Yeniden Kullan\u0131labilir ViewModel Mimarisi, tam da bu ihtiyaca cevap vererek, SwiftUI geli\u015ftirme prati\u011finizi k\u00f6kten d\u00f6n\u00fc\u015ft\u00fcrme potansiyeli ta\u015f\u0131r.<\/p>\n<p>Bu makalede g\u00f6rd\u00fc\u011f\u00fcm\u00fcz gibi, ViewModel'ler View'leri i\u015f mant\u0131\u011f\u0131ndan ve durum y\u00f6netiminden ay\u0131rarak, daha temiz ve okunabilir bir kod taban\u0131 olu\u015fturman\u0131n anahtar\u0131n\u0131 sunar. Bu ayr\u0131m, View'lerinizin sadece g\u00f6rsel sunuma odaklanmas\u0131n\u0131 sa\u011flarken, i\u015f mant\u0131\u011f\u0131n\u0131z\u0131n ba\u011f\u0131ms\u0131z ve test edilebilir bir katmanda ya\u015famas\u0131n\u0131 temin eder. Protokoller ve jenerikler arac\u0131l\u0131\u011f\u0131yla sa\u011flanan yeniden kullan\u0131labilirlik, ayn\u0131 mant\u0131\u011f\u0131 farkl\u0131 View'ler veya veri tipleri i\u00e7in tekrar tekrar yazma ihtiyac\u0131n\u0131 ortadan kald\u0131r\u0131r, bu da geli\u015ftirme h\u0131z\u0131n\u0131 art\u0131r\u0131r ve kod tekrar\u0131n\u0131 azalt\u0131r. Ba\u011f\u0131ml\u0131l\u0131k Enjeksiyonu, ViewModel'lerinizi daha esnek, mod\u00fcler ve \u00f6zellikle birim testleri i\u00e7in son derece uygun hale getirir.<\/p>\n<p>Combine \u00e7er\u00e7evesi ile ViewModel'leriniz reaktif veri ak\u0131\u015flar\u0131n\u0131 ve e\u015f zamans\u0131z i\u015flemleri zarif bir \u015fekilde y\u00f6netebilir, b\u00f6ylece karma\u015f\u0131k UI durumlar\u0131 ve a\u011f etkile\u015fimleri daha \u00f6ng\u00f6r\u00fclebilir hale gelir. Kaps\u00fclleme prensipleri ise ViewModel'lerinizin i\u00e7 durumunu koruyarak, uygulama genelinde tutarl\u0131l\u0131\u011f\u0131 ve veri g\u00fcvenli\u011fini sa\u011flar. T\u00fcm bu bile\u015fenler bir araya geldi\u011finde, sadece mevcut projelerinizi daha iyi y\u00f6netmekle kalmayacak, ayn\u0131 zamanda gelecekteki de\u011fi\u015fikliklere ve yeni \u00f6zelliklere \u00e7ok daha kolay adapte olabilecek bir yap\u0131 in\u015fa edeceksiniz.<\/p>\n<p>\u00d6zetle, Yeniden Kullan\u0131labilir ViewModel Mimarisi:<\/p>\n<ul>\n<li><strong>Temiz Kod:<\/strong> View ve i\u015f mant\u0131\u011f\u0131n\u0131 ay\u0131rarak kod okunabilirli\u011fini art\u0131r\u0131r.<\/li>\n<li><strong>Test Edilebilirlik:<\/strong> ViewModel'lerinizi ba\u011f\u0131ms\u0131z olarak test etmenizi sa\u011flar, hata yakalama oran\u0131n\u0131 y\u00fckseltir.<\/li>\n<li><strong>\u00d6l\u00e7eklenebilirlik:<\/strong> Uygulaman\u0131z b\u00fcy\u00fcd\u00fck\u00e7e bile\u015fenlerinizi y\u00f6netmeyi kolayla\u015ft\u0131r\u0131r.<\/li>\n<li><strong>Geli\u015ftirme H\u0131z\u0131:<\/strong> Kod tekrar\u0131n\u0131 azalt\u0131r ve mod\u00fcler yap\u0131s\u0131yla yeni \u00f6zellik eklemeyi h\u0131zland\u0131r\u0131r.<\/li>\n<li><strong>Ekip \u00c7al\u0131\u015fmas\u0131:<\/strong> Sorumluluklar\u0131n net ayr\u0131m\u0131 sayesinde ekiplerin paralel \u00e7al\u0131\u015fmas\u0131n\u0131 kolayla\u015ft\u0131r\u0131r.<\/li>\n<\/ul>\n<p>SwiftUI ile kaliteli, performansl\u0131 ve s\u00fcrd\u00fcr\u00fclebilir uygulamalar geli\u015ftirmek isteyen her geli\u015ftirici i\u00e7in ViewModel mimarisi vazge\u00e7ilmez bir ara\u00e7t\u0131r. Bu prensipleri benimseyerek, projelerinizin gelece\u011fe haz\u0131r oldu\u011fundan emin olabilir, geli\u015ftirme s\u00fcrecinizde daha az sorunla kar\u015f\u0131la\u015farak daha verimli sonu\u00e7lar elde edebilirsiniz.<\/p>\n<h3>S\u0131k\u00e7a Sorulan Sorular (SSS)<\/h3>\n<ul>\n<li>\n          <strong>S: Her SwiftUI View'i i\u00e7in ayr\u0131 bir ViewModel gerekli midir?<\/strong><\/p>\n<p>C: Hay\u0131r, her View i\u00e7in ayr\u0131 bir ViewModel gerekli de\u011fildir. Basit, sadece sunum ama\u00e7l\u0131 (statik metinler, basit g\u00f6rsel \u00f6\u011feler) View'ler i\u00e7in ViewModel'e ihtiya\u00e7 duyulmayabilir. ViewModel, genellikle i\u015f mant\u0131\u011f\u0131, durum y\u00f6netimi veya veri i\u015fleme gerektiren View'ler i\u00e7in kullan\u0131l\u0131r. K\u00fc\u00e7\u00fck bile\u015fenler, daha b\u00fcy\u00fck bir View'in ViewModel'ini ortak kullanabilir veya sadece <code>@State<\/code> gibi yerel durum y\u00f6netim ara\u00e7lar\u0131yla yetinebilir.<\/p>\n<\/li>\n<li>\n          <strong>S: ViewModel'ler aras\u0131nda nas\u0131l ileti\u015fim kurulur?<\/strong><\/p>\n<p>C: ViewModel'ler aras\u0131nda ileti\u015fim kurman\u0131n birka\u00e7 yolu vard\u0131r:<\/p>\n<ul>\n<li><strong>Delegasyon (Delegation):<\/strong> Bir ViewModel'in ba\u015fka bir ViewModel'e veya servise bir eylemin tamamland\u0131\u011f\u0131n\u0131 veya bir durumun de\u011fi\u015fti\u011fini bildirmesi i\u00e7in bir protokol arac\u0131l\u0131\u011f\u0131yla delege paterni kullan\u0131labilir.<\/li>\n<li><strong>Combine Publishers:<\/strong> Bir ViewModel, <code>@Published<\/code> \u00f6zelli\u011fini veya <code>PassthroughSubject<\/code> gibi bir Publisher'\u0131 d\u0131\u015far\u0131ya a\u00e7arak di\u011fer ViewModel'lerin veya View'lerin bu de\u011fi\u015fiklikleri g\u00f6zlemlemesini sa\u011flayabilir.<\/li>\n<li><strong>Environment Objects:<\/strong> Bir ViewModel'i <code>environmentObject<\/code> olarak tan\u0131mlayarak, View hiyerar\u015fisinde daha alt seviyedeki View'lerin veya di\u011fer ViewModel'lerin ona eri\u015fmesini sa\u011flayabilirsiniz. Bu, genellikle global veya uygulama \u00e7ap\u0131ndaki durumlar i\u00e7in uygundur.<\/li>\n<li><strong>Initializer Enjeksiyonu:<\/strong> Bir ViewModel, ba\u015fka bir ViewModel'i ba\u011f\u0131ml\u0131l\u0131k olarak alabilir, bu da do\u011frudan bir ileti\u015fim yolu sa\u011flar.<\/li>\n<\/ul>\n<\/li>\n<li>\n          <strong>S: ViewModel'lerde UI mant\u0131\u011f\u0131 olmal\u0131 m\u0131d\u0131r?<\/strong><\/p>\n<p>C: Genellikle hay\u0131r. ViewModel'ler, View'in g\u00f6sterim mant\u0131\u011f\u0131n\u0131 (presentation logic) ve durumunu y\u00f6netir ancak do\u011frudan UI elementlerini (\u00f6rne\u011fin, bir d\u00fc\u011fmenin rengi, bir font boyutu) veya onlar\u0131n yerle\u015fimini kontrol etmemelidir. ViewModel, View'in \"ne\" g\u00f6sterece\u011fini belirlerken, View \"nas\u0131l\" g\u00f6sterece\u011fini belirlemelidir. \u00d6rne\u011fin, bir hata mesaj\u0131n\u0131n i\u00e7eri\u011fini ViewModel sa\u011flar, ancak bu mesaj\u0131n k\u0131rm\u0131z\u0131 renkte ve b\u00fcy\u00fck fontla g\u00f6sterilmesi View'in sorumlulu\u011fundad\u0131r. Ancak, baz\u0131 durumlar (\u00f6rne\u011fin, bir navigasyon ak\u0131\u015f\u0131n\u0131 tetiklemek gibi) ViewModel'den View'e dolayl\u0131 bir UI koordinasyonu gerektirebilir.<\/p>\n<\/li>\n<li>\n          <strong>S: ViewModel kullan\u0131m\u0131 uygulaman\u0131n performans\u0131n\u0131 etkiler mi?<\/strong><\/p>\n<p>C: Do\u011fru \u015fekilde uyguland\u0131\u011f\u0131nda, ViewModel kullan\u0131m\u0131 uygulaman\u0131n performans\u0131n\u0131 olumsuz etkilemez, aksine iyile\u015ftirebilir. ViewModel'ler, i\u015f mant\u0131\u011f\u0131n\u0131 ve durum y\u00f6netimini View'den ay\u0131rarak, View'lerin daha hafif olmas\u0131n\u0131 sa\u011flar. <code>@ObservableObject<\/code> ve <code>@Published<\/code> mekanizmalar\u0131 olduk\u00e7a optimize edilmi\u015ftir ve yaln\u0131zca ilgili View'lerin de\u011fi\u015fen verilere tepki olarak yeniden \u00e7izilmesini sa\u011flar. A\u015f\u0131r\u0131 karma\u015f\u0131k ViewModel'ler veya gereksiz yere \u00e7ok say\u0131da Publisher kullan\u0131m\u0131 potansiyel olarak performans sorunlar\u0131na yol a\u00e7abilir, ancak bu genellikle k\u00f6t\u00fc tasar\u0131mdan kaynaklan\u0131r, ViewModel mimarisinin kendisinden de\u011fil. Optimize edilmi\u015f Combine operat\u00f6rleri ve ak\u0131ll\u0131 durum y\u00f6netimi ile performans sorunlar\u0131 minimuma indirilebilir.<\/p>\n<\/li>\n<\/ul>\n<p><\/body><\/p>\n","protected":false},"excerpt":{"rendered":"Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test&hellip;","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_page_header_type":"","csco_page_load_nextpost":"","csco_page_subscribe_form":"","csco_page_contact_form":"","footnotes":""},"categories":[1],"tags":[],"class_list":{"0":"post-35563","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-genel","7":"cs-entry","8":"cs-video-wrap"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.5 (Yoast SEO v25.3.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik<\/title>\n<meta name=\"description\" content=\"Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test edilebilirli\u011fi kritik \u00f6nem ta\u015f\u0131r. Ancak, geli\u015ftirme s\u00fcrecinde h\u0131zla b\u00fcy\u00fcyen projelerde, tek bir g\u00f6r\u00fcn\u00fcm (View) dosyas\u0131na y\u0131\u011f\u0131lan i\u015f mant\u0131\u011f\u0131 (business logic), durum y\u00f6netimi (state management) ve veri i\u015fleme g\u00f6revleri, k\u0131sa s\u00fcrede b\u00fcy\u00fck bir karma\u015faya yol a\u00e7abilir. Bu durum, hem yeni \u00f6zellik eklemeyi zorla\u015ft\u0131r\u0131r hem de hatalar\u0131n tespit edilmesini ve d\u00fczeltilmesini imkans\u0131z hale getirebilir. \u0130\u015fte tam da bu noktada, \u201cYeniden Kullan\u0131labilir ViewModel Mimarisi\u201d devreye girerek, SwiftUI uygulamalar\u0131n\u0131z\u0131 daha organize, esnek ve \u00f6l\u00e7eklenebilir hale getirmenin anahtar\u0131n\u0131 sunar.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik\" \/>\n<meta property=\"og:description\" content=\"Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test edilebilirli\u011fi kritik \u00f6nem ta\u015f\u0131r. Ancak, geli\u015ftirme s\u00fcrecinde h\u0131zla b\u00fcy\u00fcyen projelerde, tek bir g\u00f6r\u00fcn\u00fcm (View) dosyas\u0131na y\u0131\u011f\u0131lan i\u015f mant\u0131\u011f\u0131 (business logic), durum y\u00f6netimi (state management) ve veri i\u015fleme g\u00f6revleri, k\u0131sa s\u00fcrede b\u00fcy\u00fck bir karma\u015faya yol a\u00e7abilir. Bu durum, hem yeni \u00f6zellik eklemeyi zorla\u015ft\u0131r\u0131r hem de hatalar\u0131n tespit edilmesini ve d\u00fczeltilmesini imkans\u0131z hale getirebilir. \u0130\u015fte tam da bu noktada, \u201cYeniden Kullan\u0131labilir ViewModel Mimarisi\u201d devreye girerek, SwiftUI uygulamalar\u0131n\u0131z\u0131 daha organize, esnek ve \u00f6l\u00e7eklenebilir hale getirmenin anahtar\u0131n\u0131 sunar.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/\" \/>\n<meta property=\"og:site_name\" content=\"Kodlar\u0131n Gizemli D\u00fcnyas\u0131\" \/>\n<meta property=\"article:published_time\" content=\"2025-12-01T02:31:15+00:00\" \/>\n<meta name=\"author\" content=\"Fatih Soysal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"Fatih Soysal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"31 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/\"},\"author\":{\"name\":\"Fatih Soysal\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"headline\":\"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik\",\"datePublished\":\"2025-12-01T02:31:15+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/\"},\"wordCount\":4679,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#respond\"]}],\"copyrightYear\":\"2025\",\"copyrightHolder\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/\",\"name\":\"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\"},\"datePublished\":\"2025-12-01T02:31:15+00:00\",\"description\":\"Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test edilebilirli\u011fi kritik \u00f6nem ta\u015f\u0131r. Ancak, geli\u015ftirme s\u00fcrecinde h\u0131zla b\u00fcy\u00fcyen projelerde, tek bir g\u00f6r\u00fcn\u00fcm (View) dosyas\u0131na y\u0131\u011f\u0131lan i\u015f mant\u0131\u011f\u0131 (business logic), durum y\u00f6netimi (state management) ve veri i\u015fleme g\u00f6revleri, k\u0131sa s\u00fcrede b\u00fcy\u00fck bir karma\u015faya yol a\u00e7abilir. Bu durum, hem yeni \u00f6zellik eklemeyi zorla\u015ft\u0131r\u0131r hem de hatalar\u0131n tespit edilmesini ve d\u00fczeltilmesini imkans\u0131z hale getirebilir. \u0130\u015fte tam da bu noktada, \u201cYeniden Kullan\u0131labilir ViewModel Mimarisi\u201d devreye girerek, SwiftUI uygulamalar\u0131n\u0131z\u0131 daha organize, esnek ve \u00f6l\u00e7eklenebilir hale getirmenin anahtar\u0131n\u0131 sunar.\",\"breadcrumb\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Anasayfa\",\"item\":\"https:\/\/fatihsoysal.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/\",\"name\":\"Fatihsoysal.com\",\"description\":\"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim\",\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\",\"name\":\"Fatih Soysal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"contentUrl\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"width\":512,\"height\":512,\"caption\":\"Fatih Soysal\"},\"logo\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\"},\"description\":\"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik","description":"Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test edilebilirli\u011fi kritik \u00f6nem ta\u015f\u0131r. Ancak, geli\u015ftirme s\u00fcrecinde h\u0131zla b\u00fcy\u00fcyen projelerde, tek bir g\u00f6r\u00fcn\u00fcm (View) dosyas\u0131na y\u0131\u011f\u0131lan i\u015f mant\u0131\u011f\u0131 (business logic), durum y\u00f6netimi (state management) ve veri i\u015fleme g\u00f6revleri, k\u0131sa s\u00fcrede b\u00fcy\u00fck bir karma\u015faya yol a\u00e7abilir. Bu durum, hem yeni \u00f6zellik eklemeyi zorla\u015ft\u0131r\u0131r hem de hatalar\u0131n tespit edilmesini ve d\u00fczeltilmesini imkans\u0131z hale getirebilir. \u0130\u015fte tam da bu noktada, \u201cYeniden Kullan\u0131labilir ViewModel Mimarisi\u201d devreye girerek, SwiftUI uygulamalar\u0131n\u0131z\u0131 daha organize, esnek ve \u00f6l\u00e7eklenebilir hale getirmenin anahtar\u0131n\u0131 sunar.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/","og_locale":"tr_TR","og_type":"article","og_title":"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik","og_description":"Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test edilebilirli\u011fi kritik \u00f6nem ta\u015f\u0131r. Ancak, geli\u015ftirme s\u00fcrecinde h\u0131zla b\u00fcy\u00fcyen projelerde, tek bir g\u00f6r\u00fcn\u00fcm (View) dosyas\u0131na y\u0131\u011f\u0131lan i\u015f mant\u0131\u011f\u0131 (business logic), durum y\u00f6netimi (state management) ve veri i\u015fleme g\u00f6revleri, k\u0131sa s\u00fcrede b\u00fcy\u00fck bir karma\u015faya yol a\u00e7abilir. Bu durum, hem yeni \u00f6zellik eklemeyi zorla\u015ft\u0131r\u0131r hem de hatalar\u0131n tespit edilmesini ve d\u00fczeltilmesini imkans\u0131z hale getirebilir. \u0130\u015fte tam da bu noktada, \u201cYeniden Kullan\u0131labilir ViewModel Mimarisi\u201d devreye girerek, SwiftUI uygulamalar\u0131n\u0131z\u0131 daha organize, esnek ve \u00f6l\u00e7eklenebilir hale getirmenin anahtar\u0131n\u0131 sunar.","og_url":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/","og_site_name":"Kodlar\u0131n Gizemli D\u00fcnyas\u0131","article_published_time":"2025-12-01T02:31:15+00:00","author":"Fatih Soysal","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"Fatih Soysal","Tahmini okuma s\u00fcresi":"31 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#article","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/"},"author":{"name":"Fatih Soysal","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"headline":"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik","datePublished":"2025-12-01T02:31:15+00:00","mainEntityOfPage":{"@id":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/"},"wordCount":4679,"commentCount":0,"publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#respond"]}],"copyrightYear":"2025","copyrightHolder":{"@id":"https:\/\/fatihsoysal.com\/blog\/#organization"}},{"@type":"WebPage","@id":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/","url":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/","name":"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/#website"},"datePublished":"2025-12-01T02:31:15+00:00","description":"Modern mobil uygulama geli\u015ftirme s\u00fcre\u00e7lerinde, \u00f6zellikle de SwiftUI gibi deklaratif UI \u00e7er\u00e7evelerini kullan\u0131rken, kodun okunabilirli\u011fi, s\u00fcrd\u00fcr\u00fclebilirli\u011fi ve test edilebilirli\u011fi kritik \u00f6nem ta\u015f\u0131r. Ancak, geli\u015ftirme s\u00fcrecinde h\u0131zla b\u00fcy\u00fcyen projelerde, tek bir g\u00f6r\u00fcn\u00fcm (View) dosyas\u0131na y\u0131\u011f\u0131lan i\u015f mant\u0131\u011f\u0131 (business logic), durum y\u00f6netimi (state management) ve veri i\u015fleme g\u00f6revleri, k\u0131sa s\u00fcrede b\u00fcy\u00fck bir karma\u015faya yol a\u00e7abilir. Bu durum, hem yeni \u00f6zellik eklemeyi zorla\u015ft\u0131r\u0131r hem de hatalar\u0131n tespit edilmesini ve d\u00fczeltilmesini imkans\u0131z hale getirebilir. \u0130\u015fte tam da bu noktada, \u201cYeniden Kullan\u0131labilir ViewModel Mimarisi\u201d devreye girerek, SwiftUI uygulamalar\u0131n\u0131z\u0131 daha organize, esnek ve \u00f6l\u00e7eklenebilir hale getirmenin anahtar\u0131n\u0131 sunar.","breadcrumb":{"@id":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fatihsoysal.com\/blog\/swiftui-icin-yeniden-kullanilabilir-viewmodel-mimarisi-temiz-kod-ve-olceklenebilirlik\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Anasayfa","item":"https:\/\/fatihsoysal.com\/blog\/"},{"@type":"ListItem","position":2,"name":"SwiftUI i\u00e7in Yeniden Kullan\u0131labilir ViewModel Mimarisi: Temiz Kod ve \u00d6l\u00e7eklenebilirlik"}]},{"@type":"WebSite","@id":"https:\/\/fatihsoysal.com\/blog\/#website","url":"https:\/\/fatihsoysal.com\/blog\/","name":"Fatihsoysal.com","description":"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim","publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1","name":"Fatih Soysal","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","contentUrl":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","width":512,"height":512,"caption":"Fatih Soysal"},"logo":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/"},"description":"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.","url":"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/"}]}},"yoast_meta":{"yoast_wpseo_title":"","yoast_wpseo_metadesc":"","yoast_wpseo_canonical":""},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/35563","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/comments?post=35563"}],"version-history":[{"count":0,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/35563\/revisions"}],"wp:attachment":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/media?parent=35563"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/categories?post=35563"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/tags?post=35563"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}