ViewModel에서 데이터를 관리하고, RxSwift를 활용해 View와 데이터를 바인딩.
NetworkManager를 통해 Firebase로부터 데이터를 가져오는 비동기 로직 작성.
import Foundation
import RxSwift
import RxCocoa
class VocabularyViewModel {
// Outputs
let categories: BehaviorRelay<[Category]> = BehaviorRelay(value: [])
let errorMessage: PublishRelay<String> = PublishRelay()
// DisposeBag
private let disposeBag = DisposeBag()
// Fetch categories from NetworkManager
func fetchCategories() {
NetworkManager.shared.fetchCategories()
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] categories in
self?.categories.accept(categories)
}, onError: { [weak self] error in
self?.errorMessage.accept(error.localizedDescription)
})
.disposed(by: disposeBag)
}
}
SnapKit으로 레이아웃 구성.
RxSwift를 활용하여 ViewModel과 UI 데이터 바인딩.
import UIKit
import RxSwift
import RxCocoa
import SnapKit
class VocabularyViewController: UIViewController {
private let tableView = UITableView()
private let viewModel = VocabularyViewModel()
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
bindViewModel()
viewModel.fetchCategories()
}
private func setupUI() {
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "CategoryCell")
}
private func bindViewModel() {
// Bind categories to table view
viewModel.categories
.bind(to: tableView.rx.items(cellIdentifier: "CategoryCell", cellType: UITableViewCell.self)) { row, category, cell in
cell.textLabel?.text = category.name
}
.disposed(by: disposeBag)
// Bind error messages
viewModel.errorMessage
.subscribe(onNext: { [weak self] message in
self?.showErrorAlert(message: message)
})
.disposed(by: disposeBag)
}
private func showErrorAlert(message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
Firebase Realtime Database에서 데이터를 가져오기 위한 NetworkManager 작성.
import Foundation
import RxSwift
class NetworkManager {
static let shared = NetworkManager()
private let baseURL = "https://iosvocabulary-default-rtdb.firebaseio.com/"
func fetchCategories() -> Observable<[Category]> {
return Observable.create { observer in
let url = URL(string: "\(self.baseURL)categories.json")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
observer.onError(error)
return
}
guard let data = data else {
observer.onError(NSError(domain: "DataError", code: -1, userInfo: nil))
return
}
do {
let categories = try JSONDecoder().decode([String: Category].self, from: data).map { $0.value }
observer.onNext(categories)
observer.onCompleted()
} catch {
observer.onError(error)
}
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
}