Setup of a Clean Swift pattern helps writing unit tests

Jeroen de Vrind
Feb 15 · 6 min read
Photo by Frances Gunn on Unsplash

A good pattern comes first

The VIP-cycle in the Clean-Swift architecture

What goes where?

struct Movie {
let title: String
let releaseDate: String
}
// MARK: - Properties
private var interactor: ListMoviesInteractable?
// MARK: - Initializers
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil:
Bundle?) {
super.init(nibName: nibNameOrNil, bundle:nibBundleOrNil)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
init(interactor: ListMoviesInteractable? = nil) {
super.init(nibName: nil, bundle: nil)
self.interactor = interactor
}
// MARK: - Setup
private func setup() {
let presenter = ListMoviesPresenter(viewController: self)
interactor = ListMoviesInteractor(presenter: presenter)
}
private func fetchMovies() {
interactor?.fetchMovies()
}
protocol ListMoviesInteractable {
func fetchMovies()
}
class ListMoviesInteractor: ListMoviesInteractable { }
private var presenter: ListMoviesPresentable
private var worker: MoviesDataProvidable
init(presenter: ListMoviesPresentable, worker:
MoviesDataProvidable = MoviesWorker()) {
self.presenter = presenter
self.worker = worker
}
func fetchMovies() {
worker.fetchMovies { [weak self] (movies) -> Void in
self?.presenter.presentFetchedMovies(movies)
}
}
struct ListMoviesViewModel {
let title: String
let year: String
}
protocol ListMoviesPresentable {
func presentFetchedMovies(_ movies: [Movie])
}
class ListMoviesPresenter: ListMoviesPresentable {private weak var viewController: ListMoviesDisplayableinit(viewController: ListMoviesDisplayable
self.viewController = viewController
}
func presentFetchedMovies(_ movies: [Movie]) {
var displayedMovies: [ListMoviesViewModel] = []
var year: String
for movie in movies {
if let date = movie.releaseDate.convertToDate() {
year = date.getYearString()
} else {
year = "Unknown"
}
let displayedMovie = ListMoviesViewModel(title:
movie.title, year: year)
displayedMovies.append(displayedMovie)
}
viewController?.displayFetchedMovies(displayedMovies)
}
}
protocol ListMoviesDisplayable: class {
func displayFetchedMovies(_ movies: [ListMoviesViewModel])
}
func displayFetchedMovies(_ movies: [ListMoviesViewModel]) {
displayedMovies = movies
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt 
indexPath: IndexPath) -> UITableViewCell {

let displayedMovie = displayedMovies[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier:
"MovieCell") as! ListMoviesTableViewCell
cell.configure(with: displayedMovie)
return cell
}
func configure(with viewModel: ListMoviesViewModel) {
titleLabel?.text = viewModel.title
yearLabel?.text = viewModel.year
}

Short Swift Stories

Articles about Swift that take less than 10 minutes to read.

Jeroen de Vrind

Written by

Short Swift Stories

Articles about Swift that take less than 10 minutes to read.