-
Swift MVVM 패턴 Network -> Decode -> View개발/Swift 2021. 1. 12. 14:46
네트워킹과 받아온 데이터를 모델로 디코딩 후에 테이블 뷰에 보여주는 간단한 예제이다.
먼저 모델을 만든다.
각 뉴스의 내용을 담을 Article, Article의 리스트 형태인 ArticleList 이다
struct ArticleList : Codable { let articles :[Article] } struct Article : Codable { let title :String let description : String? }
1. Codable은 추후에 디코딩을 위해 필요하다. 서버로 보낼 데이터가 아니라면 Decodable로 사용해도된다.
2. description 은 옵셔널 String 타입으로 되어있다. 이는 나중에 에러가 발생해서 수정했던 내용이다. 먼저 얘기 하자면 desctiption에 null값이 들어갈수 있기 때문이다.
네트워크
func getArticles(url:URL,completion: @escaping ([Article]?)->()){ URLSession.shared.dataTask(with: url){ data , response, error in if let error = error { print(error.localizedDescription) completion(nil) }else if let fetchedData = data { let articleList = try? JSONDecoder().decode(ArticleList.self,from:fetchedData) if let articleList = articleList { completion(articleList.articles) } print(articleList?.articles) } }.resume() }
getArticles 라는 API 호출 메소드를 만들었다. 이 메소드는 view에서 실행할 것이다.
view 에서 getArticle() 함수를 사용하면서 콜백함수를 사용할것이다. 이럴때 @escaping을 사용해준다.
completion: @escaping ([Article]?)->()
에러처리를 우선 해준다음
data 가 들어오면 디코딩 작업을해준다.
let articleList = try? JSONDecoder().decode(ArticleList.self,from:fetchedData)
try? 의경우 에러가 발생해도 에러가 나지않고 articleList는 nil이 들어간다.
try! 를 사용하면 에러발생시 에러가 나고 코드가 진행되지않는다.
여기서 이런 에러가 발생했다.
에러 발생
Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "articles", intValue: nil), _JSONKey(stringValue: "Index 11", intValue: 11), CodingKeys(stringValue: "description", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil)
쉽게는 "description" 에 null 값이 들어가서 에러가 난 경우이다.
그래서 아까처럼 모델을 옵셔널 타입으로 변경했다.
ArticleList 모델로 디코딩이 완료되면 모델을 넘겨주는 completion이라는 콜백 함수를 실행한다.
실행하는 view는 잠시후에 보고 viewModel 먼저 보자
viewModel
struct ArticleViewModel { private let article : Article } extension ArticleViewModel{ init(_ article:Article) { self.article = article } } extension ArticleViewModel{ var title :String { return self.article.title } var description :String? { return self.article.description } }
ㄹ먼저 Article 모델의 뷰모델이다.
간단하게 init함수 와 필드 get 변수 들이있다.
extension 하지않고 struct 안에 해도 상관없다.
struct ArticleListViewModel { let articles :[Article] } extension ArticleListViewModel{ var numberOfSection:Int{ return 1 } func numberOfRowInSetion() -> Int{ return self.articles.count } func articleAtIndex(index:Int) -> ArticleViewModel{ let viewModel = ArticleViewModel.init(self.articles[index]) return viewModel; } }
ㅁArticle 리스트 형태인 필드와
추후에 TableView에서 사용할 메소드들을 넣었다.
viewController
let url = URL(string: "fetch 할 url 주소")! Webservice().getArticles(url: url) { articles in if let articles = articles{ self.articlesViewModel = ArticleListViewModel(articles:articles) } //reload table DispatchQueue.main.async { self.tableView.reloadData() } }
getArticle함수를 사용하면 콜백으로 articles가 리턴될것이다.
articles 는 여기서 뷰 모델로 바꾼후 글로벌로 저장한다.
DispatchQueue.main.async 를 사용해서 이후에 view를 리로드 해준다.
override func numberOfSections(in tableView: UITableView) -> Int { return self.articlesViewModel == nil ?0: self.articlesViewModel.numberOfRowInSetion() } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.articlesViewModel == nil ? 0 : self.articlesViewModel.numberOfRowInSetion() } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "ArticleTableViewCell", for: indexPath) as? ArticleTableViewCell else { fatalError("not found") } let articleVm = articlesViewModel.articleAtIndex(index: indexPath.row) cell.titleLabel.text = articleVm.title cell.descriptionLabel.text = articleVm.description return cell }
테이블 뷰를 위한 오버라이딩 메소드이다.
아까 뷰모델에서 article 개수와 섹션갯수 , article 객체 가져오는 메소드를 만들어놔서 사용했다.
마지막 에사용하는 ArticleTableViewCell 은 따로 만들어놨고 as ArticleTableViewCell 를 사용해 그안에있던
label text 에 접근할수있다.
'개발 > Swift' 카테고리의 다른 글
부모 viewController에서 addSubview 를 통해 뷰를 노출 시키는 방법 (0) 2021.03.22 Storyboard reference 스토리보드 분할하기 (0) 2021.03.12 Swift CollectionView Scroll 이미지로 인한 렉 현상 해결 (0) 2021.02.22 [Swift] Protocol & Delegate (0) 2020.11.04 [Swift] API 네트워킹 , Json 파싱 하는법 (0) 2020.11.03