-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
π :: (#218) οΏ½CompanySearch κ°λ°
- Loading branch information
Showing
10 changed files
with
324 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import RxFlow | ||
|
||
public enum SearchCompanyStep: Step { | ||
case searchCompanyIsRequired | ||
case companyDetailIsRequired(id: Int) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import UIKit | ||
import Presentation | ||
import Swinject | ||
import RxFlow | ||
import Core | ||
|
||
public final class SearchCompanyFlow: Flow { | ||
public let container: Container | ||
private let rootViewController: SearchCompanyViewController | ||
public var root: Presentable { | ||
return rootViewController | ||
} | ||
|
||
public init(container: Container) { | ||
self.container = container | ||
self.rootViewController = container.resolve(SearchCompanyViewController.self)! | ||
} | ||
|
||
public func navigate(to step: Step) -> FlowContributors { | ||
guard let step = step as? SearchCompanyStep else { return .none } | ||
|
||
switch step { | ||
case .searchCompanyIsRequired: | ||
return navigateToSearchCompany() | ||
|
||
case let .companyDetailIsRequired(id): | ||
return navigateToCompanyDetail(id) | ||
} | ||
} | ||
} | ||
|
||
private extension SearchCompanyFlow { | ||
func navigateToSearchCompany() -> FlowContributors { | ||
return .one(flowContributor: .contribute( | ||
withNextPresentable: rootViewController, | ||
withNextStepper: rootViewController.viewModel | ||
)) | ||
} | ||
|
||
func navigateToCompanyDetail(_ companyId: Int) -> FlowContributors { | ||
let companyDetailFlow = CompanyDetailFlow(container: container) | ||
|
||
Flows.use(companyDetailFlow, when: .created) { (root) in | ||
let view = root as? CompanyDetailViewController | ||
view?.viewModel.companyID = companyId | ||
view?.viewModel.type = .searchCompany | ||
self.rootViewController.navigationController?.pushViewController( | ||
view!, animated: true | ||
) | ||
} | ||
|
||
return .one(flowContributor: .contribute( | ||
withNextPresentable: companyDetailFlow, | ||
withNextStepper: OneStepper(withSingleStep: CompanyDetailStep.companyDetailIsRequired) | ||
)) | ||
} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
Projects/Presentation/Sources/SearchCompany/SearchCompanyViewController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import UIKit | ||
import Domain | ||
import RxSwift | ||
import RxCocoa | ||
import SnapKit | ||
import Then | ||
import Core | ||
import DesignSystem | ||
|
||
public final class SearchCompanyViewController: BaseViewController<SearchCompanyViewModel> { | ||
private let searchButtonDidTap = PublishRelay<String>() | ||
private let emptySearchView = ListEmptyView().then { | ||
$0.isHidden = true | ||
$0.setEmptyView( | ||
title: "κ²μμ΄μ κ΄λ ¨ λ νμ¬λ₯Ό λͺ»μ°Ύμμ΄μ", | ||
subTitle: "μ λλ‘ μ λ ₯νλμ§ λ€μ νλ² νμΈν΄μ£ΌμΈμ" | ||
) | ||
} | ||
|
||
private let searchImageView = UIImageView().then { | ||
$0.image = .jobisIcon(.searchIcon) | ||
} | ||
private let searchTextField = UITextField().then { | ||
$0.placeholder = "κ²μμ΄λ₯Ό μ λ ₯ν΄μ£ΌμΈμ" | ||
$0.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 0)) | ||
$0.rightView = UIView(frame: CGRect(x: 0, y: 0, width: 16, height: 0)) | ||
$0.leftViewMode = .always | ||
$0.rightViewMode = .always | ||
$0.backgroundColor = .GrayScale.gray30 | ||
$0.layer.cornerRadius = 12 | ||
} | ||
|
||
private let searchTableView = UITableView().then { | ||
$0.rowHeight = 72 | ||
$0.separatorStyle = .none | ||
$0.register( | ||
CompanyTableViewCell.self, | ||
forCellReuseIdentifier: CompanyTableViewCell.identifier | ||
) | ||
$0.showsVerticalScrollIndicator = false | ||
} | ||
public override func addView() { | ||
[ | ||
searchTextField, | ||
searchImageView, | ||
searchTableView | ||
].forEach(self.view.addSubview(_:)) | ||
[ | ||
emptySearchView | ||
].forEach(searchTableView.addSubview(_:)) | ||
} | ||
|
||
public override func setLayout() { | ||
searchTextField.snp.makeConstraints { | ||
$0.top.equalTo(view.safeAreaInsets) | ||
$0.trailing.leading.equalToSuperview().inset(24) | ||
$0.height.equalTo(48) | ||
} | ||
|
||
searchImageView.snp.makeConstraints { | ||
$0.centerY.equalTo(searchTextField) | ||
$0.left.equalTo(searchTextField.snp.left).inset(16) | ||
} | ||
|
||
searchTableView.snp.makeConstraints { | ||
$0.leading.trailing.bottom.equalToSuperview() | ||
$0.top.equalTo(searchTextField.snp.bottom).offset(12) | ||
} | ||
|
||
emptySearchView.snp.makeConstraints { | ||
$0.centerY.equalTo(view.safeAreaLayoutGuide) | ||
$0.centerX.equalTo(view.safeAreaLayoutGuide) | ||
} | ||
} | ||
|
||
public override func bind() { | ||
let input = SearchCompanyViewModel.Input( | ||
viewAppear: self.viewWillAppearPublisher, | ||
pageChange: searchTableView.rx.willDisplayCell | ||
.filter { | ||
$0.indexPath.row == self.searchTableView.numberOfRows(inSection: $0.indexPath.section) - 1 | ||
}.asObservable(), | ||
searchButtonDidTap: searchButtonDidTap, | ||
searchTableViewDidTap: searchTableView.rx.itemSelected | ||
) | ||
|
||
let output = viewModel.transform(input) | ||
|
||
output.companyListInfo | ||
.skip(1) | ||
.do(onNext: { | ||
self.emptySearchView.isHidden = !$0.isEmpty | ||
}) | ||
.bind(to: searchTableView.rx.items( | ||
cellIdentifier: CompanyTableViewCell.identifier, | ||
cellType: CompanyTableViewCell.self | ||
)) { _, element, cell in | ||
cell.adapt(model: element) | ||
} | ||
.disposed(by: disposeBag) | ||
|
||
output.emptyViewIsHidden.asObservable() | ||
.map { | ||
self.emptySearchView.isHidden = $0 | ||
} | ||
.subscribe() | ||
.disposed(by: disposeBag) | ||
} | ||
|
||
public override func configureViewController() { | ||
self.searchTextField.delegate = self | ||
viewWillAppearPublisher.asObservable() | ||
.bind { | ||
self.hideTabbar() | ||
self.navigationController?.navigationBar.prefersLargeTitles = false | ||
} | ||
.disposed(by: disposeBag) | ||
} | ||
|
||
public override func configureNavigation() { } | ||
} | ||
|
||
extension SearchCompanyViewController: UITextFieldDelegate { | ||
public func textFieldShouldReturn(_ textField: UITextField) -> Bool { | ||
let title = textField.text | ||
viewModel.searchText = title | ||
searchButtonDidTap.accept(textField.text ?? "") | ||
self.view.endEditing(true) | ||
return true | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
Projects/Presentation/Sources/SearchCompany/SearchCompanyViewModel.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import UIKit | ||
import RxSwift | ||
import RxCocoa | ||
import RxFlow | ||
import Core | ||
import Domain | ||
|
||
public final class SearchCompanyViewModel: BaseViewModel, Stepper { | ||
public let steps = PublishRelay<Step>() | ||
public var searchText: String? | ||
private let disposeBag = DisposeBag() | ||
private let fetchCompanyListUseCase: FetchCompanyListUseCase | ||
private var companyListInfo = BehaviorRelay<[CompanyEntity]>(value: []) | ||
private var pageCount: Int = 1 | ||
|
||
init( | ||
fetchCompanyListUseCase: FetchCompanyListUseCase | ||
) { | ||
self.fetchCompanyListUseCase = fetchCompanyListUseCase | ||
} | ||
|
||
public struct Input { | ||
let viewAppear: PublishRelay<Void> | ||
let pageChange: Observable<WillDisplayCellEvent> | ||
let searchButtonDidTap: PublishRelay<String> | ||
let searchTableViewDidTap: ControlEvent<IndexPath> | ||
} | ||
|
||
public struct Output { | ||
let companyListInfo: BehaviorRelay<[CompanyEntity]> | ||
let emptyViewIsHidden: PublishRelay<Bool> | ||
} | ||
|
||
public func transform(_ input: Input) -> Output { | ||
let emptyViewIsHidden = PublishRelay<Bool>() | ||
input.viewAppear.asObservable() | ||
.skip(1) | ||
.flatMap { | ||
self.pageCount = 1 | ||
return self.fetchCompanyListUseCase.execute(page: self.pageCount, name: self.searchText) | ||
} | ||
.bind(onNext: { | ||
self.companyListInfo.accept([]) | ||
self.companyListInfo.accept(self.companyListInfo.value + $0) | ||
self.pageCount = 1 | ||
}) | ||
.disposed(by: disposeBag) | ||
|
||
input.pageChange.asObservable() | ||
.distinctUntilChanged({ $0.indexPath.row }) | ||
.flatMap { _ in | ||
self.pageCount += 1 | ||
return self.fetchCompanyListUseCase.execute(page: self.pageCount, name: self.searchText) | ||
} | ||
.bind { self.companyListInfo.accept(self.companyListInfo.value + $0) } | ||
.disposed(by: disposeBag) | ||
|
||
input.searchButtonDidTap.asObservable() | ||
.filter { | ||
emptyViewIsHidden.accept(!$0.isEmpty) | ||
return $0 != "" | ||
} | ||
.flatMap { | ||
self.pageCount = 1 | ||
return self.fetchCompanyListUseCase.execute(page: self.pageCount, name: $0) | ||
} | ||
.bind(onNext: { | ||
self.companyListInfo.accept([]) | ||
self.companyListInfo.accept(self.companyListInfo.value + $0) | ||
}) | ||
.disposed(by: disposeBag) | ||
|
||
input.searchTableViewDidTap.asObservable() | ||
.map { | ||
SearchCompanyStep.companyDetailIsRequired( | ||
id: self.companyListInfo.value[$0.row].companyID | ||
) | ||
} | ||
.bind(to: steps) | ||
.disposed(by: disposeBag) | ||
|
||
return Output( | ||
companyListInfo: companyListInfo, | ||
emptyViewIsHidden: emptyViewIsHidden | ||
) | ||
} | ||
} |