Skip to content

Commit

Permalink
Start to refactor from MVC to VIP
Browse files Browse the repository at this point in the history
  • Loading branch information
dogo committed Nov 29, 2023
1 parent 94c3774 commit 9ec5dd7
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
import UIKit

final class SetsListViewController: UIViewController {
private let setsView = SetsView()
private let database: DatabaseProtocol?
private let destinyService: SWDestinyServiceProtocol
private lazy var navigator = SetsListNavigator(self.navigationController)

var presenter: SetsPresenterProtocol?
private let setsView: SetsView

// MARK: - Life Cycle

init(service: SWDestinyServiceProtocol = SWDestinyService(), database: DatabaseProtocol?) {
destinyService = service
self.database = database
init(setsView: SetsView = SetsView()) {
self.setsView = setsView
super.init(nibName: nil, bundle: nil)
}

Expand All @@ -33,18 +31,12 @@ final class SetsListViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .blackWhite
setsView.pullToRefresh.addTarget(self, action: #selector(retrieveSets), for: .valueChanged)
presenter?.viewDidLoad()

setupNavigationItem()

setsView.pullToRefresh.addTarget(self, action: #selector(retrieveSets(sender:)), for: .valueChanged)

setsView.startAnimating()
retrieveSets(sender: setsView.pullToRefresh)

setsView.setsTableView.didSelectSet = { [weak self] set in
self?.navigateToNextController(with: set)
setsView.didSelectSet = { [weak self] set in
self?.presenter?.didSelectSet(set)
}
}

Expand All @@ -54,48 +46,42 @@ final class SetsListViewController: UIViewController {
navigationItem.title = L10n.expansions
}

func setupNavigationItem() {
navigationItem.leftBarButtonItem = UIBarButtonItem(image: Asset.NavigationBar.icAbout.image, style: .plain, target: self, action: #selector(aboutButtonTouched(_:)))
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(searchButtonTouched(_:)))
@objc
func retrieveSets() {
presenter?.retrieveSets()
}

@objc
func retrieveSets(sender: UIRefreshControl) {
Task { [weak self] in
guard let self else { return }

defer {
Task { @MainActor in
self.setsView.activityIndicator.stopAnimating()
self.setsView.endRefreshControl()
}
}

do {
let setList = try await self.destinyService.retrieveSetList()
self.setsView.setsTableView.updateSetList(setList)
} catch {
ToastMessages.showNetworkErrorMessage()
LoggerManager.shared.log(event: .setsList, parameters: ["error": error.localizedDescription])
}
}
func aboutButtonTouched() {
presenter?.aboutButtonTouched()
}

@objc
func searchButtonTouched() {
presenter?.searchButtonTouched()
}
}

// MARK: - <SetsListViewDelegate>
extension SetsListViewController: SetsViewProtocol {

func startAnimating() {
setsView.startAnimating()
}

func navigateToNextController(with set: SetDTO) {
navigator.navigate(to: .cardList(database: database, with: set))
func stopAnimating() {
setsView.stopAnimating()
}

// MARK: - UIBarButton Actions
func endRefreshControl() {
setsView.endRefreshControl()
}

@objc
func aboutButtonTouched(_ sender: Any) {
navigator.navigate(to: .about)
func updateSetList(_ setList: [SetDTO]) {
setsView.updateSetList(setList)
}

@objc
func searchButtonTouched(_ sender: Any) {
navigator.navigate(to: .search(database: database))
func setupNavigationItem() {
navigationItem.leftBarButtonItem = UIBarButtonItem(image: Asset.NavigationBar.icAbout.image, style: .plain, target: self, action: #selector(aboutButtonTouched))
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(searchButtonTouched))
}
}
26 changes: 26 additions & 0 deletions SWDestinyTrades/Classes/Sets/Interactor/SetsListInteractor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// SetsListInteractor.swift
// SWDestinyTrades
//
// Created by Diogo Autilio on 29/11/23.
// Copyright © 2023 Diogo Autilio. All rights reserved.
//

import Foundation

protocol SetsInteractorProtocol {
func retrieveSets() async throws -> [SetDTO]
}

final class SetsListInteractor: SetsInteractorProtocol {

private let service: SWDestinyServiceProtocol

init(service: SWDestinyServiceProtocol = SWDestinyService()) {
self.service = service
}

func retrieveSets() async throws -> [SetDTO] {
return try await service.retrieveSetList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@ final class SetsListNavigator: Navigator {
case search(database: DatabaseProtocol?)
}

private weak var navigationController: UINavigationController?
private weak var viewController: UIViewController?

// MARK: - Initializer

init(_ navigationController: UINavigationController?) {
self.navigationController = navigationController
init(_ viewController: UIViewController) {
self.viewController = viewController
}

// MARK: - Navigator

func navigate(to destination: Destination) {
let viewController = makeViewController(for: destination)
navigationController?.pushViewController(viewController, animated: true)
viewController?.navigationController?.pushViewController(makeViewController(for: destination), animated: true)
}

// MARK: - Private
Expand Down
76 changes: 76 additions & 0 deletions SWDestinyTrades/Classes/Sets/Presenter/SetsListPresenter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// SetsListPresenter.swift
// SWDestinyTrades
//
// Created by Diogo Autilio on 28/11/23.
// Copyright © 2023 Diogo Autilio. All rights reserved.
//

import Foundation

protocol SetsPresenterProtocol {
func viewDidLoad()
func retrieveSets()
func aboutButtonTouched()
func searchButtonTouched()
func didSelectSet(_ set: SetDTO)
}

final class SetsPresenter: SetsPresenterProtocol {

weak var view: SetsViewProtocol?
private let interactor: SetsInteractorProtocol
private let database: DatabaseProtocol?
private let navigator: SetsListNavigator

init(view: SetsViewProtocol,
interactor: SetsInteractorProtocol,
database: DatabaseProtocol?,
navigator: SetsListNavigator) {
self.view = view
self.interactor = interactor
self.database = database
self.navigator = navigator
}

func viewDidLoad() {
view?.startAnimating()
view?.setupNavigationItem()
retrieveSets()
}

func retrieveSets() {
Task { [weak self] in
do {
guard let self else { return }

let setList = try await interactor.retrieveSets()

await MainActor.run { [weak self] in
self?.view?.updateSetList(setList)
self?.view?.stopAnimating()
self?.view?.endRefreshControl()
}
} catch {
await MainActor.run { [weak self] in
ToastMessages.showNetworkErrorMessage()
LoggerManager.shared.log(event: .setsList, parameters: ["error": error.localizedDescription])
self?.view?.stopAnimating()
self?.view?.endRefreshControl()
}
}
}
}

func didSelectSet(_ set: SetDTO) {
navigator.navigate(to: .cardList(database: database, with: set))
}

func aboutButtonTouched() {
navigator.navigate(to: .about)
}

func searchButtonTouched() {
navigator.navigate(to: .search(database: database))
}
}
19 changes: 19 additions & 0 deletions SWDestinyTrades/Classes/Sets/View/SetsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@

import UIKit

protocol SetsViewProtocol: AnyObject {
func startAnimating()
func stopAnimating()
func endRefreshControl()
func updateSetList(_ setList: [SetDTO])
func setupNavigationItem()
}

final class SetsView: UIView {

var didSelectSet: ((SetDTO) -> Void)?

let setsTableView = SetsTableView()

let pullToRefresh = UIRefreshControl()
Expand All @@ -24,6 +35,10 @@ final class SetsView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupBaseView()

setsTableView.didSelectSet = { [weak self] set in
self?.didSelectSet?(set)
}
}

@available(*, unavailable)
Expand Down Expand Up @@ -52,6 +67,10 @@ final class SetsView: UIView {
pullToRefresh.attributedTitle = attributedTitle
pullToRefresh.endRefreshing()
}

func updateSetList(_ setList: [SetDTO]) {
setsTableView.updateSetList(setList)
}
}

extension SetsView: BaseViewConfiguration {
Expand Down
25 changes: 25 additions & 0 deletions SWDestinyTrades/Classes/TabBar/SWDTabBarFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// SWDTabBarFactory.swift
// SWDestinyTrades
//
// Created by Diogo Autilio on 28/11/23.
// Copyright © 2023 Diogo Autilio. All rights reserved.
//

import Foundation
import UIKit

final class SWDTabBarFactory {

func makeSetsList(with database: DatabaseProtocol?) -> UIViewController {
let viewController = SetsListViewController()
let router = SetsListNavigator(viewController)
let interactor = SetsListInteractor()
let presenter = SetsPresenter(view: viewController,
interactor: interactor,
database: database,
navigator: router)
viewController.presenter = presenter
return viewController
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ final class SWDTabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()

let factory = SWDTabBarFactory()

// Create SetsListViewController Tab
let setsTab = UINavigationController(rootViewController: SetsListViewController(database: database))
let setsTab = UINavigationController(rootViewController: factory.makeSetsList(with: database))
setsTab.tabBarItem = UITabBarItem(title: L10n.cards, image: Asset.Tabbar.icCards.image, selectedImage: Asset.Tabbar.icCardsFilled.image)

// Create DeckListViewController Tab
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ final class SetsListViewControllerTests: QuickSpec {

it("should have valid layout") {
client.fileName = "sets"
sut = SetsListViewController(service: service, database: nil)

sut = SetsListViewController()
let router = SetsListNavigator(sut)
let presenter = SetsPresenter(view: sut,
interactor: SetsListInteractor(service: service),
database: nil,
navigator: router)
sut.presenter = presenter

navigation = UINavigationController(rootViewController: sut)
window.showTestWindow(controller: navigation)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// SetsListInteractorTests.swift
// SWDestinyTradesTests
//
// Created by Diogo Autilio on 29/11/23.
// Copyright © 2023 Diogo Autilio. All rights reserved.
//

import Nimble
import Nimble_Snapshots
import Quick
import UIKit

@testable import SWDestinyTrades

final class SetsListInteractorTests: AsyncSpec {

override class func spec() {

var sut: SetsListInteractor!
var service: SWDestinyService!
var client: HttpClientMock!

describe("SetsListInteractor") {

beforeEach {
client = HttpClientMock()
service = SWDestinyService(client: client)
sut = SetsListInteractor(service: service)
}

it("should retrieve the set list with success") {
let setList = try await sut.retrieveSets()
expect(setList.count) == 20
}

it("should fail to retrieve the set list") {
client.error = true
await expect { try await sut.retrieveSets() }.to(throwError())
}
}
}
}

0 comments on commit 9ec5dd7

Please sign in to comment.