İçeriğe geç

RxSwift + MVVM kullanımı

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 ModelViewViewModel 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
    }
}

 

Tarih:RxSwift

Bu yazı yorumlara kapalı.

Copyright © 2020 Kenan Atmaca