Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

프로젝트 매니저 [STEP 2] Hemg #313

Open
wants to merge 26 commits into
base: ic_9_hemg22
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7de4e2b
feat: tableView 생성
hemg2 Sep 21, 2023
af73086
feat: 파일 이동 및, 제목,내용 셀 생성
hemg2 Sep 26, 2023
68c6381
feat: 스택뷰 생성(테이블뷰 넣기)
hemg2 Sep 26, 2023
647d89e
refactor: 불필요 코드 제거
hemg2 Sep 26, 2023
9acef1b
feat: 팝업뷰 생성
hemg2 Sep 26, 2023
3fb04ce
feat: AddTODOViewController 구현
hemg2 Sep 27, 2023
4c34572
feat: CoreData 추가
hemg2 Sep 27, 2023
5cc83b0
feat: Model생성
hemg2 Sep 27, 2023
0283d80
feat: 델리게이트패턴 생성
hemg2 Sep 27, 2023
0d02fd0
refactor: Coredata 삭제
hemg2 Sep 28, 2023
adeb261
feat: 테이블뷰 데이터 이동
hemg2 Sep 28, 2023
9795be3
feat: 수정진행(델리게이트추가)
hemg2 Sep 29, 2023
6891729
feat: 네임스페이스 생성
hemg2 Sep 29, 2023
3e630d2
feat: dataManager 의존성 주입
hemg2 Sep 29, 2023
68d0c7b
feat: configureTableView 프로토콜생성
hemg2 Sep 29, 2023
9f2006c
feat: ItemUpdatable 생성
hemg2 Sep 29, 2023
b8462e4
refactor: 불필요코드 제거
hemg2 Sep 29, 2023
640478a
refactor: 프로토콜 -> 유스케이스 변환
hemg2 Sep 30, 2023
a7c1ce4
refactor: Items -> useCase이동
hemg2 Sep 30, 2023
985dd48
refactor: configureTableView -> useCase 이동
hemg2 Sep 30, 2023
77580b8
feat: AlertActionCreator 제거, move메서드 usecase 이동
hemg2 Oct 1, 2023
0e2e827
feat: MainViewModel 생성, 주입, 로직 변경
hemg2 Oct 1, 2023
d6cf6fa
Merge branch 'step2' of https://github.com/hemg2/ios-project-manager …
hemg2 Oct 1, 2023
8b65688
feat: SwipeAction 추가
hemg2 Oct 2, 2023
6d7bd96
refactor: 불필요코드 제거
hemg2 Oct 3, 2023
81240ed
refactor: countLabel background설정
hemg2 Oct 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 125 additions & 5 deletions ProjectManager/ProjectManager.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
objects = {

/* Begin PBXBuildFile section */
63C5ECB62AC1949A003904C4 /* ListTitleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C5ECB52AC1949A003904C4 /* ListTitleCell.swift */; };
63C5ECB82AC2B957003904C4 /* DescriptionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C5ECB72AC2B957003904C4 /* DescriptionCell.swift */; };
63C5ECBA2AC2FE9A003904C4 /* AddTodoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C5ECB92AC2FE9A003904C4 /* AddTodoViewController.swift */; };
63C5ECCA2AC46CDB003904C4 /* ProjectManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C5ECC92AC46CDA003904C4 /* ProjectManager.swift */; };
63C5ECCD2AC46F83003904C4 /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C5ECCC2AC46F83003904C4 /* DataManager.swift */; };
63CC81602AC6AA5B00E6355F /* ReuseIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CC815F2AC6AA5B00E6355F /* ReuseIdentifier.swift */; };
63CC816E2AC6D8CA00E6355F /* CellTitleNamespace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CC816D2AC6D8CA00E6355F /* CellTitleNamespace.swift */; };
63CC81712AC7F5B600E6355F /* MainViewControllerUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CC81702AC7F5B600E6355F /* MainViewControllerUseCase.swift */; };
63CC81752AC95D7D00E6355F /* TitleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CC81742AC95D7D00E6355F /* TitleItem.swift */; };
63CC81782AC9665900E6355F /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63CC81772AC9665900E6355F /* MainViewModel.swift */; };
C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; };
C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; };
C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0925F51E1D0094C4CF /* MainViewController.swift */; };
Expand All @@ -15,6 +25,16 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
63C5ECB52AC1949A003904C4 /* ListTitleCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTitleCell.swift; sourceTree = "<group>"; };
63C5ECB72AC2B957003904C4 /* DescriptionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionCell.swift; sourceTree = "<group>"; };
63C5ECB92AC2FE9A003904C4 /* AddTodoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTodoViewController.swift; sourceTree = "<group>"; };
63C5ECC92AC46CDA003904C4 /* ProjectManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectManager.swift; sourceTree = "<group>"; };
63C5ECCC2AC46F83003904C4 /* DataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataManager.swift; sourceTree = "<group>"; };
63CC815F2AC6AA5B00E6355F /* ReuseIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseIdentifier.swift; sourceTree = "<group>"; };
63CC816D2AC6D8CA00E6355F /* CellTitleNamespace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellTitleNamespace.swift; sourceTree = "<group>"; };
63CC81702AC7F5B600E6355F /* MainViewControllerUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewControllerUseCase.swift; sourceTree = "<group>"; };
63CC81742AC95D7D00E6355F /* TitleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleItem.swift; sourceTree = "<group>"; };
63CC81772AC9665900E6355F /* MainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = "<group>"; };
C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; };
C7431F0525F51E1D0094C4CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -35,6 +55,95 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
63C5ECB22AC1944F003904C4 /* App */ = {
isa = PBXGroup;
children = (
C7431F0525F51E1D0094C4CF /* AppDelegate.swift */,
C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */,
);
path = App;
sourceTree = "<group>";
};
63C5ECB32AC19459003904C4 /* Controller */ = {
isa = PBXGroup;
children = (
63CC81762AC9663D00E6355F /* MainView */,
63CC81792AC9726100E6355F /* AddTodoView */,
);
path = Controller;
sourceTree = "<group>";
};
63C5ECB42AC19464003904C4 /* View */ = {
isa = PBXGroup;
children = (
C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */,
63C5ECB52AC1949A003904C4 /* ListTitleCell.swift */,
63C5ECB72AC2B957003904C4 /* DescriptionCell.swift */,
);
path = View;
sourceTree = "<group>";
};
63C5ECBB2AC45243003904C4 /* Model */ = {
isa = PBXGroup;
children = (
63CC815E2AC6AA3600E6355F /* Namespace */,
63C5ECCB2AC46F4E003904C4 /* DataManager */,
63C5ECBE2AC45277003904C4 /* Entity */,
);
path = Model;
sourceTree = "<group>";
};
63C5ECBE2AC45277003904C4 /* Entity */ = {
isa = PBXGroup;
children = (
63C5ECC92AC46CDA003904C4 /* ProjectManager.swift */,
63CC81742AC95D7D00E6355F /* TitleItem.swift */,
);
path = Entity;
sourceTree = "<group>";
};
63C5ECCB2AC46F4E003904C4 /* DataManager */ = {
isa = PBXGroup;
children = (
63C5ECCC2AC46F83003904C4 /* DataManager.swift */,
);
path = DataManager;
sourceTree = "<group>";
};
63CC815E2AC6AA3600E6355F /* Namespace */ = {
isa = PBXGroup;
children = (
63CC815F2AC6AA5B00E6355F /* ReuseIdentifier.swift */,
63CC816D2AC6D8CA00E6355F /* CellTitleNamespace.swift */,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReuseIdentifier 도 Namespace 인가요 ?

);
path = Namespace;
sourceTree = "<group>";
};
63CC816F2AC7F59700E6355F /* UseCase */ = {
isa = PBXGroup;
children = (
63CC81702AC7F5B600E6355F /* MainViewControllerUseCase.swift */,
);
path = UseCase;
sourceTree = "<group>";
};
63CC81762AC9663D00E6355F /* MainView */ = {
isa = PBXGroup;
children = (
C7431F0925F51E1D0094C4CF /* MainViewController.swift */,
63CC81772AC9665900E6355F /* MainViewModel.swift */,
);
path = MainView;
sourceTree = "<group>";
};
63CC81792AC9726100E6355F /* AddTodoView */ = {
isa = PBXGroup;
children = (
63C5ECB92AC2FE9A003904C4 /* AddTodoViewController.swift */,
);
path = AddTodoView;
sourceTree = "<group>";
};
C7431EF925F51E1D0094C4CF = {
isa = PBXGroup;
children = (
Expand All @@ -54,12 +163,13 @@
C7431F0425F51E1D0094C4CF /* ProjectManager */ = {
isa = PBXGroup;
children = (
C7431F0525F51E1D0094C4CF /* AppDelegate.swift */,
C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */,
C7431F0925F51E1D0094C4CF /* MainViewController.swift */,
C7431F0E25F51E1E0094C4CF /* Assets.xcassets */,
C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */,
63CC816F2AC7F59700E6355F /* UseCase */,
63C5ECBB2AC45243003904C4 /* Model */,
63C5ECB22AC1944F003904C4 /* App */,
63C5ECB32AC19459003904C4 /* Controller */,
63C5ECB42AC19464003904C4 /* View */,
C7431F1325F51E1E0094C4CF /* Info.plist */,
C7431F0E25F51E1E0094C4CF /* Assets.xcassets */,
);
path = ProjectManager;
sourceTree = "<group>";
Expand Down Expand Up @@ -133,9 +243,19 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
63CC81752AC95D7D00E6355F /* TitleItem.swift in Sources */,
63C5ECCA2AC46CDB003904C4 /* ProjectManager.swift in Sources */,
63C5ECCD2AC46F83003904C4 /* DataManager.swift in Sources */,
63CC816E2AC6D8CA00E6355F /* CellTitleNamespace.swift in Sources */,
C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */,
63CC81782AC9665900E6355F /* MainViewModel.swift in Sources */,
63C5ECB62AC1949A003904C4 /* ListTitleCell.swift in Sources */,
63C5ECBA2AC2FE9A003904C4 /* AddTodoViewController.swift in Sources */,
C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */,
C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */,
63CC81602AC6AA5B00E6355F /* ReuseIdentifier.swift in Sources */,
63CC81712AC7F5B600E6355F /* MainViewControllerUseCase.swift in Sources */,
63C5ECB82AC2B957003904C4 /* DescriptionCell.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let dataManager: DataManagerProtocol = DataManager()
let useCase: MainViewControllerUseCase = MainViewControllerUseCaseImplementation()
let viewModel = MainViewModel(useCase: useCase)

window = UIWindow(windowScene: windowScene)
let mainViewController = MainViewController()
let mainViewController = MainViewController(dataManager: dataManager, useCase: useCase, viewModel: viewModel)
let navigationController = UINavigationController(rootViewController: mainViewController)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//
// AddTodoViewController.swift
// ProjectManager
//
// Created by Hemg on 2023/09/26.
//

import UIKit

protocol AddTodoDelegate: AnyObject {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AnyObject 는 무엇일까요 ?

func didAddTodoItem(title: String, body: String, date: Date)
func didEditTodoItem(title: String, body: String, date: Date, index: Int)
}

final class AddTodoViewController: UIViewController {
private let titleTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = .preferredFont(forTextStyle: .title1)
textField.placeholder = "Title"
textField.layer.borderColor = UIColor.lightGray.cgColor
textField.layer.borderWidth = 1.0

return textField
}()

private let bodyTextView: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.font = .preferredFont(forTextStyle: .body)
textView.text = "여기는 할일 내용 입력하는 곳입니다."
textView.textColor = .placeholderText
textView.layer.borderColor = UIColor.lightGray.cgColor
textView.layer.borderWidth = 1.0

return textView
}()

private let datePicker: UIDatePicker = {
let datePicker = UIDatePicker()
datePicker.translatesAutoresizingMaskIntoConstraints = false
datePicker.preferredDatePickerStyle = .wheels
datePicker.datePickerMode = .date

return datePicker
}()

weak var delegate: AddTodoDelegate?
private var todoItems: ProjectManager?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todoItems: ProjectManager?
조금 어색하게 읽히는거같은데요. 어떤 의도인지 말씀부탁드립니다.

private var isNew: Bool

init() {
self.isNew = true
super.init(nibName: nil, bundle: nil)
}

init(todoItems: ProjectManager?) {
self.isNew = false
self.todoItems = todoItems
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

setUpViewController()
setUpBarButtonItem()
configureUI()
setUpViewLayout()
setUpItemValues()
}

private func setUpViewController() {
view.backgroundColor = .systemBackground
title = "TODO"
bodyTextView.delegate = self
}

private func setUpBarButtonItem() {
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneButton))
navigationItem.rightBarButtonItem = doneButton

if isNew == true {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional도 아닌데 == 이 필요할까요 ?
이름이 충분이 bool 을 커버할거 같습니다.

let cancelButton = UIBarButtonItem(title: "Cancel", style: .done, target: self, action: #selector(cancelButton))
navigationItem.leftBarButtonItem = cancelButton
} else {
let editButton = UIBarButtonItem(title: "Edit", style: .done, target: self, action: #selector(editButton))
navigationItem.leftBarButtonItem = editButton
}
}

private func setUpItemValues() {
if let todoItems = todoItems {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맨 앞줄이라면 guard문도 좋은 선택지가 될 것 같습니다.

titleTextField.text = todoItems.title
bodyTextView.text = todoItems.body
datePicker.date = todoItems.date
}
}

@objc private func doneButton() {
setUpItemText()
dismiss(animated: true)
}

@objc private func cancelButton() {
dismiss(animated: true)
}

@objc private func editButton() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

눌렸을때 메서드명이 조금 이상하네요.

setUpItemText()
dismiss(animated: true)
}

private func configureUI() {
view.addSubview(titleTextField)
view.addSubview(bodyTextView)
view.addSubview(datePicker)
}

private func setUpViewLayout() {
NSLayoutConstraint.activate([
titleTextField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
titleTextField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 4),
titleTextField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -4),

datePicker.topAnchor.constraint(equalTo: titleTextField.bottomAnchor, constant: 4),
datePicker.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 4),
datePicker.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -4),

bodyTextView.topAnchor.constraint(equalTo: datePicker.bottomAnchor, constant: 4),
bodyTextView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 4),
bodyTextView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -4),
bodyTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -4)
])
}

private func setUpItemText() {
if isNew == false {
let date = datePicker.date
guard let titleText = titleTextField.text,
let bodyText = bodyTextView.text else { return }
todoItems?.title = titleText
todoItems?.body = bodyText
todoItems?.date = date

delegate?.didEditTodoItem(title: titleText, body: bodyText, date: date, index: 0)
} else {
let date = datePicker.date
guard let titleText = titleTextField.text,
let bodyText = bodyTextView.text else { return }

delegate?.didAddTodoItem(title: titleText, body: bodyText, date: date)
}
}
}

extension AddTodoViewController: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.text == "여기는 할일 내용 입력하는 곳입니다." {
textView.text = ""
textView.textColor = .label
}
}

func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "여기는 할일 내용 입력하는 곳입니다."
textView.textColor = .placeholderText
}
}

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let currentText = textView.text ?? ""
guard let stringRange = Range(range, in: currentText) else { return false }
let changedText = currentText.replacingCharacters(in: stringRange, with: text)

return changedText.count <= 999
}
}
Loading