[아키텍처] Clean Swift(VIP)
정말 클린한 것은 없는게 아닐까?
Clean Swift?
- ViewController
- Interactor
- Presenter
- Worker
- Router
- Models
Clean Architecture 를 기반으로 IOS에 맞게 재해석한 결과물이 Clean Swift라고 한다.
ViewController, Interactor, Presenter의 앞글자를 따서 VIP 패턴이라고도 불린다.
공식 홈페이지에 나와있는 이미지로
원 형태의 데이터 흐름이 만들어지는 것이 가장 큰 특징이다.
ViewController
화면 표현과 사용자와의 상호작용 담당
Interactor
비즈니스 로직 담당
Presenter
Interactor에서 얻은 결과물을 View에 표현 가능한 형태로 변환 담당
Worker
Interactor에서 복잡한 로직을 분리하거나 확장하는 용도
Router
화면 이동에 대한 로직 담당
Models
UseCase 및 케이스 별 계층 간 데이터 구조 정의
좀 더 자세한 설명은 이곳을 확인해주세요.
구현
Clean Swift는 파일 분리를 통해 역할 or 관심사 분리를 하고자 하는 면이 강한 것 같다.
그런 점이 규모가 작은 개발을 할 때는 효율성이 떨어지는 측면이 있다.
다만 파일로 분리되다 보니 각 계층의 기능은 명확하게 구별 할 수 있다.
ViewController
검색 이벤트 발생시 해당 비즈니스 로직이 구현된 함수를 호출한다.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
interactor?.fetchRepositories(request: Search.FetchRepositories.Request(text: searchBar.text))
}
리스트 갱신을 위한 함수를 구현한다.
func displayRepositories(viewModel: Search.FetchRepositories.ViewModel) {
DispatchQueue.main.async {
self.displayedRepositories = viewModel.fetchedRepositories
self.tableView.reloadData()
}
}
Interactor
비즈니스 로직을 담당하고, 세부적인 부분은 Worker에서 처리하도록 구현한다.
func fetchRepositories(request: Search.FetchRepositories.Request) {
worker = SearchWorker()
worker?.fetchRepositories(text: request.text, completion: { result in
switch result {
case .success(let response):
self.repositories = response.items
self.presenter?.presentFetchedRepositories(response: response)
case .failure(let error):
print("error : \(error.localizedDescription)")
let response = Search.FetchRepositories.Response()
self.repositories = response.items
self.presenter?.presentFetchedRepositories(response: response)
}
})
}
Presenter
interactor에서 넘겨받은 데이터를 view에 맞게 변환해 전달한다.
func presentFetchedRepositories(response: Search.FetchRepositories.Response) {
let viewModel = Search.FetchRepositories.ViewModel(fetchedRepositories: response.items)
self.viewController?.displayRepositories(viewModel: viewModel)
}
Worker
interactor에서 필요한 세부적인 비즈니스 로직을 구현한다.
func fetchRepositories(text: String?, completion: @escaping(Result<Search.FetchRepositories.Response, Error>) -> Void) {
var urlComponents = URLComponents(string: "https://api.github.com/search/repositories")
urlComponents?.queryItems = [
URLQueryItem(name: "q", value: text)
]
URLSession.shared.dataTask(with: (urlComponents?.url)!) { data, response, error in
guard error == nil else {
completion(.failure(error!))
return
}
if let response = response as? HTTPURLResponse, response.statusCode == 200, let data = data {
do {
let searchResponse = try JSONDecoder().decode(Search.FetchRepositories.Response.self, from: data)
completion(.success(searchResponse))
} catch {
print("error : \(error.localizedDescription)")
completion(.failure(error))
}
}
}.resume()
}
Router
Segue를 통해 화면이동을 하거나 코드적으로 수행할 수 있도록 구현이 가능하다.
이때 Swinject와 같은 DI를 적용한다면 ViewController의 setup을 분리해낼 수 있다.
또한 passData함수를 통해 Scene간 데이터 전달이 가능하다.
func routeToSearchDetail(segue: UIStoryboardSegue?) {
if let segue = segue {
let destinationVC = segue.destination as! SearchDetailViewController
var destinationDS = destinationVC.router!.dataStore!
passDataToSearchDetail(source: dataStore!, destination: &destinationDS)
} else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyboard.instantiateViewController(withIdentifier: "SearchDetailViewController") as! SearchDetailViewController
var destinationDS = destinationVC.router!.dataStore!
passDataToSearchDetail(source: dataStore!, destination: &destinationDS)
navigateToSearchDetail(source: viewController!, destination: destinationVC)
}
}
Models
해당 Scene에서 사용되는 UseCase를 정의하고, 해당 케이스의 계층별 데이터 구조를 정의한다.
enum Search {
// MARK: Use cases
enum FetchRepositories {
struct Request {
let text: String?
}
struct Response: Codable {
var total_count: Int = 0
var incomplete_results: Bool = false
var items: [Repository] = []
}
struct ViewModel {
let fetchedRepositories: [Repository]
}
}
}
결과
전체 코드는 깃허브에 있습니다.
Author And Source
이 문제에 관하여([아키텍처] Clean Swift(VIP)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hanchi0/아키텍처-Clean-SwiftVIP저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)