diff --git a/BankManagerConsoleApp/.swiftlint.yml b/BankManagerConsoleApp/.swiftlint.yml index e0d71201..c18004e9 100644 --- a/BankManagerConsoleApp/.swiftlint.yml +++ b/BankManagerConsoleApp/.swiftlint.yml @@ -4,6 +4,7 @@ disabled_rules: - trailing_comma - class_delegate_protocol - sorted_imports +- closure_parameter_position force_unwrapping: severity: error diff --git a/BankManagerConsoleApp/BankManagerConsoleApp.xcodeproj/project.pbxproj b/BankManagerConsoleApp/BankManagerConsoleApp.xcodeproj/project.pbxproj index 767473e1..73c3e8e5 100644 --- a/BankManagerConsoleApp/BankManagerConsoleApp.xcodeproj/project.pbxproj +++ b/BankManagerConsoleApp/BankManagerConsoleApp.xcodeproj/project.pbxproj @@ -7,30 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - 2776CBD12B6B844F00EF7155 /* TextInputReadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2776CBD02B6B844F00EF7155 /* TextInputReadable.swift */; }; - 2776CBD32B6B852F00EF7155 /* BankManagerAppMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2776CBD22B6B852F00EF7155 /* BankManagerAppMenu.swift */; }; - 2776CBD52B6B854C00EF7155 /* IOError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2776CBD42B6B854C00EF7155 /* IOError.swift */; }; - 2776CBD72B6B8BE900EF7155 /* LinkedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2776CBD62B6B8BE900EF7155 /* LinkedList.swift */; }; - 2776CBD92B6B8C1400EF7155 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2776CBD82B6B8C1400EF7155 /* Node.swift */; }; C7694E7A259C3EC00053667F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E79259C3EC00053667F /* main.swift */; }; - C7D65D1B259C8190005510E0 /* BankManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D65D1A259C8190005510E0 /* BankManager.swift */; }; - F475FC6C2B6CC0EF00B0E859 /* BankManagerAppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F475FC6B2B6CC0EF00B0E859 /* BankManagerAppError.swift */; }; - F4EC3C312B6A57B0006C9D11 /* BankTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C302B6A57B0006C9D11 /* BankTask.swift */; }; - F4EC3C332B6A5B82006C9D11 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C322B6A5B82006C9D11 /* Queue.swift */; }; - F4EC3C352B6A6BD1006C9D11 /* Order.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C342B6A6BD1006C9D11 /* Order.swift */; }; - F4EC3C392B6A6C05006C9D11 /* TaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C382B6A6C05006C9D11 /* TaskManager.swift */; }; - F4EC3C3B2B6A6C18006C9D11 /* BankerEnqueuable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C3A2B6A6C18006C9D11 /* BankerEnqueuable.swift */; }; - F4EC3C3F2B6A6CC3006C9D11 /* BankRunnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C3E2B6A6CC3006C9D11 /* BankRunnable.swift */; }; - F4EC3C412B6A6CE6006C9D11 /* TaskManagable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C402B6A6CE6006C9D11 /* TaskManagable.swift */; }; - F4EC3C432B6A6D06006C9D11 /* Banker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C422B6A6D06006C9D11 /* Banker.swift */; }; - F4EC3C452B6A6D15006C9D11 /* ClientTaskHandlable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C442B6A6D15006C9D11 /* ClientTaskHandlable.swift */; }; - F4EC3C472B6A6D6F006C9D11 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C462B6A6D6F006C9D11 /* Client.swift */; }; - F4EC3C492B6A70E7006C9D11 /* ClientEnqueuable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C482B6A70E7006C9D11 /* ClientEnqueuable.swift */; }; - F4EC3C4B2B6A71E0006C9D11 /* TextOutputDisplayable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C4A2B6A71E0006C9D11 /* TextOutputDisplayable.swift */; }; - F4EC3C4D2B6A71F8006C9D11 /* ConsoleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C4C2B6A71F8006C9D11 /* ConsoleManager.swift */; }; - F4EC3C502B6B6701006C9D11 /* BankManagerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C4F2B6B6701006C9D11 /* BankManagerApp.swift */; }; - F4EC3C522B6B7F9A006C9D11 /* TicketDispenser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C512B6B7F9A006C9D11 /* TicketDispenser.swift */; }; - F4EC3C542B6B7FAE006C9D11 /* TicketProvidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C532B6B7FAE006C9D11 /* TicketProvidable.swift */; }; + F46FA2A42B63A46A00787C3F /* BankManagerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FA2A32B63A46A00787C3F /* BankManagerApp.swift */; }; + F46FA2A82B66309000787C3F /* ConsoleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FA2A72B66309000787C3F /* ConsoleManager.swift */; }; + F46FA2AA2B6630B200787C3F /* TextInputReadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FA2A92B6630B200787C3F /* TextInputReadable.swift */; }; + F46FA2AC2B6630CF00787C3F /* BankManagerAppMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FA2AB2B6630CF00787C3F /* BankManagerAppMenu.swift */; }; + F46FA2AE2B6630E300787C3F /* IOError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FA2AD2B6630E300787C3F /* IOError.swift */; }; + F4EC3C292B6784BB006C9D11 /* TextOutputDisplayable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EC3C282B6784BB006C9D11 /* TextOutputDisplayable.swift */; }; + F4EDF9F72B5F749400C3E54C /* QueueTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EDF9F62B5F749400C3E54C /* QueueTest.swift */; }; + F4EDFA022B5FADF200C3E54C /* LinkedListTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EDFA012B5FADF200C3E54C /* LinkedListTest.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -46,31 +31,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 2776CBD02B6B844F00EF7155 /* TextInputReadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextInputReadable.swift; sourceTree = ""; }; - 2776CBD22B6B852F00EF7155 /* BankManagerAppMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankManagerAppMenu.swift; sourceTree = ""; }; - 2776CBD42B6B854C00EF7155 /* IOError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOError.swift; sourceTree = ""; }; - 2776CBD62B6B8BE900EF7155 /* LinkedList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedList.swift; sourceTree = ""; }; - 2776CBD82B6B8C1400EF7155 /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; C7694E76259C3EC00053667F /* BankManagerConsoleApp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BankManagerConsoleApp; sourceTree = BUILT_PRODUCTS_DIR; }; C7694E79259C3EC00053667F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; - C7D65D1A259C8190005510E0 /* BankManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankManager.swift; sourceTree = ""; }; - F475FC6B2B6CC0EF00B0E859 /* BankManagerAppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankManagerAppError.swift; sourceTree = ""; }; - F4EC3C302B6A57B0006C9D11 /* BankTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankTask.swift; sourceTree = ""; }; - F4EC3C322B6A5B82006C9D11 /* Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; - F4EC3C342B6A6BD1006C9D11 /* Order.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Order.swift; sourceTree = ""; }; - F4EC3C382B6A6C05006C9D11 /* TaskManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManager.swift; sourceTree = ""; }; - F4EC3C3A2B6A6C18006C9D11 /* BankerEnqueuable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankerEnqueuable.swift; sourceTree = ""; }; - F4EC3C3E2B6A6CC3006C9D11 /* BankRunnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankRunnable.swift; sourceTree = ""; }; - F4EC3C402B6A6CE6006C9D11 /* TaskManagable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManagable.swift; sourceTree = ""; }; - F4EC3C422B6A6D06006C9D11 /* Banker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Banker.swift; sourceTree = ""; }; - F4EC3C442B6A6D15006C9D11 /* ClientTaskHandlable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTaskHandlable.swift; sourceTree = ""; }; - F4EC3C462B6A6D6F006C9D11 /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; - F4EC3C482B6A70E7006C9D11 /* ClientEnqueuable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientEnqueuable.swift; sourceTree = ""; }; - F4EC3C4A2B6A71E0006C9D11 /* TextOutputDisplayable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextOutputDisplayable.swift; sourceTree = ""; }; - F4EC3C4C2B6A71F8006C9D11 /* ConsoleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleManager.swift; sourceTree = ""; }; - F4EC3C4F2B6B6701006C9D11 /* BankManagerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankManagerApp.swift; sourceTree = ""; }; - F4EC3C512B6B7F9A006C9D11 /* TicketDispenser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TicketDispenser.swift; sourceTree = ""; }; - F4EC3C532B6B7FAE006C9D11 /* TicketProvidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TicketProvidable.swift; sourceTree = ""; }; + F447FC382B5E50C7002A2692 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + F46FA2A32B63A46A00787C3F /* BankManagerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankManagerApp.swift; sourceTree = ""; }; + F46FA2A72B66309000787C3F /* ConsoleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleManager.swift; sourceTree = ""; }; + F46FA2A92B6630B200787C3F /* TextInputReadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextInputReadable.swift; sourceTree = ""; }; + F46FA2AB2B6630CF00787C3F /* BankManagerAppMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankManagerAppMenu.swift; sourceTree = ""; }; + F46FA2AD2B6630E300787C3F /* IOError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOError.swift; sourceTree = ""; }; + F4EC3C282B6784BB006C9D11 /* TextOutputDisplayable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextOutputDisplayable.swift; sourceTree = ""; }; + F4EDF9F42B5F749400C3E54C /* BankManagerConsoleAppTest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BankManagerConsoleAppTest.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F4EDF9F62B5F749400C3E54C /* QueueTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueTest.swift; sourceTree = ""; }; + F4EDFA012B5FADF200C3E54C /* LinkedListTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedListTest.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -114,35 +86,14 @@ 2776CBDC2B6B8CB400EF7155 /* View */ = { isa = PBXGroup; children = ( - 2776CBD42B6B854C00EF7155 /* IOError.swift */, - F4EC3C4C2B6A71F8006C9D11 /* ConsoleManager.swift */, + C7694E79259C3EC00053667F /* main.swift */, + F4EC3C2A2B678562006C9D11 /* App */, + F4EC3C272B678463006C9D11 /* View */, ); path = View; sourceTree = ""; }; - 2776CBDD2B6B8D4300EF7155 /* Model */ = { - isa = PBXGroup; - children = ( - F4EC3C512B6B7F9A006C9D11 /* TicketDispenser.swift */, - C7D65D1A259C8190005510E0 /* BankManager.swift */, - F4EC3C382B6A6C05006C9D11 /* TaskManager.swift */, - F4EC3C422B6A6D06006C9D11 /* Banker.swift */, - 2776CBDE2B6B8D5000EF7155 /* Value */, - ); - path = Model; - sourceTree = ""; - }; - 2776CBDE2B6B8D5000EF7155 /* Value */ = { - isa = PBXGroup; - children = ( - F4EC3C302B6A57B0006C9D11 /* BankTask.swift */, - F4EC3C342B6A6BD1006C9D11 /* Order.swift */, - F4EC3C462B6A6D6F006C9D11 /* Client.swift */, - ); - path = Value; - sourceTree = ""; - }; - C7694E6D259C3EC00053667F = { + F4EC3C272B678463006C9D11 /* View */ = { isa = PBXGroup; children = ( C7694E78259C3EC00053667F /* BankManagerConsoleApp */, @@ -158,20 +109,7 @@ name = Products; sourceTree = ""; }; - C7694E78259C3EC00053667F /* BankManagerConsoleApp */ = { - isa = PBXGroup; - children = ( - C7694E79259C3EC00053667F /* main.swift */, - 2776CBDB2B6B8C8C00EF7155 /* App */, - 2776CBDD2B6B8D4300EF7155 /* Model */, - 2776CBDC2B6B8CB400EF7155 /* View */, - F4EC3C4E2B6A72C6006C9D11 /* Protocol */, - 2776CBDA2B6B8C1F00EF7155 /* Utils */, - ); - path = BankManagerConsoleApp; - sourceTree = ""; - }; - F4EC3C4E2B6A72C6006C9D11 /* Protocol */ = { + F4EDF9F52B5F749400C3E54C /* BankManagerConsoleAppTest */ = { isa = PBXGroup; children = ( F4EC3C3A2B6A6C18006C9D11 /* BankerEnqueuable.swift */, @@ -297,30 +235,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F4EC3C3F2B6A6CC3006C9D11 /* BankRunnable.swift in Sources */, - 2776CBD92B6B8C1400EF7155 /* Node.swift in Sources */, - F4EC3C502B6B6701006C9D11 /* BankManagerApp.swift in Sources */, - F4EC3C432B6A6D06006C9D11 /* Banker.swift in Sources */, + F46FA2AC2B6630CF00787C3F /* BankManagerAppMenu.swift in Sources */, + F46FA2AE2B6630E300787C3F /* IOError.swift in Sources */, + F46FA2AA2B6630B200787C3F /* TextInputReadable.swift in Sources */, + F46FA2A42B63A46A00787C3F /* BankManagerApp.swift in Sources */, C7694E7A259C3EC00053667F /* main.swift in Sources */, - F4EC3C542B6B7FAE006C9D11 /* TicketProvidable.swift in Sources */, - 2776CBD72B6B8BE900EF7155 /* LinkedList.swift in Sources */, - F4EC3C312B6A57B0006C9D11 /* BankTask.swift in Sources */, - F4EC3C4B2B6A71E0006C9D11 /* TextOutputDisplayable.swift in Sources */, - F4EC3C492B6A70E7006C9D11 /* ClientEnqueuable.swift in Sources */, - F4EC3C452B6A6D15006C9D11 /* ClientTaskHandlable.swift in Sources */, - F4EC3C3B2B6A6C18006C9D11 /* BankerEnqueuable.swift in Sources */, - 2776CBD32B6B852F00EF7155 /* BankManagerAppMenu.swift in Sources */, - F4EC3C472B6A6D6F006C9D11 /* Client.swift in Sources */, - F4EC3C352B6A6BD1006C9D11 /* Order.swift in Sources */, - 2776CBD52B6B854C00EF7155 /* IOError.swift in Sources */, - 2776CBD12B6B844F00EF7155 /* TextInputReadable.swift in Sources */, - F4EC3C412B6A6CE6006C9D11 /* TaskManagable.swift in Sources */, - F4EC3C522B6B7F9A006C9D11 /* TicketDispenser.swift in Sources */, - C7D65D1B259C8190005510E0 /* BankManager.swift in Sources */, - F4EC3C392B6A6C05006C9D11 /* TaskManager.swift in Sources */, - F4EC3C4D2B6A71F8006C9D11 /* ConsoleManager.swift in Sources */, - F475FC6C2B6CC0EF00B0E859 /* BankManagerAppError.swift in Sources */, - F4EC3C332B6A5B82006C9D11 /* Queue.swift in Sources */, + F4EC3C292B6784BB006C9D11 /* TextOutputDisplayable.swift in Sources */, + F46FA2A82B66309000787C3F /* ConsoleManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -328,6 +249,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F4EDF9F72B5F749400C3E54C /* QueueTest.swift in Sources */, + F4EDFA022B5FADF200C3E54C /* LinkedListTest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/App/BankManagerApp.swift b/BankManagerConsoleApp/BankManagerConsoleApp/App/BankManagerApp.swift index a2868b81..91ba1add 100644 --- a/BankManagerConsoleApp/BankManagerConsoleApp/App/BankManagerApp.swift +++ b/BankManagerConsoleApp/BankManagerConsoleApp/App/BankManagerApp.swift @@ -6,68 +6,42 @@ // final class BankManagerApp { - private let input: TextInputReadable - - private let output: TextOutputDisplayable - - private var isRunning: Bool = true - - init(inputHandler: TextInputReadable, outputHandler: TextOutputDisplayable) { - self.input = inputHandler - self.output = outputHandler - } - func start() { - while self.isRunning { - startLoop() - } + startBank() } } private extension BankManagerApp { - func startLoop() { - self.output.display(output: BankManagerAppMenu.allMenusPrompt) - do { - let input = try self.input.readInput(prompt: "입력:") - let menu = try BankManagerAppMenu(input: input) - handle(menu: menu) - } catch { - handle(error: error) - } - } - - func startBank() throws { - guard let clientCount = (10...30).randomElement() else { throw BankManagerAppError.outOfIndex } - let dispenser = try TicketDispenser(totalClientCount: clientCount) - - let orders = [ - Order(taskType: .loan, bankerCount: 1), - Order(taskType: .deposit, bankerCount: 2), + func startBank() { + let tasks: [BankTask: ClientQueueManagable] = [ + .loan: ClientManager(), + .deposit: ClientManager(), ] - - let bankManager = BankManager( - textOut: self.output, - dispenser: dispenser + + let orders: [BankTask: Int] = [.loan: 2, .deposit: 3] + let bankers = makeBankers( + tasks: tasks, + orders: orders ) - bankManager.runBank(with: orders, numberOfClient: clientCount) + BankManager( + bankers: bankers, + clientManager: tasks + ).start() } - - func handle(menu: BankManagerAppMenu) { - switch menu { - case .open: - do { - try startBank() - } catch { - handle(error: error) -// return + + func makeBankers( + tasks: [BankTask: any ClientQueueManagable], + orders: [BankTask: Int] + ) -> [Banker] { + var result = [Banker]() + for (type, bankerCount) in orders { + (1...bankerCount).forEach { _ in + guard let clientManager = tasks[type] else { return } + let banker = Banker(clientManager: clientManager) + result.append(banker) } - case .end: - self.isRunning = false } - } - - func handle(error: Error) { - self.output.display(output: error.localizedDescription) + return result } } diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/BankTaskType.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/BankTaskType.swift deleted file mode 100644 index 71638d6c..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/BankTaskType.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// BankTaskType.swift -// BankManagerConsoleApp -// -// Created by 강창현 on 1/30/24. -// - -enum BankTaskType: CustomStringConvertible { - case deposit - case loan - - var description: String { - switch self { - case .deposit: - return "예금업무" - case .loan: - return "대출업무" - } - } -} - -extension BankTaskType: CaseIterable { - static var random: Self? { - return allCases.randomElement() - } -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Client.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/Client.swift deleted file mode 100644 index 660fda87..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Client.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// Client.swift -// BankManagerConsoleApp -// -// Created by Effie on 1/26/24. -// - -struct Client { - let number: Int - let taskType: BankTaskType -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/ClientManager.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/ClientManager.swift deleted file mode 100644 index 9c43a726..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/ClientManager.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// ClientManager.swift -// BankManagerConsoleApp -// -// Created by Effie on 1/26/24. -// - -import Foundation - -final class ClientManager { - private let clientQueue: Queue - - private let semaphore: DispatchSemaphore = .init(value: 1) - - init() { - self.clientQueue = Queue() - } -} - -extension ClientManager: ClientEnqueuable { - func enqueueClient(_ client: Client) { - self.clientQueue.enqueue(client) - } -} - -extension ClientManager: ClientDequeuable { - func dequeueClient() -> Client? { - self.semaphore.wait() - let result = self.clientQueue.dequeue() - self.semaphore.signal() - return result - } -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/TaskManager.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/TaskManager.swift deleted file mode 100644 index fe14e144..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/TaskManager.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// TaskManager.swift -// BankManagerConsoleApp -// -// Created by Effie on 1/31/24. -// - -import Foundation - -final class TaskManager { - private let clientQueue: Queue - - private let bankerQueue: Queue - - private let semaphore: DispatchSemaphore = .init(value: 1) - - init( - clientQueue: Queue = .init(), - bankerQueue: Queue = .init() - ) { - self.clientQueue = clientQueue - self.bankerQueue = bankerQueue - } -} - -extension TaskManager: TaskManagable { - func startTaskManaging(group: DispatchGroup) { - let bankerCount = self.bankerQueue.count - - DispatchQueue.global().async(group: group) { - while self.clientQueue.isEmpty == false { - guard - let banker = self.dequeueBanker(), - let client = self.clientQueue.dequeue() - else { - continue - } - - banker.handle(client: client, group: group) - - guard - self.bankerQueue.count != bankerCount - else { - break - } - } - } - } -} - -extension TaskManager: BankerEnqueuable { - func enqueueBanker(_ taskHandlable: ClientTaskHandlable) { - self.semaphore.wait() - self.bankerQueue.enqueue(taskHandlable) - self.semaphore.signal() - } -} - -extension TaskManager: ClientEnqueuable { - func enqueueClient(_ client: Client) { - self.clientQueue.enqueue(client) - } -} - -extension TaskManager { - private func dequeueBanker() -> ClientTaskHandlable? { - self.semaphore.wait() - let result = self.bankerQueue.dequeue() - self.semaphore.signal() - return result - } -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/TicketDispenser.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/TicketDispenser.swift deleted file mode 100644 index ac8b8eec..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/TicketDispenser.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// TicketDispenser.swift -// BankManagerConsoleApp -// -// Created by Effie on 2/1/24. -// - -final class TicketDispenser { - private var ticketNumbers: [BankTask: [Int]] - - init(totalClientCount: Int) throws { - self.ticketNumbers = try Self.componentIndices( - totalIndexCount: totalClientCount, - taskTypes: BankTask.allCases - ) - } - - private static func componentIndices( - totalIndexCount: Int, - taskTypes: [BankTask] - ) throws -> [BankTask: [Int]] { - var result: [BankTask: [Int]] = taskTypes.reduce(into: [:]) { partialResult, task in - partialResult.updateValue([], forKey: task) - } - - for number in 1...totalIndexCount { - guard let type = taskTypes.randomElement() else { throw BankManagerAppError.outOfIndex } - result[type]?.append(number) - } - - for type in taskTypes { - result[type]?.reverse() - } - return result - } -} - -extension TicketDispenser: TicketProvidable { - func provideTicket(of taskType: BankTask) -> Int? { - return self.ticketNumbers[taskType]?.popLast() - } -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/BankTask.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/BankTask.swift deleted file mode 100644 index ba47befd..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/BankTask.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// BankTask.swift -// BankManagerConsoleApp -// -// Created by Effie on 1/31/24. -// - -enum BankTask { - case deposit - case loan - - var name: String { - switch self { - case .deposit: - return "예금업무" - case .loan: - return "대출업무" - } - } - - var duration: Double { - switch self { - case .deposit: - return 0.7 - case .loan: - return 1.1 - } - } -} - -extension BankTask: CaseIterable { - static var allCasesSet: Set { - return Set(allCases) - } -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/Client.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/Client.swift deleted file mode 100644 index 33cb2523..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/Client.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// Client.swift -// BankManagerConsoleApp -// -// Created by Effie on 1/31/24. -// - -struct Client { - let number: Int - let task: BankTask -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/Order.swift b/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/Order.swift deleted file mode 100644 index 290ffe82..00000000 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/Value/Order.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// Order.swift -// BankManagerConsoleApp -// -// Created by Effie on 1/31/24. -// - -struct Order { - let taskType: BankTask - let bankerCount: Int -} diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/main.swift b/BankManagerConsoleApp/BankManagerConsoleApp/main.swift index 3e30a812..01185fc5 100644 --- a/BankManagerConsoleApp/BankManagerConsoleApp/main.swift +++ b/BankManagerConsoleApp/BankManagerConsoleApp/main.swift @@ -4,5 +4,5 @@ // Copyright © yagom academy. All rights reserved. // -let console = ConsoleManager() -BankManagerApp(inputHandler: console, outputHandler: console).start() + +BankManagerApp().start() diff --git a/BankManagerModule/Model/BankManager.swift b/BankManagerModule/Model/BankManager.swift new file mode 100644 index 00000000..2a3b9c2b --- /dev/null +++ b/BankManagerModule/Model/BankManager.swift @@ -0,0 +1,162 @@ +// +// BankManager.swift +// Created by yagom. +// Copyright © yagom academy. All rights reserved. +// + +import Foundation + +final class BankManager { + weak var delegate: BankManagerDelegate? + + private let bankers: [Banker] + + private let clientManagers: [BankTask: ClientEnqueuable & ClientClearable] + + private var currentClientNumber: Int + + private var group: DispatchGroup? + + private var isWorking: Bool = false + + private lazy var timer = BankTimer(delegate: self) + + init( + bankers: [Banker], + clientManagers: [BankTask: ClientEnqueuable & ClientClearable] + ) { + self.bankers = bankers + self.clientManagers = clientManagers + self.currentClientNumber = 0 + } + + func start(with count: Int) { + addNewClients(count: count) + trigger() + } + + func resetBank() { + guard self.isWorking else { return } + self.timer.reset() + for banker in bankers { + banker.stop() + } + self.isWorking = false + for (_, clientManager) in self.clientManagers { + clientManager.clearClients() + } + resetClientCount() + } + + func addClients(count: Int) { + addNewClients(count: count) + + if self.isWorking { + guard let group else { return } + for banker in bankers { + banker.start(group: group) + } + } else { + trigger() + } + } +} + +private extension BankManager { + func addNewClients(count: Int) { + let types = self.clientManagers.map { $0.key } + for number in (currentClientNumber + 1)...(currentClientNumber + count) { + guard let bankTaskType = types.randomElement() else { return } + let client = Client(number: number, task: bankTaskType) + self.clientManagers[bankTaskType]?.enqueueClient(client: client) + self.currentClientNumber = number + } + } + + func trigger() { + guard self.isWorking == false else { return } + self.isWorking = true + + self.group = DispatchGroup() + guard let group else { return } + + self.timer.start() + DispatchQueue.global().async { + for banker in self.bankers { + banker.start(group: group) + } + group.wait() + self.timer.end() + self.resetClientCount() + self.isWorking = false + } + } +} + +private extension BankManager { + func measure(_ progress: () -> Void) -> TimeInterval { + let start = Date() + progress() + return Date().timeIntervalSince(start) + } + + func resetClientCount() { + self.currentClientNumber = 0 + } +} + +extension BankManager: ClientManagerDelegate { + func handleEnqueueClient(client: Client) { + self.delegate?.handleEnqueueClient(client: client) + } + + func handleDequeueClient(client: Client) { + self.delegate?.handleDequeueClient(client: client) + } + + func handleClearClient() { + self.delegate?.handleClearClient() + } +} + +extension BankManager: BankerDelegate { + func handleStartTask(client: Client) { + self.delegate?.handleStartTask(client: client) + } + + func handleEndTask(client: Client) { + self.delegate?.handleEndTask(client: client) + } +} + +extension BankManager: BankTimerDelegate { + func handleUpdating(timeString: String) { + self.delegate?.handleTimer(timeString: timeString) + } +} + +protocol BankManagerEnqueueClientDelegate: AnyObject { + func handleEnqueueClient(client: Client) +} + +protocol BankManagerDequeueClientDelegate: AnyObject { + func handleDequeueClient(client: Client) +} + +protocol BankManagerClearClientDelegate: AnyObject { + func handleClearClient() +} + +protocol BankManagerStartTaskDelegate: AnyObject { + func handleStartTask(client: Client) +} + +protocol BankManagerEndTaskDelegate: AnyObject { + func handleEndTask(client: Client) +} + +protocol BankManagerTimerDelegate: AnyObject { + func handleTimer(timeString: String) +} + +typealias BankManagerDelegate = BankManagerEnqueueClientDelegate & BankManagerDequeueClientDelegate & BankManagerClearClientDelegate & BankManagerStartTaskDelegate & BankManagerEndTaskDelegate & BankManagerTimerDelegate diff --git a/BankManagerModule/Model/BankTask.swift b/BankManagerModule/Model/BankTask.swift new file mode 100644 index 00000000..25868308 --- /dev/null +++ b/BankManagerModule/Model/BankTask.swift @@ -0,0 +1,31 @@ +// +// BankTask.swift +// BankManagerConsoleApp +// +// Created by 강창현 on 1/30/24. +// + +enum BankTask { + case deposit + case loan + + var duration: Double { + switch self { + case .deposit: return 0.7 + case .loan: return 1.1 + } + } + + var name: String { + switch self { + case .deposit: return "예금" + case .loan: return "대출" + } + } +} + +extension BankTask: CaseIterable { + static var random: Self? { + return allCases.randomElement() + } +} diff --git a/BankManagerModule/Model/BankTimer.swift b/BankManagerModule/Model/BankTimer.swift new file mode 100644 index 00000000..ad4c0f40 --- /dev/null +++ b/BankManagerModule/Model/BankTimer.swift @@ -0,0 +1,61 @@ +// +// BankTimer.swift +// BankManagerUIApp +// +// Created by Effie on 2/8/24. +// + +import Foundation + +final class BankTimer { + private static let interval: TimeInterval = 0.001 + + private let delegate: BankTimerDelegate + + private var milliseconds: Int = 0 + + private var timer: Timer? + + init(delegate: BankTimerDelegate) { + self.delegate = delegate + self.milliseconds = 0 + } + + func start() { + self.timer = makeTimer() + self.timer?.fire() + } + + func end() { + self.timer?.invalidate() + } + + func reset() { + end() + self.milliseconds = 0 + let string = format() + self.delegate.handleUpdating(timeString: string) + } + + private func makeTimer() -> Timer { + let timer = Timer.scheduledTimer(withTimeInterval: Self.interval, repeats: true) { timer in + self.milliseconds += 1 + let formattedString = self.format() + self.delegate.handleUpdating(timeString: formattedString) + } + return timer + } + + private func format() -> String { + return String( + format: "%02d:%02d:%03d", + self.milliseconds / 60_000, + (self.milliseconds % 60_000) / 1_000, + self.milliseconds % 1_000 + ) + } +} + +protocol BankTimerDelegate { + func handleUpdating(timeString: String) +} diff --git a/BankManagerModule/Model/Banker.swift b/BankManagerModule/Model/Banker.swift new file mode 100644 index 00000000..5a40d1a8 --- /dev/null +++ b/BankManagerModule/Model/Banker.swift @@ -0,0 +1,63 @@ +// +// Banker.swift +// BankManagerConsoleApp +// +// Created by Effie on 1/26/24. +// + +import Foundation + +final class Banker { + private let clientManager: ClientDequeuable + + weak var delegate: BankerDelegate? + + private var isWorking: Bool = false + + init( + clientManager: ClientDequeuable + ) { + self.clientManager = clientManager + } + + func start(group: DispatchGroup) { + guard self.isWorking == false else { return } + DispatchQueue.global().async(group: group) { + self.isWorking = true + while self.isWorking, let client = self.clientManager.dequeueClient() { + self.work(for: client) + } + self.isWorking = false + } + } + + func stop() { + self.isWorking = false + } +} + +private extension Banker { + func work(for client: Client) { + self.startTask(for: client) + Thread.sleep(forTimeInterval: client.task.duration) + self.endTask(for: client) + } + + func startTask(for client: Client) { + self.delegate?.handleStartTask(client: client) + } + + func endTask(for client: Client) { + self.delegate?.handleEndTask(client: client) + } +} + +protocol BankerStartTaskDelegate: AnyObject { + func handleStartTask(client: Client) +} + +protocol BankerEndTaskDelegate: AnyObject { + func handleEndTask(client: Client) +} + +typealias BankerDelegate = BankerStartTaskDelegate & BankerEndTaskDelegate diff --git a/BankManagerModule/Model/Client.swift b/BankManagerModule/Model/Client.swift new file mode 100644 index 00000000..90c0ef32 --- /dev/null +++ b/BankManagerModule/Model/Client.swift @@ -0,0 +1,17 @@ +// +// Client.swift +// BankManagerConsoleApp +// +// Created by Effie on 1/26/24. +// + +struct Client { + let number: Int + let task: BankTask +} + +extension Client: Hashable { + func hash(into hasher: inout Hasher) { + hasher.combine(self.number) + } +} diff --git a/BankManagerModule/Model/ClientManager.swift b/BankManagerModule/Model/ClientManager.swift new file mode 100644 index 00000000..021c0c11 --- /dev/null +++ b/BankManagerModule/Model/ClientManager.swift @@ -0,0 +1,65 @@ +// +// ClientManager.swift +// BankManagerConsoleApp +// +// Created by Effie on 1/26/24. +// + +import Foundation + +final class ClientManager { + weak var delegate: (ClientManagerDelegate)? + + private let clientQueue: Queue + + private let semaphore: DispatchSemaphore = .init(value: 1) + + init() { + self.clientQueue = Queue() + } +} + +extension ClientManager: ClientEnqueuable { + func enqueueClient(client: Client) { + self.semaphore.wait() + self.clientQueue.enqueue(client) + self.delegate?.handleEnqueueClient(client: client) + self.semaphore.signal() + } +} + +extension ClientManager: ClientDequeuable { + func dequeueClient() -> Client? { + self.semaphore.wait() + guard let client = self.clientQueue.dequeue() else { + self.semaphore.signal() + return nil + } + self.delegate?.handleDequeueClient(client: client) + self.semaphore.signal() + return client + } +} + +extension ClientManager: ClientClearable { + func clearClients() { + self.semaphore.wait() + self.clientQueue.clear() + self.delegate?.handleClearClient() + self.semaphore.signal() + } +} + +protocol ClientManagerEnqueueClientDelegate: AnyObject { + func handleEnqueueClient(client: Client) +} + +protocol ClientManagerDequeueClientDelegate: AnyObject { + func handleDequeueClient(client: Client) +} + +protocol ClientManagerClearClientDelegate: AnyObject { + func handleClearClient() +} + +typealias ClientManagerDelegate = ClientManagerEnqueueClientDelegate & ClientManagerDequeueClientDelegate & ClientManagerClearClientDelegate diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Model/ClientQueueManagable.swift b/BankManagerModule/Model/ClientQueueManagable.swift similarity index 69% rename from BankManagerConsoleApp/BankManagerConsoleApp/Model/ClientQueueManagable.swift rename to BankManagerModule/Model/ClientQueueManagable.swift index a85a3afa..6f382c78 100644 --- a/BankManagerConsoleApp/BankManagerConsoleApp/Model/ClientQueueManagable.swift +++ b/BankManagerModule/Model/ClientQueueManagable.swift @@ -6,11 +6,15 @@ // protocol ClientEnqueuable { - func enqueueClient(_ client: Client) + func enqueueClient(client: Client) } protocol ClientDequeuable { func dequeueClient() -> Client? } -typealias ClientQueueManagable = ClientEnqueuable & ClientDequeuable +protocol ClientClearable { + func clearClients() +} + +typealias ClientQueueManagable = ClientEnqueuable & ClientDequeuable & ClientClearable diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Util/DataStructure/LinkedList.swift b/BankManagerModule/Util/DataStructure/LinkedList.swift similarity index 100% rename from BankManagerConsoleApp/BankManagerConsoleApp/Util/DataStructure/LinkedList.swift rename to BankManagerModule/Util/DataStructure/LinkedList.swift diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Util/DataStructure/Node.swift b/BankManagerModule/Util/DataStructure/Node.swift similarity index 100% rename from BankManagerConsoleApp/BankManagerConsoleApp/Util/DataStructure/Node.swift rename to BankManagerModule/Util/DataStructure/Node.swift diff --git a/BankManagerConsoleApp/BankManagerConsoleApp/Util/DataStructure/Queue.swift b/BankManagerModule/Util/DataStructure/Queue.swift similarity index 100% rename from BankManagerConsoleApp/BankManagerConsoleApp/Util/DataStructure/Queue.swift rename to BankManagerModule/Util/DataStructure/Queue.swift diff --git a/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj b/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj index 62a20a2d..1640ddbb 100644 --- a/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj +++ b/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj @@ -9,13 +9,33 @@ /* Begin PBXBuildFile section */ C7694E3B259C3E9F0053667F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E3A259C3E9F0053667F /* AppDelegate.swift */; }; C7694E3D259C3E9F0053667F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E3C259C3E9F0053667F /* SceneDelegate.swift */; }; - C7694E3F259C3E9F0053667F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E3E259C3E9F0053667F /* ViewController.swift */; }; - C7694E42259C3E9F0053667F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7694E40259C3E9F0053667F /* Main.storyboard */; }; C7694E44259C3EA20053667F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7694E43259C3EA20053667F /* Assets.xcassets */; }; C7694E47259C3EA20053667F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7694E45259C3EA20053667F /* LaunchScreen.storyboard */; }; C7694E52259C3EA20053667F /* BankManagerUIAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E51259C3EA20053667F /* BankManagerUIAppTests.swift */; }; C7694E5D259C3EA20053667F /* BankManagerUIAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E5C259C3EA20053667F /* BankManagerUIAppUITests.swift */; }; - C7D65D1E259C81BD005510E0 /* BankManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D65D1D259C81BD005510E0 /* BankManager.swift */; }; + F435ABA82B7407CB009B67C5 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435AB9B2B7407CB009B67C5 /* Queue.swift */; }; + F435ABA92B7407CB009B67C5 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435AB9C2B7407CB009B67C5 /* Node.swift */; }; + F435ABAA2B7407CB009B67C5 /* LinkedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435AB9D2B7407CB009B67C5 /* LinkedList.swift */; }; + F435ABAB2B7407CB009B67C5 /* BankTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435AB9F2B7407CB009B67C5 /* BankTask.swift */; }; + F435ABAC2B7407CB009B67C5 /* Banker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABA02B7407CB009B67C5 /* Banker.swift */; }; + F435ABAE2B7407CB009B67C5 /* ClientQueueManagable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABA22B7407CB009B67C5 /* ClientQueueManagable.swift */; }; + F435ABB02B7407CB009B67C5 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABA42B7407CB009B67C5 /* Client.swift */; }; + F435ABB12B7407CB009B67C5 /* BankManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABA52B7407CB009B67C5 /* BankManager.swift */; }; + F435ABB22B7407CB009B67C5 /* ClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABA62B7407CB009B67C5 /* ClientManager.swift */; }; + F435ABB52B740A46009B67C5 /* BankViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABB42B740A46009B67C5 /* BankViewModel.swift */; }; + F435ABC42B740D12009B67C5 /* ClientListTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABB82B740D12009B67C5 /* ClientListTableView.swift */; }; + F435ABC52B740D12009B67C5 /* ClientListDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABB92B740D12009B67C5 /* ClientListDataSource.swift */; }; + F435ABC62B740D12009B67C5 /* ClientListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABBA2B740D12009B67C5 /* ClientListTableViewCell.swift */; }; + F435ABC72B740D12009B67C5 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABBB2B740D12009B67C5 /* ReusableView.swift */; }; + F435ABC82B740D12009B67C5 /* ClientListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABBC2B740D12009B67C5 /* ClientListHeaderView.swift */; }; + F435ABC92B740D12009B67C5 /* ListLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABBD2B740D12009B67C5 /* ListLabel.swift */; }; + F435ABCB2B740D12009B67C5 /* TimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABBF2B740D12009B67C5 /* TimerView.swift */; }; + F435ABCC2B740D12009B67C5 /* BankList.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABC02B740D12009B67C5 /* BankList.swift */; }; + F435ABCE2B740D12009B67C5 /* BankViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABC32B740D12009B67C5 /* BankViewController.swift */; }; + F435ABD12B740D41009B67C5 /* BMColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABD02B740D41009B67C5 /* BMColor.swift */; }; + F435ABD32B7443CB009B67C5 /* BankTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F435ABD22B7443CB009B67C5 /* BankTimer.swift */; }; + F45041062B74DCD300DC4DE4 /* BankIntput.swift in Sources */ = {isa = PBXBuildFile; fileRef = F45041052B74DCD300DC4DE4 /* BankIntput.swift */; }; + F45041082B74DCE500DC4DE4 /* BankOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = F45041072B74DCE500DC4DE4 /* BankOutput.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -39,8 +59,6 @@ C7694E37259C3E9F0053667F /* BankManagerUIApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BankManagerUIApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7694E3A259C3E9F0053667F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C7694E3C259C3E9F0053667F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - C7694E3E259C3E9F0053667F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - C7694E41259C3E9F0053667F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C7694E43259C3EA20053667F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C7694E46259C3EA20053667F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C7694E48259C3EA20053667F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -50,7 +68,29 @@ C7694E58259C3EA20053667F /* BankManagerUIAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BankManagerUIAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C7694E5C259C3EA20053667F /* BankManagerUIAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankManagerUIAppUITests.swift; sourceTree = ""; }; C7694E5E259C3EA20053667F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C7D65D1D259C81BD005510E0 /* BankManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BankManager.swift; path = ../../BankManager.swift; sourceTree = ""; }; + F435AB9B2B7407CB009B67C5 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; + F435AB9C2B7407CB009B67C5 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; + F435AB9D2B7407CB009B67C5 /* LinkedList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkedList.swift; sourceTree = ""; }; + F435AB9F2B7407CB009B67C5 /* BankTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankTask.swift; sourceTree = ""; }; + F435ABA02B7407CB009B67C5 /* Banker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Banker.swift; sourceTree = ""; }; + F435ABA22B7407CB009B67C5 /* ClientQueueManagable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientQueueManagable.swift; sourceTree = ""; }; + F435ABA42B7407CB009B67C5 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; + F435ABA52B7407CB009B67C5 /* BankManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankManager.swift; sourceTree = ""; }; + F435ABA62B7407CB009B67C5 /* ClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientManager.swift; sourceTree = ""; }; + F435ABB42B740A46009B67C5 /* BankViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankViewModel.swift; sourceTree = ""; }; + F435ABB82B740D12009B67C5 /* ClientListTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientListTableView.swift; sourceTree = ""; }; + F435ABB92B740D12009B67C5 /* ClientListDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientListDataSource.swift; sourceTree = ""; }; + F435ABBA2B740D12009B67C5 /* ClientListTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientListTableViewCell.swift; sourceTree = ""; }; + F435ABBB2B740D12009B67C5 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = ""; }; + F435ABBC2B740D12009B67C5 /* ClientListHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientListHeaderView.swift; sourceTree = ""; }; + F435ABBD2B740D12009B67C5 /* ListLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLabel.swift; sourceTree = ""; }; + F435ABBF2B740D12009B67C5 /* TimerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimerView.swift; sourceTree = ""; }; + F435ABC02B740D12009B67C5 /* BankList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankList.swift; sourceTree = ""; }; + F435ABC32B740D12009B67C5 /* BankViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BankViewController.swift; sourceTree = ""; }; + F435ABD02B740D41009B67C5 /* BMColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMColor.swift; sourceTree = ""; }; + F435ABD22B7443CB009B67C5 /* BankTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankTimer.swift; sourceTree = ""; }; + F45041052B74DCD300DC4DE4 /* BankIntput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankIntput.swift; sourceTree = ""; }; + F45041072B74DCE500DC4DE4 /* BankOutput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankOutput.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -101,14 +141,13 @@ C7694E39259C3E9F0053667F /* BankManagerUIApp */ = { isa = PBXGroup; children = ( - C7D65D1D259C81BD005510E0 /* BankManager.swift */, - C7694E3A259C3E9F0053667F /* AppDelegate.swift */, - C7694E3C259C3E9F0053667F /* SceneDelegate.swift */, - C7694E3E259C3E9F0053667F /* ViewController.swift */, - C7694E40259C3E9F0053667F /* Main.storyboard */, - C7694E43259C3EA20053667F /* Assets.xcassets */, - C7694E45259C3EA20053667F /* LaunchScreen.storyboard */, - C7694E48259C3EA20053667F /* Info.plist */, + F435ABD42B74DB63009B67C5 /* App */, + F435ABB62B740D12009B67C5 /* View */, + F435ABC22B740D12009B67C5 /* ViewController */, + F435ABD52B74DBEB009B67C5 /* ViewModel */, + F435ABCF2B740D41009B67C5 /* Resource */, + F45041092B74DEB400DC4DE4 /* Supportings */, + F435AB982B7407CB009B67C5 /* BankManagerModule */, ); path = BankManagerUIApp; sourceTree = ""; @@ -131,6 +170,116 @@ path = BankManagerUIAppUITests; sourceTree = ""; }; + F435AB982B7407CB009B67C5 /* BankManagerModule */ = { + isa = PBXGroup; + children = ( + F435AB992B7407CB009B67C5 /* Util */, + F435AB9E2B7407CB009B67C5 /* Model */, + ); + name = BankManagerModule; + path = ../../BankManagerModule; + sourceTree = ""; + }; + F435AB992B7407CB009B67C5 /* Util */ = { + isa = PBXGroup; + children = ( + F435AB9A2B7407CB009B67C5 /* DataStructure */, + ); + path = Util; + sourceTree = ""; + }; + F435AB9A2B7407CB009B67C5 /* DataStructure */ = { + isa = PBXGroup; + children = ( + F435AB9B2B7407CB009B67C5 /* Queue.swift */, + F435AB9C2B7407CB009B67C5 /* Node.swift */, + F435AB9D2B7407CB009B67C5 /* LinkedList.swift */, + ); + path = DataStructure; + sourceTree = ""; + }; + F435AB9E2B7407CB009B67C5 /* Model */ = { + isa = PBXGroup; + children = ( + F435AB9F2B7407CB009B67C5 /* BankTask.swift */, + F435ABA02B7407CB009B67C5 /* Banker.swift */, + F435ABA22B7407CB009B67C5 /* ClientQueueManagable.swift */, + F435ABA42B7407CB009B67C5 /* Client.swift */, + F435ABA52B7407CB009B67C5 /* BankManager.swift */, + F435ABA62B7407CB009B67C5 /* ClientManager.swift */, + F435ABD22B7443CB009B67C5 /* BankTimer.swift */, + ); + path = Model; + sourceTree = ""; + }; + F435ABB62B740D12009B67C5 /* View */ = { + isa = PBXGroup; + children = ( + F435ABB72B740D12009B67C5 /* TableView */, + F435ABBF2B740D12009B67C5 /* TimerView.swift */, + F435ABC02B740D12009B67C5 /* BankList.swift */, + ); + path = View; + sourceTree = ""; + }; + F435ABB72B740D12009B67C5 /* TableView */ = { + isa = PBXGroup; + children = ( + F435ABBD2B740D12009B67C5 /* ListLabel.swift */, + F435ABB82B740D12009B67C5 /* ClientListTableView.swift */, + F435ABB92B740D12009B67C5 /* ClientListDataSource.swift */, + F435ABBA2B740D12009B67C5 /* ClientListTableViewCell.swift */, + F435ABBB2B740D12009B67C5 /* ReusableView.swift */, + F435ABBC2B740D12009B67C5 /* ClientListHeaderView.swift */, + ); + path = TableView; + sourceTree = ""; + }; + F435ABC22B740D12009B67C5 /* ViewController */ = { + isa = PBXGroup; + children = ( + F435ABC32B740D12009B67C5 /* BankViewController.swift */, + ); + path = ViewController; + sourceTree = ""; + }; + F435ABCF2B740D41009B67C5 /* Resource */ = { + isa = PBXGroup; + children = ( + F435ABD02B740D41009B67C5 /* BMColor.swift */, + ); + path = Resource; + sourceTree = ""; + }; + F435ABD42B74DB63009B67C5 /* App */ = { + isa = PBXGroup; + children = ( + C7694E3A259C3E9F0053667F /* AppDelegate.swift */, + C7694E3C259C3E9F0053667F /* SceneDelegate.swift */, + ); + path = App; + sourceTree = ""; + }; + F435ABD52B74DBEB009B67C5 /* ViewModel */ = { + isa = PBXGroup; + children = ( + F435ABB42B740A46009B67C5 /* BankViewModel.swift */, + F45041052B74DCD300DC4DE4 /* BankIntput.swift */, + F45041072B74DCE500DC4DE4 /* BankOutput.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + F45041092B74DEB400DC4DE4 /* Supportings */ = { + isa = PBXGroup; + children = ( + C7694E43259C3EA20053667F /* Assets.xcassets */, + C7694E45259C3EA20053667F /* LaunchScreen.storyboard */, + C7694E48259C3EA20053667F /* Info.plist */, + ); + path = Supportings; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -236,7 +385,6 @@ files = ( C7694E47259C3EA20053667F /* LaunchScreen.storyboard in Resources */, C7694E44259C3EA20053667F /* Assets.xcassets in Resources */, - C7694E42259C3E9F0053667F /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -261,10 +409,31 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C7694E3F259C3E9F0053667F /* ViewController.swift in Sources */, + F435ABB12B7407CB009B67C5 /* BankManager.swift in Sources */, + F435ABC52B740D12009B67C5 /* ClientListDataSource.swift in Sources */, + F435ABAE2B7407CB009B67C5 /* ClientQueueManagable.swift in Sources */, + F435ABC62B740D12009B67C5 /* ClientListTableViewCell.swift in Sources */, + F435ABD12B740D41009B67C5 /* BMColor.swift in Sources */, + F435ABAB2B7407CB009B67C5 /* BankTask.swift in Sources */, + F435ABCE2B740D12009B67C5 /* BankViewController.swift in Sources */, + F435ABA82B7407CB009B67C5 /* Queue.swift in Sources */, + F435ABB52B740A46009B67C5 /* BankViewModel.swift in Sources */, + F435ABB02B7407CB009B67C5 /* Client.swift in Sources */, + F435ABCC2B740D12009B67C5 /* BankList.swift in Sources */, + F45041062B74DCD300DC4DE4 /* BankIntput.swift in Sources */, + F435ABD32B7443CB009B67C5 /* BankTimer.swift in Sources */, + F435ABC42B740D12009B67C5 /* ClientListTableView.swift in Sources */, + F435ABCB2B740D12009B67C5 /* TimerView.swift in Sources */, C7694E3B259C3E9F0053667F /* AppDelegate.swift in Sources */, + F45041082B74DCE500DC4DE4 /* BankOutput.swift in Sources */, + F435ABC92B740D12009B67C5 /* ListLabel.swift in Sources */, + F435ABA92B7407CB009B67C5 /* Node.swift in Sources */, + F435ABC72B740D12009B67C5 /* ReusableView.swift in Sources */, + F435ABAC2B7407CB009B67C5 /* Banker.swift in Sources */, + F435ABC82B740D12009B67C5 /* ClientListHeaderView.swift in Sources */, C7694E3D259C3E9F0053667F /* SceneDelegate.swift in Sources */, - C7D65D1E259C81BD005510E0 /* BankManager.swift in Sources */, + F435ABAA2B7407CB009B67C5 /* LinkedList.swift in Sources */, + F435ABB22B7407CB009B67C5 /* ClientManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -300,14 +469,6 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - C7694E40259C3E9F0053667F /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - C7694E41259C3E9F0053667F /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; C7694E45259C3EA20053667F /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -441,7 +602,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = BankManagerUIApp/Info.plist; + INFOPLIST_FILE = BankManagerUIApp/Supportings/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -460,7 +621,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = BankManagerUIApp/Info.plist; + INFOPLIST_FILE = BankManagerUIApp/Supportings/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/BankManagerUIApp/BankManagerUIApp/App/AppDelegate.swift b/BankManagerUIApp/BankManagerUIApp/App/AppDelegate.swift new file mode 100644 index 00000000..8bbe5b8f --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/App/AppDelegate.swift @@ -0,0 +1,29 @@ +// +// BankManagerUIApp - AppDelegate.swift +// Created by yagom. +// Copyright © yagom academy. All rights reserved. +// + +import UIKit + +@main +final class AppDelegate: UIResponder, UIApplicationDelegate { + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return true + } + + func application( + _ application: UIApplication, + configurationForConnecting connectingSceneSession: UISceneSession, + options: UIScene.ConnectionOptions + ) -> UISceneConfiguration { + return UISceneConfiguration( + name: "Default Configuration", + sessionRole: connectingSceneSession.role + ) + } +} + diff --git a/BankManagerUIApp/BankManagerUIApp/App/SceneDelegate.swift b/BankManagerUIApp/BankManagerUIApp/App/SceneDelegate.swift new file mode 100644 index 00000000..aac407b6 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/App/SceneDelegate.swift @@ -0,0 +1,70 @@ +// +// BankManagerUIApp - SceneDelegate.swift +// Created by yagom. +// Copyright © yagom academy. All rights reserved. +// + +import UIKit + +final class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + guard let windowScene = (scene as? UIWindowScene) else { return } + self.window = UIWindow(windowScene: windowScene) + self.window?.rootViewController = Self.makeViewController() + self.window?.makeKeyAndVisible() + } + + static func makeViewController() -> UIViewController { + let clientManagers: [BankTask: ClientManager] = [ + .loan: ClientManager(), + .deposit: ClientManager(), + ] + + let orders: [BankTask: Int] = [.loan: 1, .deposit: 2] + + let bankers = makeBankers( + tasks: clientManagers, + orders: orders + ) + + let bankManager = BankManager( + bankers: bankers, + clientManagers: clientManagers + ) + + bankers.forEach { banker in banker.delegate = bankManager } + + for (_, clientManager) in clientManagers { + clientManager.delegate = bankManager + } + + let mirror = BankViewModel(bankManager: bankManager) + bankManager.delegate = mirror + + let viewController = BankViewController(bankMirror: mirror) + mirror.delegate = viewController + return viewController + } + + static func makeBankers( + tasks: [BankTask: ClientQueueManagable], + orders: [BankTask: Int] + ) -> [Banker] { + var result = [Banker]() + for (type, bankerCount) in orders { + (1...bankerCount).forEach { _ in + guard let clientManager = tasks[type] else { return } + let banker = Banker(clientManager: clientManager) + result.append(banker) + } + } + return result + } +} + diff --git a/BankManagerUIApp/BankManagerUIApp/AppDelegate.swift b/BankManagerUIApp/BankManagerUIApp/AppDelegate.swift deleted file mode 100644 index 71013f64..00000000 --- a/BankManagerUIApp/BankManagerUIApp/AppDelegate.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// BankManagerUIApp - AppDelegate.swift -// Created by yagom. -// Copyright © yagom academy. All rights reserved. -// - -import UIKit - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - - - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - // MARK: UISceneSession Lifecycle - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - - -} - diff --git a/BankManagerUIApp/BankManagerUIApp/Base.lproj/Main.storyboard b/BankManagerUIApp/BankManagerUIApp/Base.lproj/Main.storyboard deleted file mode 100644 index ce80ec92..00000000 --- a/BankManagerUIApp/BankManagerUIApp/Base.lproj/Main.storyboard +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/BankManagerUIApp/BankManagerUIApp/Resource/BMColor.swift b/BankManagerUIApp/BankManagerUIApp/Resource/BMColor.swift new file mode 100644 index 00000000..6cc17cde --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Resource/BMColor.swift @@ -0,0 +1,13 @@ +// +// BMColor.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +enum BMColor { + static let purple = UIColor(named: "BMPurple") + static let green = UIColor(named: "BMGreen") +} diff --git a/BankManagerUIApp/BankManagerUIApp/SceneDelegate.swift b/BankManagerUIApp/BankManagerUIApp/SceneDelegate.swift deleted file mode 100644 index 63d2d2a1..00000000 --- a/BankManagerUIApp/BankManagerUIApp/SceneDelegate.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// BankManagerUIApp - SceneDelegate.swift -// Created by yagom. -// Copyright © yagom academy. All rights reserved. -// - -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - var window: UIWindow? - - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let _ = (scene as? UIWindowScene) else { return } - } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } - - -} - diff --git a/BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AccentColor.colorset/Contents.json b/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AccentColor.colorset/Contents.json rename to BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AppIcon.appiconset/Contents.json rename to BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/BMGreen.colorset/Contents.json b/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/BMGreen.colorset/Contents.json new file mode 100644 index 00000000..ea797610 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/BMGreen.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x54", + "green" : "0xCF", + "red" : "0x5E" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/BMPurple.colorset/Contents.json b/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/BMPurple.colorset/Contents.json new file mode 100644 index 00000000..58716df3 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/BMPurple.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE2", + "green" : "0x5A", + "red" : "0x68" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFE" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Assets.xcassets/Contents.json b/BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/Contents.json similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Assets.xcassets/Contents.json rename to BankManagerUIApp/BankManagerUIApp/Supportings/Assets.xcassets/Contents.json diff --git a/BankManagerUIApp/BankManagerUIApp/Base.lproj/LaunchScreen.storyboard b/BankManagerUIApp/BankManagerUIApp/Supportings/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Base.lproj/LaunchScreen.storyboard rename to BankManagerUIApp/BankManagerUIApp/Supportings/Base.lproj/LaunchScreen.storyboard diff --git a/BankManagerUIApp/BankManagerUIApp/Info.plist b/BankManagerUIApp/BankManagerUIApp/Supportings/Info.plist similarity index 94% rename from BankManagerUIApp/BankManagerUIApp/Info.plist rename to BankManagerUIApp/BankManagerUIApp/Supportings/Info.plist index 5b531f7b..2688b32b 100644 --- a/BankManagerUIApp/BankManagerUIApp/Info.plist +++ b/BankManagerUIApp/BankManagerUIApp/Supportings/Info.plist @@ -33,8 +33,6 @@ Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate - UISceneStoryboardFile - Main @@ -43,8 +41,6 @@ UILaunchStoryboardName LaunchScreen - UIMainStoryboardFile - Main UIRequiredDeviceCapabilities armv7 diff --git a/BankManagerUIApp/BankManagerUIApp/View/BankList.swift b/BankManagerUIApp/BankManagerUIApp/View/BankList.swift new file mode 100644 index 00000000..367e267b --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/BankList.swift @@ -0,0 +1,27 @@ +// +// BankList.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +enum BankList { + case waiting + case working + + var backgroundColor: UIColor? { + switch self { + case .waiting: return BMColor.green + case .working: return BMColor.purple + } + } + + var name: String { + switch self { + case .waiting: return "대기중" + case .working: return "작업중" + } + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListDataSource.swift b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListDataSource.swift new file mode 100644 index 00000000..49b119a0 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListDataSource.swift @@ -0,0 +1,38 @@ +// +// ClientListDataSource.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +enum ClientListSection { + case client +} + +enum ClientListItem: Hashable { + case client(Client) +} + +typealias ClientListSnapShot = NSDiffableDataSourceSnapshot + +final class ClientListDataSource: UITableViewDiffableDataSource { + typealias TableView = ClientListTableView + typealias ClientCell = ClientListTableViewCell + + static let cellProvider: CellProvider = { tableview, indexPath, itemIdentifier in + switch itemIdentifier { + case .client(let client): + guard let cell = tableview.dequeueReusableCell( + withIdentifier: ClientCell.reuseIdentifier, for: indexPath + ) as? ClientCell else { return ClientCell() } + cell.update(with: client) + return cell + } + } + + convenience init(_ listView: TableView) { + self.init(tableView: listView, cellProvider: Self.cellProvider) + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListHeaderView.swift b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListHeaderView.swift new file mode 100644 index 00000000..a04e530e --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListHeaderView.swift @@ -0,0 +1,47 @@ +// +// ClientListHeaderView.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +final class ClientListHeaderView: UITableViewHeaderFooterView { + private let titleLabel: UILabel = { + let label = UILabel(frame: .zero) + label.font = .preferredFont(forTextStyle: .largeTitle) + label.textAlignment = .center + label.textColor = .white + return label + }() + + init(type: BankList) { + super.init(reuseIdentifier: Self.reuseIdentifier) + setType(type: type) + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setType(type: BankList) { + self.titleLabel.text = type.name + self.titleLabel.backgroundColor = type.backgroundColor + } + + private func setLayout() { + self.titleLabel.translatesAutoresizingMaskIntoConstraints = false + self.addSubview(titleLabel) + NSLayoutConstraint.activate([ + self.titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor), + self.titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor), + self.titleLabel.topAnchor.constraint(equalTo: self.topAnchor), + self.titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor), + ]) + } +} + +extension ClientListHeaderView: ReusableView { } diff --git a/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListTableView.swift b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListTableView.swift new file mode 100644 index 00000000..d06ee6da --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListTableView.swift @@ -0,0 +1,36 @@ +// +// ClientListTableView.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +final class ClientListTableView: UITableView { + typealias ClientCell = ClientListTableViewCell + typealias HeaderView = ClientListHeaderView + + let type: BankList + + init(type: BankList) { + self.type = type + super.init(frame: .zero, style: .plain) + self.separatorStyle = .none + setCollection() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func makeHeader() -> HeaderView { + return HeaderView(type: self.type) + } +} + +extension ClientListTableView: ReusableView { + private func setCollection() { + register(ClientCell.self) + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListTableViewCell.swift b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListTableViewCell.swift new file mode 100644 index 00000000..9deeeabd --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/TableView/ClientListTableViewCell.swift @@ -0,0 +1,39 @@ +// +// ClientListTableViewCell.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +final class ClientListTableViewCell: UITableViewCell { + private let label: ListLabel = ListLabel() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(with client: Client) { + self.label.configure(client: client) + } + + private func setLayout() { + self.label.translatesAutoresizingMaskIntoConstraints = false + self.addSubview(label) + NSLayoutConstraint.activate([ + self.label.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 4), + self.label.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -4), + self.label.topAnchor.constraint(equalTo: self.topAnchor, constant: 4), + self.label.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -4), + ]) + } +} + +extension ClientListTableViewCell: ReusableView { } diff --git a/BankManagerUIApp/BankManagerUIApp/View/TableView/ListLabel.swift b/BankManagerUIApp/BankManagerUIApp/View/TableView/ListLabel.swift new file mode 100644 index 00000000..a72deaeb --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/TableView/ListLabel.swift @@ -0,0 +1,38 @@ +// +// ListLabel.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +final class ListLabel: UILabel { + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + init() { + super.init(frame: .zero) + self.textAlignment = .center + self.font = .preferredFont(forTextStyle: .title3) + } + + init(client: Client) { + super.init(frame: .zero) + self.textAlignment = .center + self.font = .preferredFont(forTextStyle: .title3) + configure(client: client) + } + + func configure(client: Client) { + self.text = "\(client.number) - \(client.task.name)" + switch client.task { + case .deposit: + self.textColor = .black + case .loan: + self.textColor = .purple + } + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/View/TableView/ReusableView.swift b/BankManagerUIApp/BankManagerUIApp/View/TableView/ReusableView.swift new file mode 100644 index 00000000..cac9f70a --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/TableView/ReusableView.swift @@ -0,0 +1,25 @@ +// +// ReusableView.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +protocol ReusableView { + static var reuseIdentifier: String { get } +} + +extension ReusableView { + static var reuseIdentifier: String { + return String(describing: self) + } +} + +extension UITableView { + func register(_ cellType: Cell.Type) { + register(cellType, forCellReuseIdentifier: cellType.reuseIdentifier) + } +} + diff --git a/BankManagerUIApp/BankManagerUIApp/View/TimerView.swift b/BankManagerUIApp/BankManagerUIApp/View/TimerView.swift new file mode 100644 index 00000000..8b620076 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/View/TimerView.swift @@ -0,0 +1,43 @@ +// +// TimerView.swift +// BankManagerUIApp +// +// Created by Effie on 2/7/24. +// + +import UIKit + +final class TimerView: UIView { + private let timerLabel: UILabel = { + let label = UILabel(frame: .zero) + label.text = "업무시간 - \("00:00:000")" + label.textAlignment = .center + label.font = .preferredFont(forTextStyle: .title2) + return label + }() + + init() { + super.init(frame: .zero) + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setLayout() { + self.addSubview(timerLabel) + self.timerLabel.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.timerLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10), + self.timerLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10), + self.timerLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10), + self.timerLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10), + ]) + } + + func configure(with timeString: String) { + self.timerLabel.text = "업무시간 - \(timeString)" + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/ViewController.swift b/BankManagerUIApp/BankManagerUIApp/ViewController.swift deleted file mode 100644 index a3182156..00000000 --- a/BankManagerUIApp/BankManagerUIApp/ViewController.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// BankManagerUIApp - ViewController.swift -// Created by yagom. -// Copyright © yagom academy. All rights reserved. -// - -import UIKit - -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } - - -} - diff --git a/BankManagerUIApp/BankManagerUIApp/ViewController/BankViewController.swift b/BankManagerUIApp/BankManagerUIApp/ViewController/BankViewController.swift new file mode 100644 index 00000000..b75dc2ed --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/ViewController/BankViewController.swift @@ -0,0 +1,199 @@ +// +// BankManagerUIApp - BankViewController.swift +// Created by yagom. +// Copyright © yagom academy. All rights reserved. +// + +import UIKit + +final class BankViewController: UIViewController { + // MARK: - Properties + + private let viewModel: BankIntput + + // MARK: - UI Elements + private let addClientButton: UIButton = { + let button = UIButton(frame: .zero) + button.setTitle("10명 추가", for: .normal) + button.setTitleColor(.systemBlue, for: .normal) + return button + }() + + private let clearButton: UIButton = { + let button = UIButton(frame: .zero) + button.setTitle("초기화", for: .normal) + button.setTitleColor(.red, for: .normal) + return button + }() + + private lazy var buttonStackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.spacing = 5 + stackView.distribution = .fillEqually + + stackView.addArrangedSubview(self.addClientButton) + stackView.addArrangedSubview(self.clearButton) + return stackView + }() + + private let timerView: TimerView = TimerView() + + private let waitingListTableView = ClientListTableView(type: .waiting) + + private let workingListTableView = ClientListTableView(type: .working) + + private lazy var listStackView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.spacing = 0 + stackView.distribution = .fillEqually + + stackView.addArrangedSubview(self.waitingListTableView) + stackView.addArrangedSubview(self.workingListTableView) + return stackView + }() + + private lazy var containerView: UIStackView = { + let stackView = UIStackView() + stackView.axis = .vertical + stackView.spacing = 0 + + stackView.addArrangedSubview(self.buttonStackView) + stackView.addArrangedSubview(self.timerView) + stackView.addArrangedSubview(self.listStackView) + + return stackView + }() + + private lazy var waitingListDataSource = ClientListDataSource(self.waitingListTableView) + + private lazy var workingListDataSource = ClientListDataSource(self.workingListTableView) + + // MARK: - Initializers + + init(bankMirror: BankIntput) { + self.viewModel = bankMirror + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Lifecycle Methods + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + setLayout() + setButtonAction() + self.waitingListTableView.delegate = self + self.workingListTableView.delegate = self + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + let count = (10...30).randomElement()! + runBank(count: count) + } +} + +// MARK: - UITableViewDelegate +extension BankViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + return (tableView as? ClientListTableView)?.makeHeader() + } +} + +// MARK: - BankOutput +extension BankViewController: BankOutput { + func updateWaitingList(with clients: [Client]) { + DispatchQueue.main.async { + self.applyUpdatedWaitingList(with: clients) + } + } + + func updateWorkingList(with clients: [Client]) { + DispatchQueue.main.async { + self.applyUpdatedWorkingList(with: clients) + } + } + + func updateTime(with timeString: String) { + DispatchQueue.main.async { + self.applyUpdatedTime(with: timeString) + } + } +} + +// MARK: - Private Methods +private extension BankViewController { + private func setButtonAction() { + self.addClientButton.addAction( + UIAction { _ in self.addClient(count: 10) }, + for: .touchUpInside + ) + + self.clearButton.addAction( + UIAction { _ in self.resetBank() }, + for: .touchUpInside + ) + } + + func setLayout() { + self.view.backgroundColor = .white + self.view.addSubview(containerView) + self.addClientButton.translatesAutoresizingMaskIntoConstraints = false + self.clearButton.translatesAutoresizingMaskIntoConstraints = false + self.buttonStackView.translatesAutoresizingMaskIntoConstraints = false + self.timerView.translatesAutoresizingMaskIntoConstraints = false + self.waitingListTableView.translatesAutoresizingMaskIntoConstraints = false + self.workingListTableView.translatesAutoresizingMaskIntoConstraints = false + self.listStackView.translatesAutoresizingMaskIntoConstraints = false + self.containerView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + self.containerView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor), + self.containerView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor), + self.containerView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), + self.containerView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor), + + self.buttonStackView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.05), + self.timerView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.1), + ]) + } + + func addClient(count: Int) { + self.viewModel.addClients(count: count) + } + + func runBank(count: Int) { + self.viewModel.startBank(withCount: count) + } + + func resetBank() { + self.viewModel.resetBank() + } + + func applyUpdatedWaitingList(with list: [Client]) { + var snapshot = ClientListSnapShot() + snapshot.appendSections([.client]) + let items = list.map(ClientListItem.client) + snapshot.appendItems(items, toSection: .client) + self.waitingListDataSource.apply(snapshot) + } + + func applyUpdatedWorkingList(with list: [Client]) { + var snapshot = ClientListSnapShot() + snapshot.appendSections([.client]) + let items = list.map(ClientListItem.client) + snapshot.appendItems(items, toSection: .client) + self.workingListDataSource.apply(snapshot) + } + + func applyUpdatedTime(with timeString: String) { + self.timerView.configure(with: timeString) + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/ViewModel/BankIntput.swift b/BankManagerUIApp/BankManagerUIApp/ViewModel/BankIntput.swift new file mode 100644 index 00000000..0ee639d0 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/ViewModel/BankIntput.swift @@ -0,0 +1,12 @@ +// +// BankIntput.swift +// BankManagerUIApp +// +// Created by Effie on 2/8/24. +// + +protocol BankIntput: AnyObject { + func startBank(withCount count: Int) + func resetBank() + func addClients(count: Int) +} diff --git a/BankManagerUIApp/BankManagerUIApp/ViewModel/BankOutput.swift b/BankManagerUIApp/BankManagerUIApp/ViewModel/BankOutput.swift new file mode 100644 index 00000000..4394cdd4 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/ViewModel/BankOutput.swift @@ -0,0 +1,12 @@ +// +// BankOutput.swift +// BankManagerUIApp +// +// Created by Effie on 2/8/24. +// + +protocol BankOutput: AnyObject { + func updateWaitingList(with: [Client]) + func updateWorkingList(with: [Client]) + func updateTime(with timeString: String) +} diff --git a/BankManagerUIApp/BankManagerUIApp/ViewModel/BankViewModel.swift b/BankManagerUIApp/BankManagerUIApp/ViewModel/BankViewModel.swift new file mode 100644 index 00000000..df4843df --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/ViewModel/BankViewModel.swift @@ -0,0 +1,122 @@ +// +// BankViewModel.swift +// BankManagerConsoleApp +// +// Created by Effie on 2/5/24. +// + +import Foundation + +final class BankViewModel { + private let bankManager: BankManager + + weak var delegate: BankOutput? + + private var waitingList: [Client] { + didSet { + self.delegate?.updateWaitingList(with: self.waitingList) + } + } + + private var workingList: [Client] { + didSet { + self.delegate?.updateWorkingList(with: self.workingList) + } + } + + private var timeString: String { + didSet { + self.delegate?.updateTime(with: self.timeString) + } + } + + private var waitingSemaphore = DispatchSemaphore(value: 1) + + private var workingSemaphore = DispatchSemaphore(value: 1) + + init( + bankManager: BankManager + ) { + self.waitingList = [] + self.workingList = [] + self.bankManager = bankManager + self.timeString = "업무시간 - \("00:00:000")" + } +} + +// MARK: - BankIntput +extension BankViewModel: BankIntput { + func startBank(withCount count: Int) { + self.bankManager.start(with: count) + } + + func resetBank() { + self.bankManager.resetBank() + } + + func addClients(count: Int) { + self.bankManager.addClients(count: count) + } +} + +// MARK: - BankManagerDelegate +extension BankViewModel: BankManagerDelegate { + func handleDequeueClient(client: Client) { + DispatchQueue.global().async { + self.waitingSemaphore.wait() + guard + let index = self.waitingList.firstIndex(where: { target in client == target }) + else { + self.waitingSemaphore.signal() + return + } + self.waitingList.remove(at: index) + self.waitingSemaphore.signal() + } + } + + func handleEnqueueClient(client: Client) { + DispatchQueue.global().async { + self.waitingSemaphore.wait() + self.waitingList.append(client) + self.waitingSemaphore.signal() + } + } + + func handleEndTask(client: Client) { + DispatchQueue.global().async { + self.workingSemaphore.wait() + guard + let index = self.workingList.firstIndex(where: { target in client == target }) + else { + self.workingSemaphore.signal() + return + } + self.workingList.remove(at: index) + self.workingSemaphore.signal() + } + } + + func handleStartTask(client: Client) { + DispatchQueue.global().async { + self.workingSemaphore.wait() + self.workingList.append(client) + self.workingSemaphore.signal() + } + } + + func handleClearClient() { + DispatchQueue.global().async { + self.waitingSemaphore.wait() + self.workingSemaphore.wait() + self.waitingList.removeAll() + self.workingList.removeAll() + self.waitingSemaphore.signal() + self.workingSemaphore.signal() + } + } + + func handleTimer(timeString: String) { + self.timeString = timeString + } +}