단어장앱 ViewModel 설계 및 Firebase 연동

버질·2025년 1월 8일

1. 단어장앱 ViewModel 설계

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

2. SnapKit과 RxSwift를 활용한 UI 구현

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

3. Firebase API 연동

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()
            }
        }
    }
}
profile
iOS Developer · SwiftUI & UIKit '가끔 되고 가끔 안 되는' 문제를 뿌리부터 잡습니다.

0개의 댓글