Skip to content

Commit

Permalink
Merge pull request #145 from boostcampwm-2024/fix/qa-home
Browse files Browse the repository at this point in the history
코드 가독성 증가 (개발의 파인 다이닝 급인듯) & 카테고리 예외처리 & 기록소 이름 예외처리
  • Loading branch information
Kyxxn authored Dec 4, 2024
2 parents 7faac4f + 4b4cb49 commit 6a7dc63
Show file tree
Hide file tree
Showing 39 changed files with 183 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
try registerUseCaseDependency()
try registerViewModelFactoryDependency()
} catch let error as MHCoreError {
MHLogger.error("\(error.description)")
MHLogger.error(error.description + #function)
} catch {
MHLogger.error("\(error.localizedDescription)")
MHLogger.error(error.localizedDescription + #function)
}
}

private func registerStorageDepedency() throws {
DIContainer.shared.register(CoreDataStorage.self, object: CoreDataStorage())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ final class CreateAudioViewController: UIViewController {
configureAddSubviews()
configureConstraints()
configureAddActions()
input.send(.viewDidLoad)
input.send(.prepareTemporaryAudio)
}

// MARK: - Setup
Expand Down Expand Up @@ -274,19 +274,11 @@ final class CreateAudioViewController: UIViewController {

// MARK: - Helper
private func requestMicrophonePermission() {
let alert = UIAlertController(
title: "마이크 권한 필요",
message: "설정에서 마이크 권한을 허용해주세요.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default) { [weak self] _ in
self?.dismiss(animated: true)
})
Task {
AVAudioSession.sharedInstance().requestRecordPermission { @Sendable granted in
Task { @MainActor in
if !granted {
self.present(alert, animated: true, completion: nil)
self.showRedirectSettingAlert(with: .audio)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import MHDomain
public final class CreateAudioViewModel: ViewModelType {
// MARK: - Type
enum Input {
case viewDidLoad
case prepareTemporaryAudio
case audioButtonTapped
case saveButtonTapped
case recordCancelled
Expand Down Expand Up @@ -39,8 +39,8 @@ public final class CreateAudioViewModel: ViewModelType {
func transform(input: AnyPublisher<Input, Never>) -> AnyPublisher<Output, Never> {
input.sink { [weak self] event in
switch event {
case .viewDidLoad:
Task { await self?.viewDidLoad() }
case .prepareTemporaryAudio:
Task { await self?.prepareTemporaryAudio() }
case .audioButtonTapped:
self?.audioButtonTapped()
case .saveButtonTapped:
Expand All @@ -54,14 +54,14 @@ public final class CreateAudioViewModel: ViewModelType {
}

// MARK: - Helper
private func viewDidLoad() async {
private func prepareTemporaryAudio() async {
let mediaDescription = MediaDescription(type: .audio)
self.mediaDescription = mediaDescription
do {
let url = try await temporaryStoreMediaUsecase.execute(media: mediaDescription)
output.send(.audioFileURL(url: url))
} catch {
MHLogger.error("Error in store audio file url: \(error.localizedDescription)")
MHLogger.error(error.localizedDescription + #function)
completion(nil)
output.send(.recordCompleted)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,3 @@ public struct CreateAudioViewModelFactory {
)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ final class BookViewController: UIViewController {
let editBookViewController = EditBookViewController(viewModel: editBookViewModel, mode: .modify)
navigationController?.pushViewController(editBookViewController, animated: true)
} catch {
MHLogger.error(error)
MHLogger.error(error.localizedDescription + #function)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ final class BookCoverViewController: UIViewController {
configureAddSubviews()
configureConstraints()
configureAction()
createInput.send(.viewDidAppear)
createInput.send(.setBookCover)
modifyInput.send(.loadBookCover)
}

Expand Down Expand Up @@ -233,7 +233,7 @@ final class BookCoverViewController: UIViewController {
let editBookViewController = EditBookViewController(viewModel: editBookViewModel)
navigationController?.pushViewController(editBookViewController, animated: true)
} catch {
MHLogger.error(error)
MHLogger.error(error.localizedDescription + #function)
}
}
}
Expand Down Expand Up @@ -489,7 +489,7 @@ extension BookCoverViewController: BookCategoryViewControllerDelegate {

self.present(navigationController, animated: true)
} catch {
MHLogger.error(error)
MHLogger.error(error.localizedDescription + #function)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Photos
// TODO: - 에러 처리 필요
final class CreateBookCoverViewModel: ViewModelType {
enum Input {
case viewDidAppear
case setBookCover
case changedBookTitle(title: String?)
case changedBookColor(colorIndex: Int)
case changedBookImage(bookImage: Data?)
Expand Down Expand Up @@ -60,7 +60,7 @@ final class CreateBookCoverViewModel: ViewModelType {
func transform(input: AnyPublisher<Input, Never>) -> AnyPublisher<Output, Never> {
input.sink { [weak self] event in
switch event {
case .viewDidAppear:
case .setBookCover:
self?.setBookColor(nowIndex: 0)
Task { try await self?.fetchMemorialHouseName() }
case .changedBookTitle(let title):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ final class BookCategoryViewController: UIViewController {

setup()
bind()
input.send(.viewDidLoad)
input.send(.fetchCategories)
configureNavigationBar()
configureConstraints()
}
Expand All @@ -62,11 +62,13 @@ final class BookCategoryViewController: UIViewController {
private func bind() {
let output = viewModel.transform(input: input.eraseToAnyPublisher())

output.sink { [weak self] event in
output
.receive(on: DispatchQueue.main)
.sink { [weak self] event in
switch event {
case .createdCategory, .updatedCategory, .fetchCategories, .deletedCategory:
self?.categoryTableView.reloadData()
case .failed(let errorMessage):
case .failure(let errorMessage):
self?.showErrorAlert(with: errorMessage)
}
}.store(in: &cancellables)
Expand Down Expand Up @@ -112,24 +114,18 @@ final class BookCategoryViewController: UIViewController {
normal: normalAttributes,
selected: selectedAttributes
) { [weak self] in
guard let self else { return }

let alert = UIAlertController(
title: "카테고리 추가",
message: "새로운 카테고리를 입력해주세요.",
textFieldConfiguration: { textField in
textField.placeholder = "카테고리 이름"
},
confirmHandler: { [weak self] newText in
guard let newText, !newText.isEmpty else {
MHLogger.error("입력한 카테고리 이름이 유효하지 않습니다.")
return
}
self?.input.send(.addCategory(text: newText))
self?.input.send(.createCategory(text: newText ?? ""))
}
)

self.present(alert, animated: true)
self?.present(alert, animated: true)
}
}

Expand Down Expand Up @@ -171,26 +167,20 @@ extension BookCategoryViewController: UITableViewDelegate {
style: .normal,
title: "수정"
) { [weak self] _, _, completion in
guard let self else { return }
let alert = UIAlertController(
title: "카테고리 수정",
message: "수정할 카테고리 이름을 입력해주세요.",
textFieldConfiguration: { textField in
textField.placeholder = "카테고리 이름"
textField.text = self.viewModel.categories[indexPath.row].name
textField.text = self?.viewModel.categories[indexPath.row].name
},
confirmHandler: { [weak self] newText in
guard let self, let newText = newText, !newText.isEmpty else {
MHLogger.error("수정할 카테고리 이름이 유효하지 않습니다.")
completion(false)
return
}
self.input.send(.updateCategory(index: indexPath.row, text: newText))
self?.input.send(.updateCategory(index: indexPath.row, text: newText ?? ""))
completion(true)
}
)

self.present(alert, animated: true)
self?.present(alert, animated: true)
}

let deleteAction = UIContextualAction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import MHCore

final class BookCategoryViewModel: ViewModelType {
enum Input {
case viewDidLoad
case addCategory(text: String)
case createCategory(text: String)
case fetchCategories
case updateCategory(index: Int, text: String)
case deleteCategory(index: Int)
}
Expand All @@ -15,7 +15,7 @@ final class BookCategoryViewModel: ViewModelType {
case fetchCategories
case updatedCategory
case deletedCategory
case failed(String)
case failure(String)
}

private let createBookCategoryUseCase: CreateBookCategoryUseCase
Expand Down Expand Up @@ -52,12 +52,12 @@ final class BookCategoryViewModel: ViewModelType {
input.sink { [weak self] event in
Task {
switch event {
case .viewDidLoad:
case .createCategory(let name):
await self?.createCategory(name: name)
case .fetchCategories:
await self?.fetchCategories()
case .addCategory(let text):
await self?.createCategory(text: text)
case .updateCategory(let index, let text):
await self?.updateCategory(index: index, text: text)
await self?.updateCategory(index: index, name: text)
case .deleteCategory(let index):
await self?.deleteCategory(index: index)
}
Expand All @@ -67,57 +67,59 @@ final class BookCategoryViewModel: ViewModelType {
return output.eraseToAnyPublisher()
}

// FIXME: MainActor 제거
@MainActor
private func createCategory(text: String) async {
private func createCategory(name: String) async {
guard validateCategoryName(with: name) else {
MHLogger.error("카테고리 생성 유효성 검증 실패: \(name)" + #function)
output.send(.failure("카테고리 생성 유효성 검증 실패"))
return
}

do {
let category = BookCategory(order: categories.count, name: text)
let category = BookCategory(order: categories.count, name: name)
try await createBookCategoryUseCase.execute(with: category)
categories.append(category)
output.send(.createdCategory)
} catch {
MHLogger.error("카테고리를 생성하는데 실패했습니다: \(error)")
output.send(.failed("카테고리를 생성하는데 실패했습니다"))
MHLogger.error("카테고리를 생성하는데 실패했습니다: \(error)" + #function)
output.send(.failure("카테고리를 생성하는데 실패했습니다"))
}
}

@MainActor
private func fetchCategories() async {
do {
let fetchedCategories = try await fetchBookCategoriesUseCase.execute()
categories.append(contentsOf: fetchedCategories)
output.send(.fetchCategories)
} catch {
MHLogger.error("카테고리를 불러오는데 실패했습니다: \(error)")
output.send(.failed("카테고리를 불러오는데 실패했습니다"))
MHLogger.error("카테고리를 불러오는데 실패했습니다: \(error)" + #function)
output.send(.failure("카테고리를 불러오는데 실패했습니다"))
}
}

@MainActor
private func updateCategory(index: Int, text: String) async {
guard index >= 0 && index < categories.count else {
MHLogger.error("유효하지 않은 인덱스: \(index)")
output.send(.failed("유효하지 않은 인덱스: \(index)"))
private func updateCategory(index: Int, name: String) async {
guard validateIndex(index),
validateCategoryName(with: name) else {
MHLogger.error("카테고리 업데이트 유효성 검증 실패: \(name)" + #function)
output.send(.failure("카테고리 업데이트 유효성 검증 실패"))
return
}

do {
let oldName = categories[index].name
let category = BookCategory(order: index, name: text)
let category = BookCategory(order: index, name: name)
try await updateBookCategoryUseCase.execute(oldName: oldName, with: category)
categories[index] = category
output.send(.updatedCategory)
} catch {
MHLogger.error("카테고리를 업데이트하는데 실패했습니다: \(error)")
output.send(.failed("카테고리를 업데이트하는데 실패했습니다"))
MHLogger.error("카테고리를 업데이트하는데 실패했습니다: \(error)" + #function)
output.send(.failure("카테고리를 업데이트하는데 실패했습니다"))
}
}

@MainActor
private func deleteCategory(index: Int) async {
guard index >= 0 && index < categories.count else {
MHLogger.error("유효하지 않은 인덱스: \(index)")
output.send(.failed("유효하지 않은 인덱스: \(index)"))
guard validateIndex(index) else {
MHLogger.error("카테고리 삭제 유효성 검증 실패: \(index)" + #function)
output.send(.failure("카테고리를 삭제하는데 실패했습니다"))
return
}

Expand All @@ -127,8 +129,55 @@ final class BookCategoryViewModel: ViewModelType {
categories.remove(at: index)
output.send(.deletedCategory)
} catch {
MHLogger.error("카테고리를 삭제하는데 실패했습니다: \(error)")
output.send(.failed("카테고리를 삭제하는데 실패했습니다"))
MHLogger.error("카테고리를 삭제하는데 실패했습니다: \(error)" + #function)
output.send(.failure("카테고리를 삭제하는데 실패했습니다"))
}
}

// MARK: - 카테고리 유효성 검사
private func validateCategoryName(with name: String) -> Bool {
return validateNonEmptyName(name)
&& validateUniqueName(name)
&& validateNameLength(name)
}

// 인덱스 유효성 검사
private func validateIndex(_ index: Int) -> Bool {
guard index >= 0 && index < categories.count else {
MHLogger.error("유효하지 않은 인덱스: \(index)" + #function)
output.send(.failure("유효하지 않은 인덱스: \(index)"))
return false
}
return true
}

// 공백 또는 빈 문자열 검사
private func validateNonEmptyName(_ name: String) -> Bool {
guard !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
MHLogger.error("카테고리 이름이 비어있거나 공백만 포함되어 있음: \(name)" + #function)
output.send(.failure("카테고리 이름은 공백일 수 없습니다."))
return false
}
return true
}

// 중복 이름 검사
private func validateUniqueName(_ name: String) -> Bool {
guard !categories.contains(where: { $0.name == name }) else {
MHLogger.error("중복된 카테고리 이름: \(name)" + #function)
output.send(.failure("이미 존재하는 카테고리: \(name)"))
return false
}
return true
}

// 이름 길이 검사
private func validateNameLength(_ name: String) -> Bool {
guard name.count <= 10 else {
MHLogger.error("카테고리 이름이 너무 깁니다: \(name)" + #function)
output.send(.failure("카테고리 이름은 10자 이하여야 합니다."))
return false
}
return true
}
}
Loading

0 comments on commit 6a7dc63

Please sign in to comment.