Skip to content

Commit

Permalink
Merge branch 'main' into feature/UploadDomainData
Browse files Browse the repository at this point in the history
  • Loading branch information
GeonH0 authored Jul 24, 2024
2 parents ed32a83 + 6632383 commit f527923
Show file tree
Hide file tree
Showing 18 changed files with 2,158 additions and 1,158 deletions.
2,100 changes: 1,057 additions & 1,043 deletions HomeCafeRecipes/HomeCafeRecipes.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions HomeCafeRecipes/HomeCafeRecipes/Domain/Entities/Recipe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,25 @@ struct Recipe {
let likeCount: Int
let createdAt: Date
}

extension Recipe {

static func dummyRecipe() -> Recipe {
.init(
id: 1,
type: .coffee,
name: "",
description: "",
writer: .init(
id: 1,
profileImage: "",
nickname: "",
createdAt: Date()
),
imageUrls: [],
isLikedByCurrentUser: false,
likeCount: 0,
createdAt: Date()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,43 @@
//

import Foundation

import RxSwift

protocol RecipeDetailInteractorDelegate: AnyObject {
func fetchedRecipe(result: Result<Recipe, Error>)
}

protocol InputRecipeDetailInteractor {
func viewDidLoad()
}

protocol OutputRecipeDetailInteractor {
var recipe: Observable<Result<Recipe, Error>> { get }
protocol RecipeDetailInteractor {
func viewDidLoad()
}

class RecipeDetailInteractor: InputRecipeDetailInteractor, OutputRecipeDetailInteractor {

class RecipeDetailInteractorImpl: RecipeDetailInteractor {
private let fetchRecipeDetailUseCase: FetchRecipeDetailUseCase
private let recipeID: Int
private let disposeBag = DisposeBag()
private let recipeDetailSubject = PublishSubject<Result<Recipe, Error>>()
weak var delegate: RecipeDetailInteractorDelegate?

var recipe: Observable<Result<Recipe, Error>> {
return recipeDetailSubject.asObservable()
}


init(
fetchRecipeDetailUseCase: FetchRecipeDetailUseCase,
recipeID: Int
) {
self.fetchRecipeDetailUseCase = fetchRecipeDetailUseCase
self.recipeID = recipeID
}

func setDelegate(_ delegate: RecipeDetailInteractorDelegate) {
self.delegate = delegate
bindOutputs()
}

private func bindOutputs() {
recipe
.subscribe(onNext: { [weak self] result in
self?.delegate?.fetchedRecipe(result: result)
})
.disposed(by: disposeBag)
}


func viewDidLoad() {
fetchRecipeDetail()
}

private func fetchRecipeDetail() {
fetchRecipeDetailUseCase.execute(recipeID: recipeID)
.subscribe { [weak self] result in
self?.handleResult(result)
}
.subscribe(onSuccess: { [weak self] result in
self?.delegate?.fetchedRecipe(result: result)
}, onFailure: { [weak self] error in
self?.delegate?.fetchedRecipe(result: .failure(error))
})
.disposed(by: disposeBag)
}

private func handleResult(_ result: Result<Recipe, Error>) {
switch result {
case .success(let recipe):
self.recipeDetailSubject.onNext(.success(recipe))
case .failure(let error):
self.recipeDetailSubject.onNext(.failure(error))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,28 @@
//

import Foundation

import RxSwift

protocol RecipeListInteractorDelegate: AnyObject {
func fetchedRecipes(result: Result<[Recipe], Error>)
func showRecipeDetail(ID: Int)
}

protocol InputRecipeListInteractor {
protocol RecipeListInteractor {
func viewDidLoad()
func fetchNextPage()
func didSelectItem(ID: Int)
func searchRecipes(with query: String)
func resetSearch()
}

protocol OutputRecipeListInteractor {
var recipes: Observable<Result<[Recipe], Error>> { get }
}

class RecipeListInteractor: InputRecipeListInteractor, OutputRecipeListInteractor {
class RecipeListInteractorImpl: RecipeListInteractor {

private let disposeBag = DisposeBag()
private let fetchFeedListUseCase: FetchFeedListUseCase
private let searchFeedListUseCase: SearchFeedListUseCase
private weak var delegate: RecipeListInteractorDelegate?
weak var delegate: RecipeListInteractorDelegate?

private var currentPage: Int = 1
private var isFetching = false
Expand All @@ -40,34 +37,18 @@ class RecipeListInteractor: InputRecipeListInteractor, OutputRecipeListInteracto

private let recipesSubject = BehaviorSubject<Result<[Recipe], Error>>(value: .success([]))

var recipes: Observable<Result<[Recipe], Error>> {
return recipesSubject.asObservable()
}

init(fetchFeedListUseCase: FetchFeedListUseCase, searchFeedListUseCase: SearchFeedListUseCase) {
self.fetchFeedListUseCase = fetchFeedListUseCase
self.searchFeedListUseCase = searchFeedListUseCase
}

func setDelegate(_ delegate: RecipeListInteractorDelegate) {
self.delegate = delegate
bindOutputs()
}

private func bindOutputs() {
recipes
.subscribe(onNext: { [weak self] result in
self?.delegate?.fetchedRecipes(result: result)
})
.disposed(by: disposeBag)
}


func viewDidLoad() {
fetchRecipes()
}

func fetchNextPage() {
fetchNextRecipes(nextPage: currentPage)
guard !isFetching else { return }
fetchNextRecipes()
}

func didSelectItem(ID: Int) {
Expand All @@ -78,6 +59,7 @@ class RecipeListInteractor: InputRecipeListInteractor, OutputRecipeListInteracto
isSearching = false
currentSearchQuery = nil
currentPage = 1
allRecipes.removeAll()
recipesSubject.onNext(.success([]))
fetchRecipes()
}
Expand All @@ -89,29 +71,35 @@ class RecipeListInteractor: InputRecipeListInteractor, OutputRecipeListInteracto
isSearching = true
currentPage = 1
searchFeedListUseCase.execute(title: title, pageNumber: currentPage)
.subscribe { [weak self] result in
.subscribe(onSuccess: { [weak self] result in
self?.handleResult(result)
}
}, onFailure: { [weak self] error in
self?.handleResult(.failure(error))
})
.disposed(by: disposeBag)
}

private func fetchRecipes() {
guard !isFetching else { return }
isFetching = true
fetchFeedListUseCase.execute(pageNumber: currentPage)
.subscribe { [weak self] result in
.subscribe(onSuccess: { [weak self] result in
self?.handleResult(result)
}
}, onFailure: { [weak self] error in
self?.handleResult(.failure(error))
})
.disposed(by: disposeBag)
}

private func fetchNextRecipes(nextPage: Int) {
private func fetchNextRecipes() {
guard !isFetching else { return }
isFetching = true
fetchFeedListUseCase.execute(pageNumber: nextPage)
.subscribe { [weak self] result in
fetchFeedListUseCase.execute(pageNumber: currentPage)
.subscribe(onSuccess: { [weak self] result in
self?.handleResult(result)
}
}, onFailure: { [weak self] error in
self?.handleResult(.failure(error))
})
.disposed(by: disposeBag)
}

Expand All @@ -127,11 +115,10 @@ class RecipeListInteractor: InputRecipeListInteractor, OutputRecipeListInteracto
} else {
allRecipes.append(contentsOf: recipes)
}
self.recipesSubject.onNext(.success(allRecipes))
self.currentPage += 1
delegate?.fetchedRecipes(result: .success(allRecipes))
currentPage += 1
case .failure(let error):
recipesSubject.onNext(.failure(error))
delegate?.fetchedRecipes(result: .failure(error))
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ final class RecipeDetailViewController: UIViewController {

init(interactor: RecipeDetailInteractor) {
self.interactor = interactor
super.init(nibName: nil, bundle: nil)
self.interactor.setDelegate(self)
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
Expand All @@ -37,7 +36,7 @@ final class RecipeDetailViewController: UIViewController {
interactor.viewDidLoad()
contentView.customNavigationBar.backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
}

private func displayError(_ error: Error) {
let alert = UIAlertController(title: "해당 레시피를 로드하는데 실패했습니다.", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
Expand All @@ -60,7 +59,17 @@ extension RecipeDetailViewController: RecipeDetailInteractorDelegate {
self.contentView.configure(with: recipeItemViewModel)
}
case .failure(let error):
self.displayError(error)
DispatchQueue.main.async {
self.displayError(error)
}
}
}
}


extension RecipeDetailViewController: Drawable {
var viewController: UIViewController? {
return self
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ final class RecipeListView: UIView {
private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())

private var recipes: [RecipeListItemViewModel] = []
weak var coordinator: RecipeDetailCoordinatorProtocol?
weak var delegate: RecipeListViewDelegate?

override init(frame: CGRect) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ final class RecipeListViewController: UIViewController {
private var recipes: [RecipeListItemViewModel] = []
private let searchBar = SearchBar()
private let recipeListView = RecipeListView()

init(interactor: RecipeListInteractor) {
self.interactor = interactor
super.init(nibName: nil, bundle: nil)
private let recipeListMapper = RecipeListMapper()
private let router: RecipeListRouter

init(interactor: RecipeListInteractor, router: RecipeListRouter) {
self.interactor.setDelegate(self)

self.interactor = interactor
self.router = router
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
Expand All @@ -27,7 +29,6 @@ final class RecipeListViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
recipeListView.delegate = self
setupUI()
interactor.viewDidLoad()
}
Expand All @@ -36,16 +37,16 @@ final class RecipeListViewController: UIViewController {
view.backgroundColor = .white
view.addSubview(searchBar)
view.addSubview(recipeListView)

searchBar.translatesAutoresizingMaskIntoConstraints = false
recipeListView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
searchBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
searchBar.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
searchBar.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
searchBar.heightAnchor.constraint(equalToConstant: 50),

recipeListView.topAnchor.constraint(equalTo: searchBar.bottomAnchor),
recipeListView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
recipeListView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
Expand All @@ -54,10 +55,10 @@ final class RecipeListViewController: UIViewController {

searchBar.setDelegate(self)
}

}

// MARK: - UISearchBarDelegate

extension RecipeListViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isBlank {
Expand All @@ -75,6 +76,7 @@ extension RecipeListViewController: UISearchBarDelegate {
}

// MARK: - RecipeListInteractorDelegate

extension RecipeListViewController: RecipeListInteractorDelegate {
func fetchedRecipes(result: Result<[Recipe], Error>) {
switch result {
Expand All @@ -90,7 +92,7 @@ extension RecipeListViewController: RecipeListInteractorDelegate {
}

func showRecipeDetail(ID: Int) {
coordinator.showRecipeDetail(from: self, recipeID: ID)
router.navigateToRecipeDetail(from: self, recipeID: ID)
}
}

Expand All @@ -100,7 +102,7 @@ extension RecipeListViewController: RecipeListViewDelegate {
interactor.didSelectItem(ID: ID)
}

func ScrollToBottom() {
func scrollToBottom() {
interactor.fetchNextPage()
}
}
Loading

0 comments on commit f527923

Please sign in to comment.