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-1] maxhyunm #305

Open
wants to merge 20 commits into
base: ic_9_maxhyunm
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
133 changes: 127 additions & 6 deletions ProjectManager/ProjectManager.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,44 @@
BA6463102AB94FA10080E80D /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = BA64630F2AB94FA10080E80D /* FirebaseFirestore */; };
BA6463122AB94FA10080E80D /* FirebaseFirestoreCombine-Community in Frameworks */ = {isa = PBXBuildFile; productRef = BA6463112AB94FA10080E80D /* FirebaseFirestoreCombine-Community */; };
BA6463142AB94FA10080E80D /* FirebaseFirestoreSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BA6463132AB94FA10080E80D /* FirebaseFirestoreSwift */; };
BA6463582AC042A90080E80D /* ToDo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BA6463562AC042A90080E80D /* ToDo.xcdatamodeld */; };
BA64635F2AC043680080E80D /* ToDo+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA64635D2AC043680080E80D /* ToDo+CoreDataClass.swift */; };
BA6463602AC043680080E80D /* ToDo+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA64635E2AC043680080E80D /* ToDo+CoreDataProperties.swift */; };
BA6463642AC043F50080E80D /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463632AC043F50080E80D /* CoreDataManager.swift */; };
BA6463662AC0440E0080E80D /* CoreDataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463652AC0440E0080E80D /* CoreDataError.swift */; };
BA6463682AC044D70080E80D /* ToDoStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463672AC044D70080E80D /* ToDoStatus.swift */; };
BA64636A2AC045380080E80D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463692AC045380080E80D /* Observable.swift */; };
BA64636D2AC045700080E80D /* ViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA64636C2AC045700080E80D /* ViewModelProtocol.swift */; };
BA64636F2AC04A700080E80D /* ToDoListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA64636E2AC04A700080E80D /* ToDoListViewModel.swift */; };
BA6463732AC04F590080E80D /* ToDoListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463722AC04F590080E80D /* ToDoListView.swift */; };
BA6463752AC04F920080E80D /* ToDoListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463742AC04F920080E80D /* ToDoListHeaderView.swift */; };
BA6463782AC04FE40080E80D /* AlertBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463772AC04FE40080E80D /* AlertBuilder.swift */; };
BA64637A2AC050AD0080E80D /* ToDoListViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6463792AC050AD0080E80D /* ToDoListViewCell.swift */; };
C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; };
C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; };
C7431F0A25F51E1D0094C4CF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0925F51E1D0094C4CF /* ViewController.swift */; };
C7431F0A25F51E1D0094C4CF /* ToDoListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0925F51E1D0094C4CF /* ToDoListViewController.swift */; };
C7431F0F25F51E1E0094C4CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7431F0E25F51E1E0094C4CF /* Assets.xcassets */; };
C7431F1225F51E1E0094C4CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
BA6463572AC042A90080E80D /* ToDo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = ToDo.xcdatamodel; sourceTree = "<group>"; };
BA64635D2AC043680080E80D /* ToDo+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ToDo+CoreDataClass.swift"; sourceTree = "<group>"; };
BA64635E2AC043680080E80D /* ToDo+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ToDo+CoreDataProperties.swift"; sourceTree = "<group>"; };
BA6463632AC043F50080E80D /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = "<group>"; };
BA6463652AC0440E0080E80D /* CoreDataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataError.swift; sourceTree = "<group>"; };
BA6463672AC044D70080E80D /* ToDoStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoStatus.swift; sourceTree = "<group>"; };
BA6463692AC045380080E80D /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = "<group>"; };
BA64636C2AC045700080E80D /* ViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelProtocol.swift; sourceTree = "<group>"; };
BA64636E2AC04A700080E80D /* ToDoListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListViewModel.swift; sourceTree = "<group>"; };
BA6463722AC04F590080E80D /* ToDoListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListView.swift; sourceTree = "<group>"; };
BA6463742AC04F920080E80D /* ToDoListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListHeaderView.swift; sourceTree = "<group>"; };
BA6463772AC04FE40080E80D /* AlertBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertBuilder.swift; sourceTree = "<group>"; };
BA6463792AC050AD0080E80D /* ToDoListViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListViewCell.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>"; };
C7431F0925F51E1D0094C4CF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
C7431F0925F51E1D0094C4CF /* ToDoListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListViewController.swift; sourceTree = "<group>"; };
C7431F0E25F51E1E0094C4CF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C7431F1125F51E1E0094C4CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
C7431F1325F51E1E0094C4CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand All @@ -45,6 +71,73 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
BA6463612AC043CE0080E80D /* Model */ = {
isa = PBXGroup;
children = (
BA6463622AC043D30080E80D /* CoreData */,
BA6463672AC044D70080E80D /* ToDoStatus.swift */,
BA6463692AC045380080E80D /* Observable.swift */,
);
path = Model;
sourceTree = "<group>";
};
BA6463622AC043D30080E80D /* CoreData */ = {
isa = PBXGroup;
children = (
BA64635D2AC043680080E80D /* ToDo+CoreDataClass.swift */,
BA64635E2AC043680080E80D /* ToDo+CoreDataProperties.swift */,
BA6463632AC043F50080E80D /* CoreDataManager.swift */,
BA6463652AC0440E0080E80D /* CoreDataError.swift */,
);
path = CoreData;
sourceTree = "<group>";
};
BA64636B2AC0455B0080E80D /* ViewModel */ = {
isa = PBXGroup;
children = (
BA64636C2AC045700080E80D /* ViewModelProtocol.swift */,
BA64636E2AC04A700080E80D /* ToDoListViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
BA6463702AC04AAB0080E80D /* List */ = {
isa = PBXGroup;
children = (
BA64636B2AC0455B0080E80D /* ViewModel */,
BA64637B2AC0613D0080E80D /* Cell */,
C7431F0925F51E1D0094C4CF /* ToDoListViewController.swift */,
BA6463722AC04F590080E80D /* ToDoListView.swift */,
);
path = List;
sourceTree = "<group>";
};
BA6463712AC04AC50080E80D /* App */ = {
isa = PBXGroup;
children = (
C7431F0525F51E1D0094C4CF /* AppDelegate.swift */,
C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */,
);
path = App;
sourceTree = "<group>";
};
BA6463762AC04FDA0080E80D /* Builder */ = {
isa = PBXGroup;
children = (
BA6463772AC04FE40080E80D /* AlertBuilder.swift */,
);
path = Builder;
sourceTree = "<group>";
};
BA64637B2AC0613D0080E80D /* Cell */ = {
isa = PBXGroup;
children = (
BA6463742AC04F920080E80D /* ToDoListHeaderView.swift */,
BA6463792AC050AD0080E80D /* ToDoListViewCell.swift */,
);
path = Cell;
sourceTree = "<group>";
};
C7431EF925F51E1D0094C4CF = {
isa = PBXGroup;
children = (
Expand All @@ -64,12 +157,14 @@
C7431F0425F51E1D0094C4CF /* ProjectManager */ = {
isa = PBXGroup;
children = (
C7431F0525F51E1D0094C4CF /* AppDelegate.swift */,
C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */,
C7431F0925F51E1D0094C4CF /* ViewController.swift */,
BA6463712AC04AC50080E80D /* App */,
BA6463762AC04FDA0080E80D /* Builder */,
BA6463612AC043CE0080E80D /* Model */,
BA6463702AC04AAB0080E80D /* List */,
C7431F0E25F51E1E0094C4CF /* Assets.xcassets */,
C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */,
C7431F1325F51E1E0094C4CF /* Info.plist */,
BA6463562AC042A90080E80D /* ToDo.xcdatamodeld */,
);
path = ProjectManager;
sourceTree = "<group>";
Expand Down Expand Up @@ -153,9 +248,22 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C7431F0A25F51E1D0094C4CF /* ViewController.swift in Sources */,
C7431F0A25F51E1D0094C4CF /* ToDoListViewController.swift in Sources */,
BA64637A2AC050AD0080E80D /* ToDoListViewCell.swift in Sources */,
C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */,
C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */,
BA6463752AC04F920080E80D /* ToDoListHeaderView.swift in Sources */,
BA64636D2AC045700080E80D /* ViewModelProtocol.swift in Sources */,
BA64636F2AC04A700080E80D /* ToDoListViewModel.swift in Sources */,
BA6463662AC0440E0080E80D /* CoreDataError.swift in Sources */,
BA6463602AC043680080E80D /* ToDo+CoreDataProperties.swift in Sources */,
BA6463682AC044D70080E80D /* ToDoStatus.swift in Sources */,
BA6463782AC04FE40080E80D /* AlertBuilder.swift in Sources */,
BA6463732AC04F590080E80D /* ToDoListView.swift in Sources */,
BA64636A2AC045380080E80D /* Observable.swift in Sources */,
BA64635F2AC043680080E80D /* ToDo+CoreDataClass.swift in Sources */,
BA6463642AC043F50080E80D /* CoreDataManager.swift in Sources */,
BA6463582AC042A90080E80D /* ToDo.xcdatamodeld in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -388,6 +496,19 @@
productName = FirebaseFirestoreSwift;
};
/* End XCSwiftPackageProductDependency section */

/* Begin XCVersionGroup section */
BA6463562AC042A90080E80D /* ToDo.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
BA6463572AC042A90080E80D /* ToDo.xcdatamodel */,
);
currentVersion = BA6463572AC042A90080E80D /* ToDo.xcdatamodel */;
path = ToDo.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = C7431EFA25F51E1D0094C4CF /* Project object */;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C7431F0125F51E1D0094C4CF"
BuildableName = "ProjectManager.app"
BlueprintName = "ProjectManager"
ReferencedContainer = "container:ProjectManager.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C7431F0125F51E1D0094C4CF"
BuildableName = "ProjectManager.app"
BlueprintName = "ProjectManager"
ReferencedContainer = "container:ProjectManager.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C7431F0125F51E1D0094C4CF"
BuildableName = "ProjectManager.app"
BlueprintName = "ProjectManager"
ReferencedContainer = "container:ProjectManager.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// ProjectManager - AppDelegate.swift
// Created by yagom.
// Copyright © yagom. All rights reserved.
//
// Last modified by Max.

import UIKit

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// ProjectManager - SceneDelegate.swift
// Created by yagom.
// Copyright © yagom. All rights reserved.
//
// Last modified by Max.

import UIKit

Expand All @@ -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 }

window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
let coreDataManager = CoreDataManager()
let baseViewController = ToDoListViewController(coreDataManager)
Copy link

Choose a reason for hiding this comment

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

뷰 모델은 왜 외부에서 주입받지 않고, 뷰컨 내부에서 생성해줄까요?

Copy link
Author

Choose a reason for hiding this comment

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

coreDataManager의 경우 하나를 만들어 공유할 일이 많지만 ViewModel은 뷰/뷰컨트롤러에 따라 제각기 다른 뷰모델을 만들어야 할 수 있기 때문에 전부 SceneDelegate에서 따로 만들어서 주입하게 되면 너무 복잡할 것 같다고 생각하여 위처럼 작업하였습니다!

Copy link

Choose a reason for hiding this comment

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

요거는 만들어서 주입해주는게 좋을거 같아요
dependency injection과 testing도 같이 공부해보시면 좋을거 같아여
요건 민망하지만 제가 2년전쯤 쓴 블로그

Copy link
Author

Choose a reason for hiding this comment

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

왓 감사합니다ㅠㅠㅠ!!!! 참고하여 공부할게요!!!

Copy link

Choose a reason for hiding this comment

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

https://techblog.woowahan.com/2704/ 요것도 보세여 다른 글 들도 보시구

let navigationViewController = UINavigationController(rootViewController: baseViewController)
window?.rootViewController = navigationViewController
window?.makeKeyAndVisible()
}

Expand Down
81 changes: 81 additions & 0 deletions ProjectManager/ProjectManager/Builder/AlertBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// AlertBuilder.swift
// ProjectManager
//
// Created by Max on 2023/09/24.
//

import UIKit

final class AlertBuilder {
let alertController: UIAlertController
private let viewController: UIViewController
havilog marked this conversation as resolved.
Show resolved Hide resolved
private var controllerTitle: String = ""
private var controllerMessage: String = ""
private var alertActions: [UIAlertAction] = []

init(viewController: UIViewController, prefferedStyle: UIAlertController.Style) {
self.viewController = viewController
self.alertController = UIAlertController(title: nil, message: nil, preferredStyle: prefferedStyle)
}

func setControllerTitle(title: String) {
self.controllerTitle = title
}

func setControllerMessage(message: String) {
self.controllerMessage = message
}

func addAction(_ actionType: AlertActionType, action: ((UIAlertAction) -> Void)? = nil) {
let action = UIAlertAction(title: actionType.title, style: actionType.style, handler: action)
alertActions.append(action)
}

@discardableResult
func show() -> Self {
alertController.title = controllerTitle
alertController.message = controllerMessage
alertActions.forEach { alertController.addAction($0) }

viewController.present(alertController, animated: true)

return self
}
}

extension AlertBuilder {
enum AlertActionType {
case confirm
case cancel
case delete
case other(title: String, style: UIAlertAction.Style)

var title: String {
switch self {
case .confirm:
return "확인"
case .cancel:
return "취소"
case .delete:
return "삭제"
case .other(let title, _):
return title
}
}

var style: UIAlertAction.Style {
switch self {
case .cancel:
return .cancel
case .delete:
return .destructive
case .other(_, let style):
return style
default:
return .default
}
}
}
}

Loading