Merhabalar bu yazımda RxSwift ile MVVM tasarım desenini nasıl kullanırız ? sorusunun yanıtını bulacağınız bir örnek vereceğim.
İlk olarak kısaca MVVM tasarım deseni nedir bunu anlatayım. Bildiğimiz gibi popüler bir çok tasarım deseni bulunmaktadır. Hiç şüphesiz bunlardan biride MVVM tasarım desenidir. Bu desen Model – View – ViewModel yapılarından oluşur ve uygulama bu yapılara göre şekillenir.
Yukardaki şekilde gördüğünüz gibi mantığını çok güzel bir şekilde aktarmış. Bu MVVM yapısında MVC yapısındaki gibi bir Mode yapısı bulunmakta. Burada sunucudan gelen datalar modellenmekte veya kendi oluşturacağınız data sınıfları burada bulunmaktadır. ViewModel yapısıda arayüz ile Model arasındaki köprüdür. Model ile elde ettiğimiz datalar işlenerek View katmanı için şekillendirilir, hazırlanır. View katmanı sadece ViewModel ile haberleşir. ViewModel ile düzenlenen data View üzerinde gerekli UI işlemleri yardımıyla kullanıcılara iletilir. Önemli bir durum View kesinlikle Model sınıfından haberdar değildir. Sadece ViewModel sınıfını kullanarak işlem yapar.
Yararları kısmına gelirsek yazılım geliştiriciler ve arayüz tasarımcıları aynı proje üzerinde birbirinden bağımsız bir şekilde eş zamanlı olarak çalışabilme imkanını yakalar.
Görsel arayüzün ve kodun birbirinden ayrışması uygulamanın test edilebilirliğini arttırmakta ve TDD (Test Driven Development) açısından da kolaylıklar sağlamaktadır. Aynı zamanda bu ayrışımlar uygulamamızın bakımı da kolaylaştırmış oluyor. Model tarafında bir değişiklik yapılmak istendiğinde UI tarafında hiçbir değişiklik yapılmasına gerek kalmadan geliştirmeler rahatlıkla yapılabilir.
Aşağıdaki örnek ile RxSwift kullanarak basit bir api üzerinden veri alıp UITableView üzerinde gösterme işlemi yapmakta.
Model:
struct Repistory { let name:String let url:String }
ViewModel:
import UIKit import RxCocoa import RxSwift struct ViewModel { var searchText = Variable("") let disposeBag = DisposeBag() let apiProvider:Api var data:Driver<[Repistory]> init(api:Api) { self.apiProvider = api data = searchText.asObservable() .throttle(0.3, scheduler: MainScheduler.instance) .distinctUntilChanged() .flatMapLatest { api.getRepistories($0) }.asDriver(onErrorJustReturn: []) } } class Api { func getRepistories(_ githubId:String) -> Observable<[Repistory]> { guard !githubId.isEmpty, let url = URL(string: "https://api.github.com/users/\(githubId)/repos") else {return Observable.just([])} return URLSession.shared .rx.json(request: URLRequest(url: url)) .retry(1) .map { var repistories = [Repistory]() if let items = $0 as? [[String:AnyObject]] { items.forEach({ (data) in guard let name = data["name"] as? String, let url = data["html_url"] as? String else {return} repistories.append(Repistory(name: name, url: url)) }) } return repistories } } }
View:
import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! let disposeBag = DisposeBag() var searchController:UISearchController! var searchBar:UISearchBar { return searchController.searchBar } var repoViewModel:ViewModel? var apiProvider = Api() override func viewDidLoad() { super.viewDidLoad() setupSearchBar() repoViewModel = ViewModel(api: self.apiProvider) if let viewModel = repoViewModel { viewModel.data.drive(tableView.rx.items(cellIdentifier: "myCell")) { _ , repo, cell in cell.textLabel?.text = repo.name cell.detailTextLabel?.text = repo.url }.disposed(by: disposeBag) searchBar.rx.text.orEmpty.bind(to: viewModel.searchText).disposed(by: disposeBag) } } func setupSearchBar() { searchController = UISearchController(searchResultsController: nil) searchController.obscuresBackgroundDuringPresentation = false searchBar.showsCancelButton = true searchBar.placeholder = "Username" self.tableView.tableHeaderView = searchController.searchBar definesPresentationContext = true } }
Bu yazı yorumlara kapalı.