MVVM은 최근에 iOS개발에 많이 이용되는 디자인 패턴이다.
장점!
Binding : 하나를 다른것과 mapping하는 것.
View Controller : Model과 View사이에서 존재하고, delegate pattern을 사용하여 함께 연동하도록 함. Controller는 protocol을 통해 통신한다. 예를 들어 UITableView는 UITableViewDatasource protocol을 통해 통신한다.
Model: 데이터를 조작하는 데이터 및 논리가 저장되는 위치. 모델 객체나 네트워킹 코드가 여기에 저장되어 있을 수 있습니다.
View: User에게 정보를 보여주는 화면.
ViewModel: 뷰 내에 보여지는 필드들을 포함.
MVVM은 모델을 뷰의 다른 표현으로 변환해야 할 때 적합하며 여러 모델 간 변환이 필요한 뷰 컨트롤러를 가볍게 만든다.
MVVM은 특히 단위 테스트를 통해 테스트할 코드의 성향을 개선한다.
그러나 MVVM은 재사용 가능성을 극대화하기 위해 ViewModel을 초기에 설계하는 복잡하며, 더 많은 파일을 작성해야 한다.
MVVM 시행을 만드는 옵션이 있다.
예제
public struct BreachModel{
var title:String
}
class BreachViewModel {
init(model:[BreachModel]? = nil) {
if let inputModel = model {
breaches = inputModel
}
}
var breaches = [BreachModel]()
public func configure(_ view:BreachView) {
view.titleLabel.text = breaches.first?.title
}
}
extension BreachViewModel {
func fetchBreaches(completion:@escaping(Result<[BreachModel],Error>) -> Void) {
completion(.success(breaches))
}
}
import UIKit
class simpleViewController: UIViewController {
var breachesViewModel = BreachViewModel(model: [BreachModel(title: "000webhost")])
override func viewDidLoad() {
super.viewDidLoad()
let breachView = BreachView(frame: self.view.frame)
breachesViewModel.configure(breachView)
self.view.addSubview(breachView)
breachView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
breachView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
breachView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
breachView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
breachView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
}
viewModel이 api를 호출한다.
func fetchBreaches(completion: @escaping (Result<[BreachModel], Error>) -> Void) {
HTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: { [weak self] result in
guard let self = self else {return}
switch result {
case .failure(let error):
print ("failure", error)
case .success(let dta) :
let decoder = JSONDecoder()
do
{
self.breaches = try decoder.decode([BreachModel].self, from: dta)
completion(.success(try decoder.decode([BreachModel].self, from: dta)))
} catch {
// deal with error from JSON decoding if used in production
}
}
})
}
class HTTPManager {
static let shared: HTTPManager = HTTPManager()
enum HTTPError: Error {
case invalidURL
case invalidResponse(Data?, URLResponse?)
}
public func get(urlString: String, completionBlock: @escaping (Result<Data, Error>) -> Void) {
guard let url = URL(string: urlString) else {
completionBlock(.failure(HTTPError.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard error == nil else {
completionBlock(.failure(error!))
return
}
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode else {
completionBlock(.failure(HTTPError.invalidResponse(data, response)))
return
}
completionBlock(.success(responseData))
}
task.resume()
}
}
출처: https://stevenpcurtis.medium.com/mvvm-in-swift-19ba3f87ed45
https://github.com/stevencurtis/SwiftCoding/tree/master/MVVM/SimpleMVVMNetwork