diff --git a/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj b/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj index 62a20a2d..d88b5a4c 100644 --- a/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj +++ b/BankManagerUIApp/BankManagerUIApp.xcodeproj/project.pbxproj @@ -7,15 +7,24 @@ objects = { /* Begin PBXBuildFile section */ + 08FE43F42B73972500D09BB7 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FE43F32B73972500D09BB7 /* Client.swift */; }; + 08FE43F62B73977A00D09BB7 /* BankTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FE43F52B73977A00D09BB7 /* BankTask.swift */; }; + 08FE43FE2B74611C00D09BB7 /* BankTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08FE43FD2B74611C00D09BB7 /* BankTimer.swift */; }; + 7796775D2B73AB18007315E8 /* ClientTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7796775C2B73AB18007315E8 /* ClientTableViewCell.swift */; }; + 7796775F2B73B208007315E8 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7796775E2B73B208007315E8 /* ViewModel.swift */; }; + 779677632B74575F007315E8 /* UITableViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 779677622B74575F007315E8 /* UITableViewCell+.swift */; }; + 77B02E322B7306E900C3D686 /* UIStackView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B02E312B7306E900C3D686 /* UIStackView+.swift */; }; + 77B02E342B730AD700C3D686 /* Then.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B02E332B730AD700C3D686 /* Then.swift */; }; + 77F8FBB52B74AE510085F544 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F8FBB42B74AE510085F544 /* Observable.swift */; }; + 77F8FBB72B74AEEF0085F544 /* BankViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F8FBB62B74AEEF0085F544 /* BankViewModel.swift */; }; + 77F8FBBA2B74B6A40085F544 /* BankRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F8FBB92B74B6A40085F544 /* BankRepository.swift */; }; 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 */; }; + C7694E3F259C3E9F0053667F /* BankViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7694E3E259C3E9F0053667F /* BankViewController.swift */; }; 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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -36,11 +45,21 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 08FE43F32B73972500D09BB7 /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; + 08FE43F52B73977A00D09BB7 /* BankTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankTask.swift; sourceTree = ""; }; + 08FE43FD2B74611C00D09BB7 /* BankTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankTimer.swift; sourceTree = ""; }; + 7796775C2B73AB18007315E8 /* ClientTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTableViewCell.swift; sourceTree = ""; }; + 7796775E2B73B208007315E8 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; + 779677622B74575F007315E8 /* UITableViewCell+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+.swift"; sourceTree = ""; }; + 77B02E312B7306E900C3D686 /* UIStackView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+.swift"; sourceTree = ""; }; + 77B02E332B730AD700C3D686 /* Then.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Then.swift; sourceTree = ""; }; + 77F8FBB42B74AE510085F544 /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; + 77F8FBB62B74AEEF0085F544 /* BankViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankViewModel.swift; sourceTree = ""; }; + 77F8FBB92B74B6A40085F544 /* BankRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankRepository.swift; sourceTree = ""; }; 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 = ""; }; + C7694E3E259C3E9F0053667F /* BankViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankViewController.swift; 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 +69,6 @@ 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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -78,6 +96,98 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 77F8FBA92B7469500085F544 /* App */ = { + isa = PBXGroup; + children = ( + C7694E3A259C3E9F0053667F /* AppDelegate.swift */, + C7694E3C259C3E9F0053667F /* SceneDelegate.swift */, + ); + path = App; + sourceTree = ""; + }; + 77F8FBAA2B7469630085F544 /* Util */ = { + isa = PBXGroup; + children = ( + 77F8FBB02B746A460085F544 /* Type */, + 77F8FBAD2B74699F0085F544 /* Constant */, + 77F8FBAC2B74697E0085F544 /* Protocol */, + 77F8FBAB2B74696D0085F544 /* Extension */, + ); + path = Util; + sourceTree = ""; + }; + 77F8FBAB2B74696D0085F544 /* Extension */ = { + isa = PBXGroup; + children = ( + 77B02E312B7306E900C3D686 /* UIStackView+.swift */, + 779677622B74575F007315E8 /* UITableViewCell+.swift */, + ); + path = Extension; + sourceTree = ""; + }; + 77F8FBAC2B74697E0085F544 /* Protocol */ = { + isa = PBXGroup; + children = ( + 7796775E2B73B208007315E8 /* ViewModel.swift */, + 77B02E332B730AD700C3D686 /* Then.swift */, + ); + path = Protocol; + sourceTree = ""; + }; + 77F8FBAD2B74699F0085F544 /* Constant */ = { + isa = PBXGroup; + children = ( + 08FE43F52B73977A00D09BB7 /* BankTask.swift */, + ); + path = Constant; + sourceTree = ""; + }; + 77F8FBAE2B7469CF0085F544 /* Resource */ = { + isa = PBXGroup; + children = ( + C7694E43259C3EA20053667F /* Assets.xcassets */, + C7694E45259C3EA20053667F /* LaunchScreen.storyboard */, + C7694E48259C3EA20053667F /* Info.plist */, + ); + path = Resource; + sourceTree = ""; + }; + 77F8FBAF2B746A1B0085F544 /* Scene */ = { + isa = PBXGroup; + children = ( + 77F8FBB12B746A520085F544 /* BankScene */, + ); + path = Scene; + sourceTree = ""; + }; + 77F8FBB02B746A460085F544 /* Type */ = { + isa = PBXGroup; + children = ( + 08FE43F32B73972500D09BB7 /* Client.swift */, + 77F8FBB42B74AE510085F544 /* Observable.swift */, + ); + path = Type; + sourceTree = ""; + }; + 77F8FBB12B746A520085F544 /* BankScene */ = { + isa = PBXGroup; + children = ( + C7694E3E259C3E9F0053667F /* BankViewController.swift */, + 7796775C2B73AB18007315E8 /* ClientTableViewCell.swift */, + 08FE43FD2B74611C00D09BB7 /* BankTimer.swift */, + 77F8FBB62B74AEEF0085F544 /* BankViewModel.swift */, + ); + path = BankScene; + sourceTree = ""; + }; + 77F8FBB82B74B67D0085F544 /* Model */ = { + isa = PBXGroup; + children = ( + 77F8FBB92B74B6A40085F544 /* BankRepository.swift */, + ); + path = Model; + sourceTree = ""; + }; C7694E2E259C3E9F0053667F = { isa = PBXGroup; children = ( @@ -101,14 +211,11 @@ 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 */, + 77F8FBA92B7469500085F544 /* App */, + 77F8FBAA2B7469630085F544 /* Util */, + 77F8FBB82B74B67D0085F544 /* Model */, + 77F8FBAF2B746A1B0085F544 /* Scene */, + 77F8FBAE2B7469CF0085F544 /* Resource */, ); path = BankManagerUIApp; sourceTree = ""; @@ -236,7 +343,6 @@ files = ( C7694E47259C3EA20053667F /* LaunchScreen.storyboard in Resources */, C7694E44259C3EA20053667F /* Assets.xcassets in Resources */, - C7694E42259C3E9F0053667F /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -261,10 +367,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C7694E3F259C3E9F0053667F /* ViewController.swift in Sources */, + 77F8FBB72B74AEEF0085F544 /* BankViewModel.swift in Sources */, + 77F8FBBA2B74B6A40085F544 /* BankRepository.swift in Sources */, + C7694E3F259C3E9F0053667F /* BankViewController.swift in Sources */, + 7796775F2B73B208007315E8 /* ViewModel.swift in Sources */, C7694E3B259C3E9F0053667F /* AppDelegate.swift in Sources */, + 779677632B74575F007315E8 /* UITableViewCell+.swift in Sources */, + 77F8FBB52B74AE510085F544 /* Observable.swift in Sources */, + 08FE43F62B73977A00D09BB7 /* BankTask.swift in Sources */, + 7796775D2B73AB18007315E8 /* ClientTableViewCell.swift in Sources */, + 77B02E322B7306E900C3D686 /* UIStackView+.swift in Sources */, + 08FE43FE2B74611C00D09BB7 /* BankTimer.swift in Sources */, + 08FE43F42B73972500D09BB7 /* Client.swift in Sources */, + 77B02E342B730AD700C3D686 /* Then.swift in Sources */, C7694E3D259C3E9F0053667F /* SceneDelegate.swift in Sources */, - C7D65D1E259C81BD005510E0 /* BankManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -300,14 +416,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 +549,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = BankManagerUIApp/Info.plist; + DEVELOPMENT_TEAM = VGM28F2FH7; + INFOPLIST_FILE = BankManagerUIApp/Resource/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -460,7 +569,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = BankManagerUIApp/Info.plist; + DEVELOPMENT_TEAM = VGM28F2FH7; + INFOPLIST_FILE = BankManagerUIApp/Resource/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/BankManagerUIApp/BankManagerUIApp/AppDelegate.swift b/BankManagerUIApp/BankManagerUIApp/App/AppDelegate.swift similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/AppDelegate.swift rename to BankManagerUIApp/BankManagerUIApp/App/AppDelegate.swift diff --git a/BankManagerUIApp/BankManagerUIApp/SceneDelegate.swift b/BankManagerUIApp/BankManagerUIApp/App/SceneDelegate.swift similarity index 80% rename from BankManagerUIApp/BankManagerUIApp/SceneDelegate.swift rename to BankManagerUIApp/BankManagerUIApp/App/SceneDelegate.swift index 63d2d2a1..deffed98 100644 --- a/BankManagerUIApp/BankManagerUIApp/SceneDelegate.swift +++ b/BankManagerUIApp/BankManagerUIApp/App/SceneDelegate.swift @@ -10,12 +10,12 @@ 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 } + guard let windowScene = (scene as? UIWindowScene) else { return } + + window = UIWindow(windowScene: windowScene) + window?.rootViewController = BankViewController(viewModel: BankViewModel()) + window?.makeKeyAndVisible() } func sceneDidDisconnect(_ scene: UIScene) { 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/Model/BankRepository.swift b/BankManagerUIApp/BankManagerUIApp/Model/BankRepository.swift new file mode 100644 index 00000000..2c280961 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Model/BankRepository.swift @@ -0,0 +1,26 @@ +// +// BankRepository.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/08. +// + +import Foundation + +final class BankRepository { + + private var clientNumber: Int = 1 + + func makeClientData(onCompleted: @escaping ([Client]) -> Void) { + let startNumber = clientNumber + var waitingQueue: [Client] = [] + + (startNumber..<(startNumber + 10)).forEach { + guard let task = BankTask.allCases.randomElement() else { return } + waitingQueue.append(Client(number: $0, bankTask: task)) + } + + clientNumber += 10 + onCompleted(waitingQueue) + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AccentColor.colorset/Contents.json b/BankManagerUIApp/BankManagerUIApp/Resource/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AccentColor.colorset/Contents.json rename to BankManagerUIApp/BankManagerUIApp/Resource/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/BankManagerUIApp/BankManagerUIApp/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Assets.xcassets/AppIcon.appiconset/Contents.json rename to BankManagerUIApp/BankManagerUIApp/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/BankManagerUIApp/BankManagerUIApp/Assets.xcassets/Contents.json b/BankManagerUIApp/BankManagerUIApp/Resource/Assets.xcassets/Contents.json similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Assets.xcassets/Contents.json rename to BankManagerUIApp/BankManagerUIApp/Resource/Assets.xcassets/Contents.json diff --git a/BankManagerUIApp/BankManagerUIApp/Base.lproj/LaunchScreen.storyboard b/BankManagerUIApp/BankManagerUIApp/Resource/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from BankManagerUIApp/BankManagerUIApp/Base.lproj/LaunchScreen.storyboard rename to BankManagerUIApp/BankManagerUIApp/Resource/Base.lproj/LaunchScreen.storyboard diff --git a/BankManagerUIApp/BankManagerUIApp/Info.plist b/BankManagerUIApp/BankManagerUIApp/Resource/Info.plist similarity index 94% rename from BankManagerUIApp/BankManagerUIApp/Info.plist rename to BankManagerUIApp/BankManagerUIApp/Resource/Info.plist index 5b531f7b..2688b32b 100644 --- a/BankManagerUIApp/BankManagerUIApp/Info.plist +++ b/BankManagerUIApp/BankManagerUIApp/Resource/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/Scene/BankScene/BankTimer.swift b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankTimer.swift new file mode 100644 index 00000000..b90d4dde --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankTimer.swift @@ -0,0 +1,45 @@ +// +// TimerHandler.swift +// BankManagerUIApp +// +// Created by ㅣ on 2/8/24. +// + +import Foundation + +private struct TimeUnit { + let hours: Int + let minutes: Int + let seconds: Int +} + +final class BankTimer { + + private var timer = Timer() + private var count = 0 + var timeString: ((String) -> Void)? + + func start() { + timer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(timerCounter), userInfo: nil, repeats: true) + } + + func reset() { + timer.invalidate() + timeString?("00:00:00") + } + + @objc private func timerCounter() { + count += 1 + let time = secondsToHoursMinutesSeconds(seconds: count) + let convertedTimeString = makeTimeString(time) + timeString?(convertedTimeString) + } + + private func secondsToHoursMinutesSeconds(seconds: Int) -> TimeUnit { + return TimeUnit.init(hours: (seconds / 3600), minutes: ((seconds % 3600) / 60), seconds: ((seconds % 3600) % 60)) + } + + private func makeTimeString(_ timeUnit: TimeUnit) -> String { + return String(format: "%02d:%02d:%02d", timeUnit.hours, timeUnit.minutes, timeUnit.seconds) + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankViewController.swift b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankViewController.swift new file mode 100644 index 00000000..e262a970 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankViewController.swift @@ -0,0 +1,219 @@ +// +// BankManagerUIApp - BankViewController.swift +// Created by yagom. +// Copyright © yagom academy. All rights reserved. +// + +import UIKit + +final class BankViewController: UIViewController { + + // MARK: Properties + private let addCustomerButton = UIButton().then { + $0.setTitle("고객 10명 추가", for: .normal) + $0.setTitleColor(.systemBlue, for: .normal) + } + + private let resetButton = UIButton().then { + $0.setTitle("초기화", for: .normal) + $0.setTitleColor(.systemRed, for: .normal) + } + + private let optionButtonStackView = UIStackView().then { + $0.axis = .horizontal + $0.alignment = .fill + $0.distribution = .fillEqually + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private let timerLabel = UILabel().then { + $0.text = "업무시간 - " + $0.textAlignment = .center + $0.font = .systemFont(ofSize: 22) + } + + private let greenView = UIView().then { + $0.backgroundColor = .systemGreen + } + + private let purpleView = UIView().then { + $0.backgroundColor = .systemPurple + } + + private let waitingQueueTitleLabel = UILabel().then { + $0.text = "대기중" + $0.textAlignment = .center + $0.textColor = .white + $0.font = .systemFont(ofSize: 40) + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private let workingQueueTitleLabel = UILabel().then { + $0.text = "업무중" + $0.textAlignment = .center + $0.textColor = .white + $0.font = .systemFont(ofSize: 40) + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private let titleStackView = UIStackView().then { + $0.axis = .horizontal + $0.alignment = .fill + $0.distribution = .fillEqually + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private let headerStackView = UIStackView().then { + $0.axis = .vertical + $0.alignment = .fill + $0.distribution = .fillEqually + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private let waitingQueueTableView = UITableView().then { + $0.separatorStyle = .none + } + + private let workingQueueTableView = UITableView().then { + $0.separatorStyle = .none + } + + private let tableStackView = UIStackView().then { + $0.axis = .horizontal + $0.alignment = .fill + $0.distribution = .fillEqually + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private let entireStackView = UIStackView().then { + $0.axis = .vertical + $0.alignment = .fill + $0.translatesAutoresizingMaskIntoConstraints = false + } + + private let viewModel: BankViewModel + + // MARK: Initializer + init(viewModel: BankViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("BankViewController Init Error!") + } + + // MARK: Life Cycle + override func viewDidLoad() { + super.viewDidLoad() + + configureUI() + setUpTableView() + addButtonTarget() + bind() + } + + // MARK: Custom Methods + private func configureUI() { + view.backgroundColor = .white + view.addSubview(entireStackView) + entireStackView.addArrangedSubviews([headerStackView, tableStackView]) + headerStackView.addArrangedSubviews([optionButtonStackView, timerLabel, titleStackView]) + tableStackView.addArrangedSubviews([waitingQueueTableView, workingQueueTableView]) + titleStackView.addArrangedSubviews([greenView, purpleView]) + optionButtonStackView.addArrangedSubviews([addCustomerButton, resetButton]) + greenView.addSubview(waitingQueueTitleLabel) + purpleView.addSubview(workingQueueTitleLabel) + + NSLayoutConstraint.activate([ + entireStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + entireStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + entireStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + entireStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + headerStackView.heightAnchor.constraint(equalTo: entireStackView.heightAnchor, multiplier: 0.2), + waitingQueueTitleLabel.topAnchor.constraint(equalTo: greenView.topAnchor), + waitingQueueTitleLabel.leadingAnchor.constraint(equalTo: greenView.leadingAnchor), + waitingQueueTitleLabel.trailingAnchor.constraint(equalTo: greenView.trailingAnchor), + waitingQueueTitleLabel.bottomAnchor.constraint(equalTo: greenView.bottomAnchor), + workingQueueTitleLabel.topAnchor.constraint(equalTo: purpleView.topAnchor), + workingQueueTitleLabel.leadingAnchor.constraint(equalTo: purpleView.leadingAnchor), + workingQueueTitleLabel.trailingAnchor.constraint(equalTo: purpleView.trailingAnchor), + workingQueueTitleLabel.bottomAnchor.constraint(equalTo: purpleView.bottomAnchor) + ]) + } + + private func setUpTableView() { + [waitingQueueTableView, workingQueueTableView].forEach { + $0.register(ClientTableViewCell.self, forCellReuseIdentifier: ClientTableViewCell.className) + $0.delegate = self + $0.dataSource = self + } + } + + private func addButtonTarget() { + addCustomerButton.addTarget(self, action: #selector(addCustommerButtonDidTap), for: .touchUpInside) + resetButton.addTarget(self, action: #selector(resetButtonDidTap), for: .touchUpInside) + } + + private func bind() { + viewModel.waitingClients.subscribe { [weak self] _ in + self?.waitingQueueTableView.reloadData() + } + + viewModel.workingClients.subscribe { [weak self] _ in + self?.workingQueueTableView.reloadData() + } + + viewModel.timeString.subscribe { [weak self] result in + self?.timerLabel.text = result + } + } + + // MARK: @objc Methods + @objc private func addCustommerButtonDidTap() { + viewModel.fetchData() + } + + @objc private func resetButtonDidTap() { + viewModel.reset() + } +} + +// MARK: - UITableViewDelegate +extension BankViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 40 + } +} + +// MARK: - UITableViewDataSource +extension BankViewController: UITableViewDataSource { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch tableView { + case waitingQueueTableView: + return viewModel.waitingClients.value.count + case workingQueueTableView: + return viewModel.workingClients.value.count + default: + return 0 + } + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ClientTableViewCell.className, for: indexPath) as? ClientTableViewCell + else { return UITableViewCell() } + + switch tableView { + case waitingQueueTableView: + cell.setUpData(data: viewModel.waitingClients.value[indexPath.row]) + case workingQueueTableView: + cell.setUpData(data: viewModel.workingClients.value[indexPath.row]) + default: + break + } + + return cell + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankViewModel.swift b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankViewModel.swift new file mode 100644 index 00000000..4678d2cd --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/BankViewModel.swift @@ -0,0 +1,66 @@ +// +// BankViewModel.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/08. +// + +import Foundation + +final class BankViewModel { + + // MARK: Properties + private let repository = BankRepository() + private let timer = BankTimer() + + private(set) var waitingClients: Observable<[Client]> = Observable([]) + private(set) var workingClients: Observable<[Client]> = Observable([]) + private(set) var timeString: Observable = Observable("") + private let loanSemaphore = DispatchSemaphore(value: 1) + private let depositSemaphore = DispatchSemaphore(value: 2) + + // MARK: Custom Methods + func fetchData() { + repository.makeClientData { [weak self] result in + self?.waitingClients.value += result + self?.startTask() + self?.start() + } + } + + func fetchTimeString() { + timer.timeString = { [weak self] in + self?.timeString.value = $0 + } + } + + func start() { + timer.start() + fetchTimeString() + } + + func reset() { + timer.reset() + } + + func startTask() { + while !waitingClients.value.isEmpty { + let client = waitingClients.value.removeFirst() + workingClients.value.append(client) + handleTask(of: client) + } + } + + private func handleTask(of client: Client) { + let semaphore = client.bankTask == .loan ? loanSemaphore : depositSemaphore + + semaphore.wait() + DispatchQueue.global().async { [weak self] in + guard let self = self else {return} + Thread.sleep(forTimeInterval: client.bankTask.requiredTime) + if self.workingClients.value.isEmpty { return } + self.workingClients.value.removeFirst() + semaphore.signal() + } + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/ClientTableViewCell.swift b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/ClientTableViewCell.swift new file mode 100644 index 00000000..242d892f --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Scene/BankScene/ClientTableViewCell.swift @@ -0,0 +1,54 @@ +// +// ClientTableViewCell.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/07. +// + +import UIKit + +final class ClientTableViewCell: UITableViewCell { + + // MARK: Properties + private let titleLabel = UILabel().then { + $0.font = .systemFont(ofSize: 20) + $0.font = .systemFont(ofSize: 20, weight: .medium) + } + + // MARK: Initializer + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + configureUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Life Cycle + override func prepareForReuse() { + super.prepareForReuse() + + titleLabel.text = nil + titleLabel.textColor = .black + } + + // MARK: Custom Methods + private func configureUI() { + selectionStyle = .none + + contentView.addSubview(titleLabel) + titleLabel.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + titleLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), + titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) + ]) + } + + func setUpData(data: Client) { + titleLabel.text = "\(data.number) - \(data.bankTask.description)" + titleLabel.textColor = data.bankTask == .loan ? .systemPurple : .black + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Util/Constant/BankTask.swift b/BankManagerUIApp/BankManagerUIApp/Util/Constant/BankTask.swift new file mode 100644 index 00000000..d733feed --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Util/Constant/BankTask.swift @@ -0,0 +1,26 @@ +// +// BankTask.swift +// BankManagerUIApp +// +// Created by ㅣ on 2/7/24. +// + +import Foundation + +enum BankTask: String, CaseIterable { + case loan = "대출" + case deposit = "예금" + + var description: String { + return self.rawValue + } + + var requiredTime: Double { + switch self { + case .loan: + return 1.1 + case .deposit: + return 0.7 + } + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Util/Extension/UIStackView+.swift b/BankManagerUIApp/BankManagerUIApp/Util/Extension/UIStackView+.swift new file mode 100644 index 00000000..1307d425 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Util/Extension/UIStackView+.swift @@ -0,0 +1,16 @@ +// +// UIStackView+.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/07. +// + +import UIKit + +extension UIStackView { + + /// 다수의 View를 StackView에 한꺼번에 추가하는 메서드 + func addArrangedSubviews(_ views: [UIView]) { + views.forEach { self.addArrangedSubview($0) } + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Util/Extension/UITableViewCell+.swift b/BankManagerUIApp/BankManagerUIApp/Util/Extension/UITableViewCell+.swift new file mode 100644 index 00000000..0746309d --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Util/Extension/UITableViewCell+.swift @@ -0,0 +1,19 @@ +// +// UITableViewCell+.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/08. +// + +import UIKit + +extension UITableViewCell { + + static var className: String { + NSStringFromClass(self.classForCoder()).components(separatedBy: ".").last ?? "" + } + + var className: String { + NSStringFromClass(self.classForCoder).components(separatedBy: ".").last ?? "" + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Util/Protocol/Then.swift b/BankManagerUIApp/BankManagerUIApp/Util/Protocol/Then.swift new file mode 100644 index 00000000..eefb39a8 --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Util/Protocol/Then.swift @@ -0,0 +1,21 @@ +// +// Then.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/07. +// + +import UIKit + +protocol Then {} + +extension Then where Self: AnyObject { + + @inlinable + func then(_ block: (Self) throws -> Void) rethrows -> Self { + try block(self) + return self + } +} + +extension NSObject: Then {} diff --git a/BankManagerUIApp/BankManagerUIApp/Util/Protocol/ViewModel.swift b/BankManagerUIApp/BankManagerUIApp/Util/Protocol/ViewModel.swift new file mode 100644 index 00000000..1bb469cd --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Util/Protocol/ViewModel.swift @@ -0,0 +1,15 @@ +// +// ViewModel.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/07. +// + +import Foundation + +protocol ViewModel { + associatedtype Input + associatedtype Output + + func transform(input: Input) -> Output +} diff --git a/BankManagerUIApp/BankManagerUIApp/Util/Type/Client.swift b/BankManagerUIApp/BankManagerUIApp/Util/Type/Client.swift new file mode 100644 index 00000000..759b91cb --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Util/Type/Client.swift @@ -0,0 +1,18 @@ +// +// Customer.swift +// BankManagerUIApp +// +// Created by ㅣ on 2/7/24. +// + +import Foundation + +struct Client { + let number: Int + let bankTask: BankTask + + init(number: Int, bankTask: BankTask) { + self.number = number + self.bankTask = bankTask + } +} diff --git a/BankManagerUIApp/BankManagerUIApp/Util/Type/Observable.swift b/BankManagerUIApp/BankManagerUIApp/Util/Type/Observable.swift new file mode 100644 index 00000000..1b57c15c --- /dev/null +++ b/BankManagerUIApp/BankManagerUIApp/Util/Type/Observable.swift @@ -0,0 +1,28 @@ +// +// Observable.swift +// BankManagerUIApp +// +// Created by EUNJU on 2024/02/08. +// + +import Foundation + +final class Observable { + + private var listener: ((T) -> Void)? + + var value: T { + didSet { + self.listener?(value) + } + } + + init(_ value: T) { + self.value = value + } + + func subscribe(_ closure: @escaping (T) -> Void) { + closure(value) + self.listener = closure + } +} 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. - } - - -} -