From cee65b73e45fa45f7ebe6eb00d8d74c1ad66d82a Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Mon, 27 Nov 2023 17:49:07 +0900 Subject: [PATCH 01/10] =?UTF-8?q?=F0=9F=94=A7=20EditProfile=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=ED=8F=B4=EB=8D=94=20=EB=B0=8F=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 50 +++++++++++++++++-- .../EditProfile/EditProfileInteractor.swift | 30 +++++++++++ .../EditProfile/EditProfileModels.swift | 13 +++++ .../EditProfile/EditProfilePresenter.swift | 22 ++++++++ .../EditProfile/EditProfileRouter.swift | 35 +++++++++++++ .../EditProfileViewController.swift | 40 +++++++++++++++ .../EditProfile/EditProfileWorker.swift | 19 +++++++ .../{ => Views}/ProfileHeaderView.swift | 0 .../ThumbnailCollectionViewCell.swift | 0 9 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift create mode 100644 iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift create mode 100644 iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift create mode 100644 iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift create mode 100644 iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift create mode 100644 iOS/Layover/Layover/Scenes/EditProfile/EditProfileWorker.swift rename iOS/Layover/Layover/Scenes/Profile/{ => Views}/ProfileHeaderView.swift (100%) rename iOS/Layover/Layover/Scenes/Profile/{ => Views}/ThumbnailCollectionViewCell.swift (100%) diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index 0119865..d311a9c 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -67,6 +67,12 @@ FC4975932B03432800D8627F /* Pretendard-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FC49758A2B03432800D8627F /* Pretendard-Bold.ttf */; }; FC4975942B03432800D8627F /* Pretendard-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FC49758B2B03432800D8627F /* Pretendard-Regular.ttf */; }; FC4975992B03439000D8627F /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC4975982B03439000D8627F /* UIFont+.swift */; }; + FC5BE11C2B148D160036366D /* EditProfilePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1162B148D160036366D /* EditProfilePresenter.swift */; }; + FC5BE11D2B148D160036366D /* EditProfileWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1172B148D160036366D /* EditProfileWorker.swift */; }; + FC5BE11E2B148D160036366D /* EditProfileRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1182B148D160036366D /* EditProfileRouter.swift */; }; + FC5BE11F2B148D160036366D /* EditProfileModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1192B148D160036366D /* EditProfileModels.swift */; }; + FC5BE1202B148D170036366D /* EditProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE11A2B148D160036366D /* EditProfileViewController.swift */; }; + FC5BE1212B148D170036366D /* EditProfileInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE11B2B148D160036366D /* EditProfileInteractor.swift */; }; FC68E29B2B02325D001AABFF /* Requestable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E29A2B02325D001AABFF /* Requestable.swift */; }; FC68E29D2B02326A001AABFF /* Responsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E29C2B02326A001AABFF /* Responsable.swift */; }; FC68E29F2B023315001AABFF /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E29E2B023315001AABFF /* HTTPMethod.swift */; }; @@ -181,6 +187,12 @@ FC49758A2B03432800D8627F /* Pretendard-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Bold.ttf"; sourceTree = ""; }; FC49758B2B03432800D8627F /* Pretendard-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Regular.ttf"; sourceTree = ""; }; FC4975982B03439000D8627F /* UIFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = ""; }; + FC5BE1162B148D160036366D /* EditProfilePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfilePresenter.swift; sourceTree = ""; }; + FC5BE1172B148D160036366D /* EditProfileWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileWorker.swift; sourceTree = ""; }; + FC5BE1182B148D160036366D /* EditProfileRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileRouter.swift; sourceTree = ""; }; + FC5BE1192B148D160036366D /* EditProfileModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileModels.swift; sourceTree = ""; }; + FC5BE11A2B148D160036366D /* EditProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileViewController.swift; sourceTree = ""; }; + FC5BE11B2B148D160036366D /* EditProfileInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileInteractor.swift; sourceTree = ""; }; FC68E29A2B02325D001AABFF /* Requestable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Requestable.swift; sourceTree = ""; }; FC68E29C2B02326A001AABFF /* Responsable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Responsable.swift; sourceTree = ""; }; FC68E29E2B023315001AABFF /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; @@ -389,7 +401,7 @@ path = Cell; sourceTree = ""; }; - FC2511A72B04DA9C004717BC /* Map */ = { + FC2511A72B04DA9C004717BC /* Map */ = { isa = PBXGroup; children = ( FC767FA62B1269980088CF9B /* Views */, @@ -414,6 +426,28 @@ path = Fonts; sourceTree = ""; }; + FC5BE1112B148AF00036366D /* Views */ = { + isa = PBXGroup; + children = ( + FC930E7F2B0CFB0B00AA48E3 /* ProfileHeaderView.swift */, + FC930E7D2B0CDB2900AA48E3 /* ThumbnailCollectionViewCell.swift */, + ); + path = Views; + sourceTree = ""; + }; + FC5BE1152B148B540036366D /* EditProfile */ = { + isa = PBXGroup; + children = ( + FC5BE11A2B148D160036366D /* EditProfileViewController.swift */, + FC5BE11B2B148D160036366D /* EditProfileInteractor.swift */, + FC5BE1162B148D160036366D /* EditProfilePresenter.swift */, + FC5BE1192B148D160036366D /* EditProfileModels.swift */, + FC5BE1182B148D160036366D /* EditProfileRouter.swift */, + FC5BE1172B148D160036366D /* EditProfileWorker.swift */, + ); + path = EditProfile; + sourceTree = ""; + }; FC68E2992B02320F001AABFF /* Network */ = { isa = PBXGroup; children = ( @@ -437,8 +471,8 @@ FC767F9A2B12283D0088CF9B /* PatchUserName.json */, ); path = MockData; - sourceTree = ""; - }; + sourceTree = ""; + }; FC767FA62B1269980088CF9B /* Views */ = { isa = PBXGroup; children = ( @@ -524,6 +558,7 @@ 194551EB2B037F1E00299768 /* Login */, 1945521A2B0478A100299768 /* Home */, FC930E6E2B0CD73B00AA48E3 /* Profile */, + FC5BE1152B148B540036366D /* EditProfile */, 194552272B0479B600299768 /* BaseViewController.swift */, ); path = Scenes; @@ -567,14 +602,13 @@ FC930E6E2B0CD73B00AA48E3 /* Profile */ = { isa = PBXGroup; children = ( + FC5BE1112B148AF00036366D /* Views */, FC930E732B0CD75C00AA48E3 /* ProfileViewController.swift */, FC930E742B0CD75C00AA48E3 /* ProfileInteractor.swift */, FC930E6F2B0CD75C00AA48E3 /* ProfilePresenter.swift */, FC930E722B0CD75C00AA48E3 /* ProfileModels.swift */, FC930E712B0CD75C00AA48E3 /* ProfileRouter.swift */, FC930E7B2B0CD80800AA48E3 /* ProfileConfigurator.swift */, - FC930E7D2B0CDB2900AA48E3 /* ThumbnailCollectionViewCell.swift */, - FC930E7F2B0CFB0B00AA48E3 /* ProfileHeaderView.swift */, ); path = Profile; sourceTree = ""; @@ -729,19 +763,23 @@ 19AACFCA2B0F7C3B0088143E /* Response.swift in Sources */, FC767F972B1224B80088CF9B /* IntroduceDTO.swift in Sources */, 1972CCD42B138E6B00C3C762 /* SignUpRouter.swift in Sources */, + FC5BE11C2B148D160036366D /* EditProfilePresenter.swift in Sources */, FC68E2A32B0233BC001AABFF /* NetworkError.swift in Sources */, FC3F3BD82B069EB30080E3A6 /* MapCarouselCollectionViewCell.swift in Sources */, 194552252B0478B400299768 /* HomeViewController.swift in Sources */, 194552222B0478B400299768 /* HomeWorker.swift in Sources */, + FC5BE1202B148D170036366D /* EditProfileViewController.swift in Sources */, 194552282B0479B600299768 /* BaseViewController.swift in Sources */, 194552212B0478B400299768 /* HomePresenter.swift in Sources */, 1972CCDA2B13A4BA00C3C762 /* SignUpWorker.swift in Sources */, 1945520D2B0399E500299768 /* MainTabBarViewController.swift in Sources */, FC2511AB2B04EA6B004717BC /* MapConfigurator.swift in Sources */, 1945523B2B05258200299768 /* HomeConfigurator.swift in Sources */, + FC5BE11D2B148D160036366D /* EditProfileWorker.swift in Sources */, 194551F62B037F2D00299768 /* LoginViewController.swift in Sources */, FC767FA52B125F430088CF9B /* UIViewController+.swift in Sources */, FCEE0FF22B036B6000195BBE /* LOButton.swift in Sources */, + FC5BE1212B148D170036366D /* EditProfileInteractor.swift in Sources */, FC930E7C2B0CD80800AA48E3 /* ProfileConfigurator.swift in Sources */, FC2511A62B049020004717BC /* SignUpConfigurator.swift in Sources */, 194552392B05230E00299768 /* HomeCarouselCollectionViewCell.swift in Sources */, @@ -752,8 +790,10 @@ 194552312B04DA1A00299768 /* LOCircleButton.swift in Sources */, 194552262B0478B400299768 /* HomeInteractor.swift in Sources */, FC930E772B0CD75C00AA48E3 /* ProfileRouter.swift in Sources */, + FC5BE11F2B148D160036366D /* EditProfileModels.swift in Sources */, 194551F42B037F2D00299768 /* LoginRouter.swift in Sources */, FC4975992B03439000D8627F /* UIFont+.swift in Sources */, + FC5BE11E2B148D160036366D /* EditProfileRouter.swift in Sources */, FC767F952B1222350088CF9B /* ProfileImageDTO.swift in Sources */, 194551F52B037F2D00299768 /* LoginModels.swift in Sources */, 835A619E2B068115002F22A5 /* PlaybackWorker.swift in Sources */, diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift new file mode 100644 index 0000000..fb9e1f4 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift @@ -0,0 +1,30 @@ +// +// EditProfileInteractor.swift +// Layover +// +// Created by kong on 2023/11/27. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol EditProfileBusinessLogic { + +} + +protocol EditProfileDataStore { + +} + +class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStore { + + // MARK: - Properties + + typealias Models = EditProfileModels + + lazy var worker = EditProfileWorker() + var presenter: EditProfilePresentationLogic? + + // MARK: - Use Case - Fetch From Local DataStore + +} diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift new file mode 100644 index 0000000..4482fe0 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift @@ -0,0 +1,13 @@ +// +// EditProfileModels.swift +// Layover +// +// Created by kong on 2023/11/27. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +enum EditProfileModels { + +} diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift new file mode 100644 index 0000000..30f05a4 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift @@ -0,0 +1,22 @@ +// +// EditProfilePresenter.swift +// Layover +// +// Created by kong on 2023/11/27. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol EditProfilePresentationLogic { + +} + +final class EditProfilePresenter: EditProfilePresentationLogic { + + // MARK: - Properties + + typealias Models = EditProfileModels + weak var viewController: EditProfileDisplayLogic? + +} diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift new file mode 100644 index 0000000..85e493b --- /dev/null +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift @@ -0,0 +1,35 @@ +// +// EditProfileRouter.swift +// Layover +// +// Created by kong on 2023/11/27. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol EditProfileRoutingLogic { + func routeToNext() +} + +protocol EditProfileDataPassing { + var dataStore: EditProfileDataStore? { get } +} + +final class EditProfileRouter: NSObject, EditProfileRoutingLogic, EditProfileDataPassing { + + // MARK: - Properties + + weak var viewController: EditProfileViewController? + var dataStore: EditProfileDataStore? + + // MARK: - Routing + + func routeToNext() { + // let destinationVC = UIStoryboard(name: "", bundle: nil).instantiateViewController(withIdentifier: "") as! NextViewController + // var destinationDS = destinationVC.router!.dataStore! + // passDataTo(destinationDS, from: dataStore!) + // viewController?.navigationController?.pushViewController(destinationVC, animated: true) + } + +} diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift new file mode 100644 index 0000000..093854f --- /dev/null +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -0,0 +1,40 @@ +// +// EditProfileViewController.swift +// Layover +// +// Created by kong on 2023/11/27. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol EditProfileDisplayLogic: AnyObject { + +} + +final class EditProfileViewController: UIViewController, EditProfileDisplayLogic { + + // MARK: - Properties + + typealias Models = EditProfileModels + var router: (NSObjectProtocol & EditProfileRoutingLogic & EditProfileDataPassing)? + var interactor: EditProfileBusinessLogic? + + // MARK: - Object Lifecycle + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + + // MARK: - View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + } + +} diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileWorker.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileWorker.swift new file mode 100644 index 0000000..653c75c --- /dev/null +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileWorker.swift @@ -0,0 +1,19 @@ +// +// EditProfileWorker.swift +// Layover +// +// Created by kong on 2023/11/27. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +final class EditProfileWorker { + + // MARK: - Properties + + typealias Models = EditProfileModels + + // MARK: - Methods + +} diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileHeaderView.swift b/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift similarity index 100% rename from iOS/Layover/Layover/Scenes/Profile/ProfileHeaderView.swift rename to iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift diff --git a/iOS/Layover/Layover/Scenes/Profile/ThumbnailCollectionViewCell.swift b/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift similarity index 100% rename from iOS/Layover/Layover/Scenes/Profile/ThumbnailCollectionViewCell.swift rename to iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift From a55f3e6d652110a88f7328a42bd653397e8a3fd1 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Mon, 27 Nov 2023 17:50:50 +0900 Subject: [PATCH 02/10] =?UTF-8?q?=F0=9F=94=A7=20UserWorekr=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EB=A9=94=EC=86=8C=EB=93=9C=20modify=EC=97=90?= =?UTF-8?q?=EC=84=9C=20update=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover/Workers/MockUserWorker.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iOS/Layover/Layover/Workers/MockUserWorker.swift b/iOS/Layover/Layover/Workers/MockUserWorker.swift index b892917..25eace0 100644 --- a/iOS/Layover/Layover/Workers/MockUserWorker.swift +++ b/iOS/Layover/Layover/Workers/MockUserWorker.swift @@ -18,7 +18,7 @@ final class MockUserWorker: UserWorkerProtocol { // MARK: - Methods - func modifyNickname(to nickname: String) async throws -> String { + func updateNickname(to nickname: String) async throws -> String { guard let fileLocation = Bundle.main.url(forResource: "PatchUserName", withExtension: "json") else { throw NetworkError.unknown } let mockData = try Data(contentsOf: fileLocation) @@ -58,7 +58,7 @@ final class MockUserWorker: UserWorkerProtocol { return data.exist } - func modifyIntroduce(to introduce: String) async throws -> String { + func updateIntroduce(to introduce: String) async throws -> String { guard let fileLocation = Bundle.main.url(forResource: "PatchIntroduce", withExtension: "json") else { throw NetworkError.unknown } let mockData = try Data(contentsOf: fileLocation) @@ -101,10 +101,10 @@ final class MockUserWorker: UserWorkerProtocol { } protocol UserWorkerProtocol { - func modifyNickname(to nickname: String) async throws -> String + func updateNickname(to nickname: String) async throws -> String func checkDuplication(for nickname: String) async throws -> Bool // TODO: multipart request 구현 - // func modifyProfileImage() async throws -> URL - func modifyIntroduce(to introduce: String) async throws -> String + // func updateProfileImage() async throws -> URL + func updateIntroduce(to introduce: String) async throws -> String func withdraw() async throws -> String } From 44ebf1dbb1e8433c0fed6408271476cb2fc98423 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Mon, 27 Nov 2023 19:46:17 +0900 Subject: [PATCH 03/10] =?UTF-8?q?=E2=9C=A8=20EditProfileConfigurator=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 4 +++ .../EditProfile/EditProfileConfigurator.swift | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index d311a9c..8db0b74 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -73,6 +73,7 @@ FC5BE11F2B148D160036366D /* EditProfileModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1192B148D160036366D /* EditProfileModels.swift */; }; FC5BE1202B148D170036366D /* EditProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE11A2B148D160036366D /* EditProfileViewController.swift */; }; FC5BE1212B148D170036366D /* EditProfileInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE11B2B148D160036366D /* EditProfileInteractor.swift */; }; + FC5BE1232B1490660036366D /* EditProfileConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC5BE1222B1490660036366D /* EditProfileConfigurator.swift */; }; FC68E29B2B02325D001AABFF /* Requestable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E29A2B02325D001AABFF /* Requestable.swift */; }; FC68E29D2B02326A001AABFF /* Responsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E29C2B02326A001AABFF /* Responsable.swift */; }; FC68E29F2B023315001AABFF /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E29E2B023315001AABFF /* HTTPMethod.swift */; }; @@ -193,6 +194,7 @@ FC5BE1192B148D160036366D /* EditProfileModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileModels.swift; sourceTree = ""; }; FC5BE11A2B148D160036366D /* EditProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileViewController.swift; sourceTree = ""; }; FC5BE11B2B148D160036366D /* EditProfileInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileInteractor.swift; sourceTree = ""; }; + FC5BE1222B1490660036366D /* EditProfileConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileConfigurator.swift; sourceTree = ""; }; FC68E29A2B02325D001AABFF /* Requestable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Requestable.swift; sourceTree = ""; }; FC68E29C2B02326A001AABFF /* Responsable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Responsable.swift; sourceTree = ""; }; FC68E29E2B023315001AABFF /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; @@ -444,6 +446,7 @@ FC5BE1192B148D160036366D /* EditProfileModels.swift */, FC5BE1182B148D160036366D /* EditProfileRouter.swift */, FC5BE1172B148D160036366D /* EditProfileWorker.swift */, + FC5BE1222B1490660036366D /* EditProfileConfigurator.swift */, ); path = EditProfile; sourceTree = ""; @@ -813,6 +816,7 @@ FC68E29D2B02326A001AABFF /* Responsable.swift in Sources */, FC2511A02B045C0A004717BC /* SignUpInteractor.swift in Sources */, FC767F862B1214C10088CF9B /* CheckUserNameDTO.swift in Sources */, + FC5BE1232B1490660036366D /* EditProfileConfigurator.swift in Sources */, 1945522A2B04883800299768 /* UIView+.swift in Sources */, FCE52FFA2B0FCB0A002CDB75 /* URLSession+.swift in Sources */, 19743C052B06940D001E405A /* PlayerView.swift in Sources */, diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift new file mode 100644 index 0000000..f695201 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift @@ -0,0 +1,33 @@ +// +// EditProfileConfigurator.swift +// Layover +// +// Created by kong on 2023/11/27. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +final class EditProfileConfigurator: Configurator { + + typealias ViewController = EditProfileViewController + + static let shared = EditProfileConfigurator() + + private init() { } + + func configure(_ viewController: ViewController) { + let router = EditProfileRouter() + router.viewController = viewController + + let presenter = EditProfilePresenter() + presenter.viewController = viewController + + let interactor = EditProfileInteractor() + interactor.presenter = presenter + + viewController.router = router + viewController.interactor = interactor + } + +} From 487d0dad00ee18be21e2784486dc8753dcd1f70a Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Mon, 27 Nov 2023 19:46:44 +0900 Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=92=84=20EditProfile=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EditProfileViewController.swift | 119 +++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift index 093854f..ad2d961 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -12,7 +12,60 @@ protocol EditProfileDisplayLogic: AnyObject { } -final class EditProfileViewController: UIViewController, EditProfileDisplayLogic { +final class EditProfileViewController: BaseViewController, EditProfileDisplayLogic { + + // MARK: - UI Components + + private let profileImageView: UIImageView = { + let imageView: UIImageView = UIImageView() + imageView.image = UIImage.profile + return imageView + }() + + private let editProfileImageButton: LOCircleButton = { + let button = LOCircleButton(style: .photo, diameter: 32) + return button + }() + + private lazy var nicknameTextfield: LOTextField = { + let textField = LOTextField() + textField.placeholder = "닉네임을 입력해주세요." + return textField + }() + + private let nicknameAlertLabel: UILabel = { + let label = UILabel() + label.font = .loFont(type: .caption) + label.textColor = .error + return label + }() + + private lazy var introduceTextfield: LOTextField = { + let textField = LOTextField() + textField.placeholder = "소개를 입력해주세요." + return textField + }() + + private let introduceAlertLabel: UILabel = { + let label = UILabel() + label.font = .loFont(type: .caption) + label.textColor = .error + return label + }() + + private lazy var checkDuplicateNicknameButton: LOButton = { + let button = LOButton(style: .basic) + button.isEnabled = false + button.setTitle("중복확인", for: .normal) + return button + }() + + private lazy var confirmButton: LOButton = { + let button = LOButton(style: .basic) + button.isEnabled = false + button.setTitle("완료", for: .normal) + return button + }() // MARK: - Properties @@ -24,17 +77,79 @@ final class EditProfileViewController: UIViewController, EditProfileDisplayLogic override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + setup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) + setup() } - // MARK: - View Lifecycle override func viewDidLoad() { super.viewDidLoad() } + // MARK: - Methods + + override func setUI() { + self.title = "프로필 수정" + } + + override func setConstraints() { + view.addSubviews(profileImageView, editProfileImageButton, nicknameTextfield, nicknameAlertLabel, introduceTextfield, + introduceAlertLabel, nicknameAlertLabel, checkDuplicateNicknameButton, confirmButton) + view.subviews.forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + } + + NSLayoutConstraint.activate([ + profileImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 26), + profileImageView.widthAnchor.constraint(equalToConstant: 72), + profileImageView.heightAnchor.constraint(equalToConstant: 72), + profileImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + + editProfileImageButton.trailingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 8), + editProfileImageButton.bottomAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 8), + + nicknameTextfield.heightAnchor.constraint(equalToConstant: 44), + nicknameTextfield.topAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 32), + nicknameTextfield.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), + nicknameTextfield.trailingAnchor.constraint(equalTo: checkDuplicateNicknameButton.leadingAnchor, constant: -16), + + nicknameAlertLabel.topAnchor.constraint(equalTo: nicknameTextfield.bottomAnchor, constant: 5), + nicknameAlertLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), + nicknameTextfield.trailingAnchor.constraint(equalTo: nicknameTextfield.trailingAnchor), + + checkDuplicateNicknameButton.heightAnchor.constraint(equalToConstant: 44), + checkDuplicateNicknameButton.widthAnchor.constraint(equalToConstant: 83), + checkDuplicateNicknameButton.centerYAnchor.constraint(equalTo: nicknameTextfield.centerYAnchor), + checkDuplicateNicknameButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), + + introduceTextfield.heightAnchor.constraint(equalToConstant: 44), + introduceTextfield.topAnchor.constraint(equalTo: nicknameAlertLabel.bottomAnchor, constant: 17), + introduceTextfield.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), + introduceTextfield.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), + + introduceAlertLabel.topAnchor.constraint(equalTo: introduceTextfield.bottomAnchor, constant: 5), + introduceAlertLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), + introduceAlertLabel.trailingAnchor.constraint(equalTo: introduceTextfield.trailingAnchor), + + confirmButton.heightAnchor.constraint(equalToConstant: 50), + confirmButton.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor), + confirmButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), + confirmButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16) + ]) + + } + + private func setup() { + EditProfileConfigurator.shared.configure(self) + } + +} + +#Preview { + UINavigationController(rootViewController: EditProfileViewController()) } From d14e84794de977b14ccc771dd08befab278871e1 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Mon, 27 Nov 2023 19:53:38 +0900 Subject: [PATCH 05/10] =?UTF-8?q?=E2=9C=A8=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=8B=9C=20PHPicker=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EditProfileViewController.swift | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift index ad2d961..85e6d94 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -6,6 +6,7 @@ // Copyright © 2023 CodeBomber. All rights reserved. // +import PhotosUI import UIKit protocol EditProfileDisplayLogic: AnyObject { @@ -16,14 +17,26 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog // MARK: - UI Components + private lazy var phPickerViewController: PHPickerViewController = { + var configuration = PHPickerConfiguration() + configuration.filter = .images + configuration.selectionLimit = 1 + let phPickerViewController = PHPickerViewController(configuration: configuration) + phPickerViewController.delegate = self + return phPickerViewController + }() + private let profileImageView: UIImageView = { let imageView: UIImageView = UIImageView() imageView.image = UIImage.profile return imageView }() - private let editProfileImageButton: LOCircleButton = { + private lazy var editProfileImageButton: LOCircleButton = { let button = LOCircleButton(style: .photo, diameter: 32) + button.addAction(UIAction { _ in + self.present(self.phPickerViewController, animated: true) + }, for: .touchUpInside) return button }() @@ -150,6 +163,18 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog } +extension EditProfileViewController: PHPickerViewControllerDelegate { + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + picker.dismiss(animated: true) + let item = results.first?.itemProvider + if let item = item, item.canLoadObject(ofClass: UIImage.self) { + item.loadObject(ofClass: UIImage.self) { [weak self] (image, _) in + // interactor에 전달 + } + } + } +} + #Preview { UINavigationController(rootViewController: EditProfileViewController()) } From a0f62c4c7bb64c37518d43c6fef6ab9b4dd792f5 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Tue, 28 Nov 2023 05:20:09 +0900 Subject: [PATCH 06/10] =?UTF-8?q?=E2=9C=A8=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80,=20=EB=8B=89=EB=84=A4=EC=9E=84,?= =?UTF-8?q?=20=EC=86=8C=EA=B0=9C=20=EB=B3=80=EA=B2=BD=20VIP=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=ED=81=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EditProfile/EditProfileConfigurator.swift | 18 +++-- .../EditProfile/EditProfileInteractor.swift | 63 +++++++++++++-- .../EditProfile/EditProfileModels.swift | 69 ++++++++++++++++ .../EditProfile/EditProfilePresenter.swift | 22 +++++- .../EditProfile/EditProfileRouter.swift | 9 +-- .../EditProfileViewController.swift | 79 ++++++++++++++++++- .../Scenes/Profile/ProfileConfigurator.swift | 16 ++-- .../Scenes/Profile/ProfileInteractor.swift | 22 +++++- .../Scenes/SignUpScene/SignUpInteractor.swift | 14 +--- .../Scenes/SignUpScene/SignUpModels.swift | 18 ----- .../Layover/Workers/MockUserWorker.swift | 27 +++++++ 11 files changed, 291 insertions(+), 66 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift index f695201..41676d7 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift @@ -17,17 +17,19 @@ final class EditProfileConfigurator: Configurator { private init() { } func configure(_ viewController: ViewController) { - let router = EditProfileRouter() - router.viewController = viewController - - let presenter = EditProfilePresenter() - presenter.viewController = viewController - + let viewController = viewController let interactor = EditProfileInteractor() - interactor.presenter = presenter + let presenter = EditProfilePresenter() + let worker = MockUserWorker() + let router = EditProfileRouter() - viewController.router = router + router.viewController = viewController + router.dataStore = interactor viewController.interactor = interactor + viewController.router = router + interactor.presenter = presenter + interactor.userWorker = worker + presenter.viewController = viewController } } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift index fb9e1f4..1187ff9 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift @@ -9,22 +9,75 @@ import UIKit protocol EditProfileBusinessLogic { - + func fetchProfile() + func validateProfileInfo(with request: EditProfileModels.ValidateProfileInfo.Request) + func checkDuplication(with request: EditProfileModels.CheckNicknameDuplication.Request) + func editProfile(with requeset: EditProfileModels.EditProfile.Request) } protocol EditProfileDataStore { - + var nickname: String? { get set } + var profileImageURL: URL? { get set } + var introduce: String? { get set } } -class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStore { +final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStore { // MARK: - Properties typealias Models = EditProfileModels - lazy var worker = EditProfileWorker() + var userWorker: UserWorkerProtocol? var presenter: EditProfilePresentationLogic? - // MARK: - Use Case - Fetch From Local DataStore + var nicknameChecked: Bool = true + var introduceChecked: Bool = true + + // MARK: - Data Store + + var nickname: String? + var profileImageURL: URL? + var introduce: String? + + // MARK: - Use Case + + func fetchProfile() { + presenter?.presentProfile(with: EditProfileModels.FetchProfile.Reponse(nickname: nickname, + introduce: introduce, + profileImage: profileImageURL)) + } + + func validateProfileInfo(with request: EditProfileModels.ValidateProfileInfo.Request) { + let nicknameChanged = nickname != request.nickname + let introduceChanged = introduce != request.introduce + let isChanged = nicknameChanged || request.profileImageChanged || introduceChanged + let isValid = (userWorker?.validateNickname(request.nickname) == .valid) && validateIntroduce(request.introduce ?? "") + let response = EditProfileModels.ValidateProfileInfo.Response(isValid: isChanged && isValid, + nicknameChanged: nicknameChanged) + presenter?.presentProfileInfoValidation(with: response) + } + + func checkDuplication(with request: EditProfileModels.CheckNicknameDuplication.Request) { + Task { + do { + let response = try await userWorker?.checkDuplication(for: request.nickname) + nicknameChecked = response ?? true + await MainActor.run { + presenter?.presentNicknameDuplication(with: EditProfileModels.CheckNicknameDuplication.Response(isDuplicate: response ?? true)) + } + } catch let error { + // TODO: present toast + print(error.localizedDescription) + } + } + } + + func editProfile(with requeset: EditProfileModels.EditProfile.Request) { + + } + + private func validateIntroduce(_ introduce: String) -> Bool { + return introduce.count <= 50 + } } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift index 4482fe0..40322d6 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift @@ -10,4 +10,73 @@ import UIKit enum EditProfileModels { + enum FetchProfile { + struct Request { + + } + + struct Reponse { + var nickname: String? + var introduce: String? + var profileImage: URL? + } + + struct ViewModel { + var nickname: String? + var introduce: String? + var profileImage: URL? + } + } + + enum ValidateProfileInfo { + struct Request { + var nickname: String + var introduce: String? + var profileImageChanged: Bool + } + struct Response { + var isValid: Bool + var nicknameChanged: Bool + } + struct ViewModel { + var nicknameAlertDescription: String? + var introduceAlertDescription: String? + var canCheckDuplication: Bool + var canEditProfile: Bool + } + } + + enum CheckNicknameDuplication { + struct Request { + var nickname: String + } + struct Response { + var isDuplicate: Bool + } + struct ViewModel { + var isValidNickname: Bool + var alertDescription: String { + isValidNickname ? "사용가능한 닉네임입니다." : "사용중인 닉네임입니다." + } + } + } + + enum EditProfile { + struct Request { + var nickname: String + var introduce: String? + var profileImage: UIImage? + } + + struct Response { + var nickname: String + var introduce: String? + var profileImgaeURL: URL? + } + + struct ViewModel { + + } + } + } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift index 30f05a4..e47d568 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift @@ -9,7 +9,9 @@ import UIKit protocol EditProfilePresentationLogic { - + func presentProfile(with response: EditProfileModels.FetchProfile.Reponse) + func presentProfileInfoValidation(with response: EditProfileModels.ValidateProfileInfo.Response) + func presentNicknameDuplication(with response: EditProfileModels.CheckNicknameDuplication.Response) } final class EditProfilePresenter: EditProfilePresentationLogic { @@ -19,4 +21,22 @@ final class EditProfilePresenter: EditProfilePresentationLogic { typealias Models = EditProfileModels weak var viewController: EditProfileDisplayLogic? + func presentProfile(with response: EditProfileModels.FetchProfile.Reponse) { + let viewModel = EditProfileModels.FetchProfile.ViewModel(nickname: response.nickname, + introduce: response.introduce, + profileImage: response.profileImage) + viewController?.displayProfile(viewModel: viewModel) + } + + func presentProfileInfoValidation(with response: EditProfileModels.ValidateProfileInfo.Response) { + let viewModel = EditProfileModels.ValidateProfileInfo.ViewModel(canCheckDuplication: response.nicknameChanged, + canEditProfile: response.isValid) + viewController?.displayProfileInfoValidation(viewModel: viewModel) + } + + func presentNicknameDuplication(with response: EditProfileModels.CheckNicknameDuplication.Response) { + let viewModel = EditProfileModels.CheckNicknameDuplication.ViewModel(isValidNickname: !response.isDuplicate) + viewController?.displayNicknameDuplication(viewModel: viewModel) + } + } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift index 85e493b..1901cfa 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift @@ -9,7 +9,7 @@ import UIKit protocol EditProfileRoutingLogic { - func routeToNext() + } protocol EditProfileDataPassing { @@ -25,11 +25,4 @@ final class EditProfileRouter: NSObject, EditProfileRoutingLogic, EditProfileDat // MARK: - Routing - func routeToNext() { - // let destinationVC = UIStoryboard(name: "", bundle: nil).instantiateViewController(withIdentifier: "") as! NextViewController - // var destinationDS = destinationVC.router!.dataStore! - // passDataTo(destinationDS, from: dataStore!) - // viewController?.navigationController?.pushViewController(destinationVC, animated: true) - } - } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift index 85e6d94..71bb406 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -10,10 +10,12 @@ import PhotosUI import UIKit protocol EditProfileDisplayLogic: AnyObject { - + func displayProfile(viewModel: EditProfileModels.FetchProfile.ViewModel) + func displayProfileInfoValidation(viewModel: EditProfileModels.ValidateProfileInfo.ViewModel) + func displayNicknameDuplication(viewModel: EditProfileModels.CheckNicknameDuplication.ViewModel) } -final class EditProfileViewController: BaseViewController, EditProfileDisplayLogic { +final class EditProfileViewController: BaseViewController { // MARK: - UI Components @@ -29,6 +31,8 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog private let profileImageView: UIImageView = { let imageView: UIImageView = UIImageView() imageView.image = UIImage.profile + imageView.layer.cornerRadius = 36 + imageView.clipsToBounds = true return imageView }() @@ -43,6 +47,7 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog private lazy var nicknameTextfield: LOTextField = { let textField = LOTextField() textField.placeholder = "닉네임을 입력해주세요." + textField.addTarget(self, action: #selector(profileInfoChanged), for: .editingChanged) return textField }() @@ -56,6 +61,7 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog private lazy var introduceTextfield: LOTextField = { let textField = LOTextField() textField.placeholder = "소개를 입력해주세요." + textField.addTarget(self, action: #selector(profileInfoChanged), for: .editingChanged) return textField }() @@ -70,6 +76,7 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog let button = LOButton(style: .basic) button.isEnabled = false button.setTitle("중복확인", for: .normal) + button.addTarget(self, action: #selector(checkDuplicateNicknameButtonDidTap), for: .touchUpInside) return button }() @@ -86,6 +93,9 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog var router: (NSObjectProtocol & EditProfileRoutingLogic & EditProfileDataPassing)? var interactor: EditProfileBusinessLogic? + private var changedProfileImage: UIImage? + private var nicknameIsValid: Bool = true + // MARK: - Object Lifecycle override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { @@ -102,6 +112,7 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog override func viewDidLoad() { super.viewDidLoad() + interactor?.fetchProfile() } // MARK: - Methods @@ -161,6 +172,24 @@ final class EditProfileViewController: BaseViewController, EditProfileDisplayLog EditProfileConfigurator.shared.configure(self) } + @objc private func profileInfoChanged() { + guard let nickname = nicknameTextfield.text, + let introduce = introduceTextfield.text else { return } + let profileImageChanged = changedProfileImage != nil + let profileInfoRequest = EditProfileModels.ValidateProfileInfo.Request(nickname: nickname, + introduce: introduce, + profileImageChanged: profileImageChanged) + interactor?.validateProfileInfo(with: profileInfoRequest) + } + + @objc private func checkDuplicateNicknameButtonDidTap() { + guard let nickname = nicknameTextfield.text else { return } + checkDuplicateNicknameButton.isEnabled = false + + let request = Models.CheckNicknameDuplication.Request(nickname: nickname) + interactor?.checkDuplication(with: request) + } + } extension EditProfileViewController: PHPickerViewControllerDelegate { @@ -169,12 +198,54 @@ extension EditProfileViewController: PHPickerViewControllerDelegate { let item = results.first?.itemProvider if let item = item, item.canLoadObject(ofClass: UIImage.self) { item.loadObject(ofClass: UIImage.self) { [weak self] (image, _) in - // interactor에 전달 + guard let self else { return } + DispatchQueue.main.async { + self.profileImageView.image = image as? UIImage + self.changedProfileImage = image as? UIImage + self.profileInfoChanged() + } } } } } +extension EditProfileViewController: EditProfileDisplayLogic { + + func displayProfile(viewModel: Models.FetchProfile.ViewModel) { + nicknameTextfield.text = viewModel.nickname + + if let url = viewModel.profileImage, let data = try? Data(contentsOf: url) { + profileImageView.image = UIImage(data: data) + } + + if let introduce = viewModel.introduce { + introduceTextfield.text = introduce + } + } + + func displayProfileInfoValidation(viewModel: EditProfileModels.ValidateProfileInfo.ViewModel) { + nicknameAlertLabel.isHidden = viewModel.canCheckDuplication + nicknameAlertLabel.text = viewModel.nicknameAlertDescription + nicknameAlertLabel.textColor = .error + + checkDuplicateNicknameButton.isEnabled = viewModel.canCheckDuplication + + introduceAlertLabel.text = viewModel.introduceAlertDescription + introduceAlertLabel.textColor = .error + + confirmButton.isEnabled = viewModel.canEditProfile && !checkDuplicateNicknameButton.isEnabled + checkDuplicateNicknameButton.isEnabled = viewModel.canCheckDuplication + } + + func displayNicknameDuplication(viewModel: Models.CheckNicknameDuplication.ViewModel) { + nicknameAlertLabel.isHidden = false + nicknameAlertLabel.text = viewModel.alertDescription + nicknameAlertLabel.textColor = viewModel.isValidNickname ? .correct : .error + confirmButton.isEnabled = viewModel.isValidNickname + } + +} + #Preview { - UINavigationController(rootViewController: EditProfileViewController()) + EditProfileViewController() } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift index 0cf9da7..b381013 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift @@ -17,17 +17,17 @@ final class ProfileConfigurator: Configurator { private init() { } func configure(_ viewController: ViewController) { - let router = ProfileRouter() - router.viewController = viewController - - let presenter = ProfilePresenter() - presenter.viewController = viewController - + let viewController = viewController let interactor = ProfileInteractor() - interactor.presenter = presenter + let presenter = ProfilePresenter() + let router = ProfileRouter() - viewController.router = router + router.viewController = viewController + router.dataStore = interactor viewController.interactor = interactor + viewController.router = router + interactor.presenter = presenter + presenter.viewController = viewController } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift index 51ad2c2..e738dc9 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift @@ -9,14 +9,20 @@ import UIKit protocol ProfileBusinessLogic { - + func fetchProfile() } protocol ProfileDataStore { - + var nickname: String? { get set } + var profileImageURL: URL? { get set } + var introduce: String? { get set } } -class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { +final class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { + + var nickname: String? + var profileImageURL: URL? + var introduce: String? // MARK: - Properties @@ -24,4 +30,14 @@ class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { var presenter: ProfilePresentationLogic? + func fetchProfile() { + nickname = "kong" + profileImageURL = URL(string: "https://i.ibb.co/qML8vdN/2023-11-25-9-08-01.png") + introduce = "콩이라고해" + let response = ProfileModels.FetchProfile.Response(nickname: nickname, + introduce: introduce, + profileImageURL: profileImageURL) + presenter?.present(with: response) + } + } diff --git a/iOS/Layover/Layover/Scenes/SignUpScene/SignUpInteractor.swift b/iOS/Layover/Layover/Scenes/SignUpScene/SignUpInteractor.swift index a8bab03..341b3f6 100644 --- a/iOS/Layover/Layover/Scenes/SignUpScene/SignUpInteractor.swift +++ b/iOS/Layover/Layover/Scenes/SignUpScene/SignUpInteractor.swift @@ -35,8 +35,9 @@ final class SignUpInteractor: SignUpBusinessLogic, SignUpDataStore { // MARK: - UseCase: 닉네임 유효성 검사 func validateNickname(with request: SignUpModels.ValidateNickname.Request) { - let response = check(nickname: request.nickname) - presenter?.presentNicknameValidation(with: response) + guard let userWorker else { return } + let response = userWorker.validateNickname(request.nickname) + presenter?.presentNicknameValidation(with: SignUpModels.ValidateNickname.Response(nicknameState: response)) } func checkDuplication(with request: SignUpModels.CheckDuplication.Request) { @@ -53,15 +54,6 @@ final class SignUpInteractor: SignUpBusinessLogic, SignUpDataStore { } } - private func check(nickname: String) -> SignUpModels.ValidateNickname.Response { - if nickname.count < 2 || nickname.count > 8 { - return .init(nicknameState: .lessThan2GreaterThan8) - } else if nickname.wholeMatch(of: /^[a-zA-Z0-9ㄱ-ㅎㅏ-ㅣ가-힣]+/) == nil { - return .init(nicknameState: .invalidCharacter) - } - return .init(nicknameState: .valid) - } - // MARK: - UseCase: SignUp func signUp(with request: SignUpModels.SignUp.Request) { diff --git a/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift b/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift index fbd723a..bbe1bd2 100644 --- a/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift +++ b/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift @@ -23,24 +23,6 @@ enum SignUpModels { var canCheckDuplication: Bool var alertDescription: String? } - - // MARK: - Nickname State Type - enum NicknameState { - case valid - case lessThan2GreaterThan8 - case invalidCharacter - - var alertDescription: String? { - switch self { - case .valid: - return nil - case .lessThan2GreaterThan8: - return "2자 이상 8자 이하로 입력해주세요." - case .invalidCharacter: - return "입력할 수 없는 문자입니다." - } - } - } } // MARK: - Check Nickname Duplication Use Case diff --git a/iOS/Layover/Layover/Workers/MockUserWorker.swift b/iOS/Layover/Layover/Workers/MockUserWorker.swift index 25eace0..e675dbd 100644 --- a/iOS/Layover/Layover/Workers/MockUserWorker.swift +++ b/iOS/Layover/Layover/Workers/MockUserWorker.swift @@ -8,6 +8,23 @@ import Foundation +enum NicknameState { + case valid + case lessThan2GreaterThan8 + case invalidCharacter + + var alertDescription: String? { + switch self { + case .valid: + return nil + case .lessThan2GreaterThan8: + return "2자 이상 8자 이하로 입력해주세요." + case .invalidCharacter: + return "입력할 수 없는 문자입니다." + } + } +} + final class MockUserWorker: UserWorkerProtocol { // MARK: - Properties @@ -18,6 +35,15 @@ final class MockUserWorker: UserWorkerProtocol { // MARK: - Methods + func validateNickname(_ nickname: String) -> NicknameState { + if nickname.count < 2 || nickname.count > 8 { + return .lessThan2GreaterThan8 + } else if nickname.wholeMatch(of: /^[a-zA-Z0-9ㄱ-ㅎㅏ-ㅣ가-힣]+/) == nil { + return .invalidCharacter + } + return .valid + } + func updateNickname(to nickname: String) async throws -> String { guard let fileLocation = Bundle.main.url(forResource: "PatchUserName", withExtension: "json") else { throw NetworkError.unknown } @@ -101,6 +127,7 @@ final class MockUserWorker: UserWorkerProtocol { } protocol UserWorkerProtocol { + func validateNickname(_ nickname: String) -> NicknameState func updateNickname(to nickname: String) async throws -> String func checkDuplication(for nickname: String) async throws -> Bool // TODO: multipart request 구현 From b06cb68fb6194b253fe71b14d3299652c17afc85 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Tue, 28 Nov 2023 05:20:51 +0900 Subject: [PATCH 07/10] =?UTF-8?q?=E2=9C=A8=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EB=B7=B0=EC=97=90=EC=84=9C=20=ED=8E=B8=EC=A7=91=20=EB=B7=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EB=B0=8F=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=8C=A8=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scenes/Profile/ProfileModels.swift | 18 ++++++++++ .../Scenes/Profile/ProfilePresenter.swift | 11 +++++-- .../Scenes/Profile/ProfileRouter.swift | 23 ++++++++----- .../Profile/ProfileViewController.swift | 33 ++++++++++++++----- .../Profile/Views/ProfileHeaderView.swift | 19 +++++++---- 5 files changed, 77 insertions(+), 27 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift index 493bf46..763d246 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift @@ -9,5 +9,23 @@ import UIKit enum ProfileModels { + enum FetchProfile { + struct Request { + } + + struct Response { + var nickname: String? + var introduce: String? + var profileImageURL: URL? + // video data + } + + struct ViewModel { + var nickname: String? + var introduce: String? + var profileImageURL: URL? + // video data + } + } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift b/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift index a2c4a7e..f2d2365 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift @@ -9,14 +9,21 @@ import UIKit protocol ProfilePresentationLogic { - + func present(with response: ProfileModels.FetchProfile.Response) } -class ProfilePresenter: ProfilePresentationLogic { +final class ProfilePresenter: ProfilePresentationLogic { // MARK: - Properties typealias Models = ProfileModels weak var viewController: ProfileDisplayLogic? + func present(with response: ProfileModels.FetchProfile.Response) { + let viewModel = ProfileModels.FetchProfile.ViewModel(nickname: response.nickname, + introduce: response.introduce, + profileImageURL: response.profileImageURL) + viewController?.fetchProfile(viewModel: viewModel) + } + } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift index e60bdf2..0fd44fd 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift @@ -9,7 +9,7 @@ import UIKit protocol ProfileRoutingLogic { - func routeToNext() + func routeToEditProfileViewController() } protocol ProfileDataPassing { @@ -25,16 +25,21 @@ class ProfileRouter: NSObject, ProfileRoutingLogic, ProfileDataPassing { // MARK: - Routing - func routeToNext() { - // let destinationVC = UIStoryboard(name: "", bundle: nil).instantiateViewController(withIdentifier: "") as! NextViewController - // var destinationDS = destinationVC.router!.dataStore! - // passDataTo(destinationDS, from: dataStore!) - // viewController?.navigationController?.pushViewController(destinationVC, animated: true) + func routeToEditProfileViewController() { + let editProfileViewController = EditProfileViewController() + guard let source = dataStore, + var destination = editProfileViewController.router?.dataStore + else { return } + + passProfileDataToEditProfile(source: source, destination: &destination) + viewController?.navigationController?.pushViewController(editProfileViewController, animated: true) } // MARK: - Data Passing - // func passDataTo(_ destinationDS: inout NextDataStore, from sourceDS: ProfileDataStore) { - // destinationDS.attribute = sourceDS.attribute - // } + private func passProfileDataToEditProfile(source: ProfileDataStore, destination: inout EditProfileDataStore) { + destination.nickname = source.nickname + destination.introduce = source.introduce + destination.profileImageURL = source.profileImageURL + } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index 8de83a8..15557df 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -9,10 +9,10 @@ import UIKit protocol ProfileDisplayLogic: AnyObject { - + func fetchProfile(viewModel: ProfileModels.FetchProfile.ViewModel) } -final class ProfileViewController: BaseViewController, ProfileDisplayLogic { +final class ProfileViewController: BaseViewController { // MARK: - Type @@ -67,6 +67,7 @@ final class ProfileViewController: BaseViewController, ProfileDisplayLogic { super.viewDidLoad() setNavigationBar() setVideoCollectionView() + interactor?.fetchProfile() } override func setConstraints() { @@ -108,7 +109,7 @@ final class ProfileViewController: BaseViewController, ProfileDisplayLogic { let barButtonItem: UIBarButtonItem = UIBarButtonItem(title: nil, image: UIImage(resource: .setting), target: self, - action: #selector(didTapSettingButton)) + action: #selector(settingButtonDidTap)) barButtonItem.tintColor = .layoverWhite self.navigationItem.rightBarButtonItem = barButtonItem case .other: @@ -122,20 +123,34 @@ final class ProfileViewController: BaseViewController, ProfileDisplayLogic { snapshot.appendSections([UUID()]) snapshot.appendItems([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) videoDatasource.apply(snapshot) + } + + @objc private func editbuttonDidTap() { + router?.routeToEditProfileViewController() + } + + @objc private func settingButtonDidTap() { + + } + +} + +extension ProfileViewController: ProfileDisplayLogic { + + func fetchProfile(viewModel: ProfileModels.FetchProfile.ViewModel) { videoDatasource.supplementaryViewProvider = { [weak self] (collectionView, kind, indexPath) in guard kind == UICollectionView.elementKindSectionHeader else { return UICollectionReusableView() } let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ProfileHeaderView.identifier, for: indexPath) as? ProfileHeaderView - header?.editButton.isHidden = self?.profileType == .other + header?.editButton.addTarget(self, action: #selector(self?.editbuttonDidTap), for: .touchUpInside) + header?.configure(profileImageURL: viewModel.profileImageURL, + nickname: viewModel.nickname, + introduce: viewModel.introduce) return header } } - @objc private func didTapSettingButton() { - - } - } #Preview { - ProfileViewController(profileType: .own) + UINavigationController(rootViewController: ProfileViewController(profileType: .own)) } diff --git a/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift b/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift index 8d35443..182ee33 100644 --- a/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift +++ b/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift @@ -26,7 +26,7 @@ final class ProfileHeaderView: UICollectionReusableView { return label }() - private let descriptionLabel: UILabel = { + private let introduceLabel: UILabel = { let label: UILabel = UILabel() label.text = "안녕하세요 로인서입니다" label.textColor = .layoverWhite @@ -54,9 +54,14 @@ final class ProfileHeaderView: UICollectionReusableView { // MARK: - Methods + func configure(profileImageURL: URL?, nickname: String?, introduce: String?) { + nicknameLabel.text = nickname + introduceLabel.text = introduce + } + private func setConstraints() { - addSubviews(profileImageView, nicknameLabel, editButton, descriptionLabel) - [profileImageView, nicknameLabel, editButton, descriptionLabel].forEach { + addSubviews(profileImageView, nicknameLabel, editButton, introduceLabel) + subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -75,10 +80,10 @@ final class ProfileHeaderView: UICollectionReusableView { editButton.heightAnchor.constraint(equalToConstant: 34), editButton.widthAnchor.constraint(equalToConstant: 58), - descriptionLabel.topAnchor.constraint(equalTo: nicknameLabel.bottomAnchor, constant: 18), - descriptionLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), - descriptionLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), - descriptionLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -21) + introduceLabel.topAnchor.constraint(equalTo: nicknameLabel.bottomAnchor, constant: 18), + introduceLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), + introduceLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), + introduceLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -21) ]) } From 0272c8eaf723526181ece5807d71165c514a4a76 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Tue, 28 Nov 2023 05:41:06 +0900 Subject: [PATCH 08/10] =?UTF-8?q?=F0=9F=94=A7=20validateProfileInfo=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=82=B4=EB=B6=80=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EB=A6=AC=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/EditProfile/EditProfileInteractor.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift index 1187ff9..3ca60c7 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift @@ -50,9 +50,9 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor func validateProfileInfo(with request: EditProfileModels.ValidateProfileInfo.Request) { let nicknameChanged = nickname != request.nickname let introduceChanged = introduce != request.introduce - let isChanged = nicknameChanged || request.profileImageChanged || introduceChanged - let isValid = (userWorker?.validateNickname(request.nickname) == .valid) && validateIntroduce(request.introduce ?? "") - let response = EditProfileModels.ValidateProfileInfo.Response(isValid: isChanged && isValid, + let profileInfoChanged = nicknameChanged || request.profileImageChanged || introduceChanged + let profileInfoValiation = (userWorker?.validateNickname(request.nickname) == .valid) && validateIntroduce(request.introduce ?? "") + let response = EditProfileModels.ValidateProfileInfo.Response(isValid: profileInfoChanged && profileInfoValiation, nicknameChanged: nicknameChanged) presenter?.presentProfileInfoValidation(with: response) } From 56b1120618a3b6a232a79e10b780269ebeab8631 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Tue, 28 Nov 2023 18:48:41 +0900 Subject: [PATCH 09/10] =?UTF-8?q?=F0=9F=94=A7=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81,=20=EC=98=A4=ED=83=88?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EditProfile/EditProfileInteractor.swift | 18 +++++++----------- .../Scenes/EditProfile/EditProfileModels.swift | 4 ++-- .../EditProfile/EditProfilePresenter.swift | 12 ++++++------ .../EditProfileViewController.swift | 10 ++++++---- .../Scenes/Profile/ProfileInteractor.swift | 18 +++++++++++++----- .../Layover/Scenes/Profile/ProfileModels.swift | 5 +++-- .../Scenes/Profile/ProfilePresenter.swift | 9 ++++----- .../Layover/Scenes/Profile/ProfileRouter.swift | 13 +++++-------- .../Scenes/Profile/ProfileViewController.swift | 18 ++++++++---------- .../Profile/Views/ProfileHeaderView.swift | 7 ++++++- .../Scenes/SignUpScene/SignUpModels.swift | 2 +- 11 files changed, 61 insertions(+), 55 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift index 3ca60c7..5849b9a 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift @@ -17,8 +17,8 @@ protocol EditProfileBusinessLogic { protocol EditProfileDataStore { var nickname: String? { get set } - var profileImageURL: URL? { get set } var introduce: String? { get set } + var profileImage: UIImage? { get set } } final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStore { @@ -30,24 +30,21 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor var userWorker: UserWorkerProtocol? var presenter: EditProfilePresentationLogic? - var nicknameChecked: Bool = true - var introduceChecked: Bool = true - // MARK: - Data Store var nickname: String? - var profileImageURL: URL? var introduce: String? + var profileImage: UIImage? // MARK: - Use Case func fetchProfile() { presenter?.presentProfile(with: EditProfileModels.FetchProfile.Reponse(nickname: nickname, introduce: introduce, - profileImage: profileImageURL)) + profileImage: profileImage)) } - func validateProfileInfo(with request: EditProfileModels.ValidateProfileInfo.Request) { + func validateProfileInfo(with request: Models.ValidateProfileInfo.Request) { let nicknameChanged = nickname != request.nickname let introduceChanged = introduce != request.introduce let profileInfoChanged = nicknameChanged || request.profileImageChanged || introduceChanged @@ -57,13 +54,12 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor presenter?.presentProfileInfoValidation(with: response) } - func checkDuplication(with request: EditProfileModels.CheckNicknameDuplication.Request) { + func checkDuplication(with request: Models.CheckNicknameDuplication.Request) { Task { do { let response = try await userWorker?.checkDuplication(for: request.nickname) - nicknameChecked = response ?? true await MainActor.run { - presenter?.presentNicknameDuplication(with: EditProfileModels.CheckNicknameDuplication.Response(isDuplicate: response ?? true)) + presenter?.presentNicknameDuplication(with: Models.CheckNicknameDuplication.Response(isDuplicate: response ?? true)) } } catch let error { // TODO: present toast @@ -72,7 +68,7 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor } } - func editProfile(with requeset: EditProfileModels.EditProfile.Request) { + func editProfile(with requeset: Models.EditProfile.Request) { } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift index 40322d6..4ca836c 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift @@ -18,13 +18,13 @@ enum EditProfileModels { struct Reponse { var nickname: String? var introduce: String? - var profileImage: URL? + var profileImage: UIImage? } struct ViewModel { var nickname: String? var introduce: String? - var profileImage: URL? + var profileImage: UIImage? } } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift index e47d568..4b2c11f 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift @@ -22,20 +22,20 @@ final class EditProfilePresenter: EditProfilePresentationLogic { weak var viewController: EditProfileDisplayLogic? func presentProfile(with response: EditProfileModels.FetchProfile.Reponse) { - let viewModel = EditProfileModels.FetchProfile.ViewModel(nickname: response.nickname, - introduce: response.introduce, - profileImage: response.profileImage) + let viewModel = Models.FetchProfile.ViewModel(nickname: response.nickname, + introduce: response.introduce, + profileImage: response.profileImage) viewController?.displayProfile(viewModel: viewModel) } func presentProfileInfoValidation(with response: EditProfileModels.ValidateProfileInfo.Response) { - let viewModel = EditProfileModels.ValidateProfileInfo.ViewModel(canCheckDuplication: response.nicknameChanged, - canEditProfile: response.isValid) + let viewModel = Models.ValidateProfileInfo.ViewModel(canCheckDuplication: response.nicknameChanged, + canEditProfile: response.isValid) viewController?.displayProfileInfoValidation(viewModel: viewModel) } func presentNicknameDuplication(with response: EditProfileModels.CheckNicknameDuplication.Response) { - let viewModel = EditProfileModels.CheckNicknameDuplication.ViewModel(isValidNickname: !response.isDuplicate) + let viewModel = Models.CheckNicknameDuplication.ViewModel(isValidNickname: !response.isDuplicate) viewController?.displayNicknameDuplication(viewModel: viewModel) } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift index 71bb406..21d870e 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -214,13 +214,15 @@ extension EditProfileViewController: EditProfileDisplayLogic { func displayProfile(viewModel: Models.FetchProfile.ViewModel) { nicknameTextfield.text = viewModel.nickname - if let url = viewModel.profileImage, let data = try? Data(contentsOf: url) { - profileImageView.image = UIImage(data: data) - } - if let introduce = viewModel.introduce { introduceTextfield.text = introduce } + + if let image = viewModel.profileImage { + profileImageView.image = image + } else { + profileImageView.image = UIImage.profile + } } func displayProfileInfoValidation(viewModel: EditProfileModels.ValidateProfileInfo.ViewModel) { diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift index e738dc9..7f3c0a0 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift @@ -14,15 +14,21 @@ protocol ProfileBusinessLogic { protocol ProfileDataStore { var nickname: String? { get set } - var profileImageURL: URL? { get set } var introduce: String? { get set } + var profileImage: UIImage? { get set } } final class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { var nickname: String? - var profileImageURL: URL? var introduce: String? + var profileImage: UIImage? + + private let provider: ProviderType + + init(provider: ProviderType = Provider()) { + self.provider = provider + } // MARK: - Properties @@ -31,12 +37,14 @@ final class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { var presenter: ProfilePresentationLogic? func fetchProfile() { + if let data = try? Data(contentsOf: URL(string: "https://i.ibb.co/qML8vdN/2023-11-25-9-08-01.png")!) { + profileImage = UIImage(data: data) + } nickname = "kong" - profileImageURL = URL(string: "https://i.ibb.co/qML8vdN/2023-11-25-9-08-01.png") introduce = "콩이라고해" - let response = ProfileModels.FetchProfile.Response(nickname: nickname, + let response = ProfileModels.FetchProfile.Response(nickname: nickname, introduce: introduce, - profileImageURL: profileImageURL) + profileImage: profileImage) presenter?.present(with: response) } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift index 763d246..7da5989 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift @@ -9,6 +9,7 @@ import UIKit enum ProfileModels { + enum FetchProfile { struct Request { @@ -17,14 +18,14 @@ enum ProfileModels { struct Response { var nickname: String? var introduce: String? - var profileImageURL: URL? + var profileImage: UIImage? // video data } struct ViewModel { var nickname: String? var introduce: String? - var profileImageURL: URL? + var profileImage: UIImage? // video data } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift b/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift index f2d2365..405ae6d 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift @@ -19,11 +19,10 @@ final class ProfilePresenter: ProfilePresentationLogic { typealias Models = ProfileModels weak var viewController: ProfileDisplayLogic? - func present(with response: ProfileModels.FetchProfile.Response) { - let viewModel = ProfileModels.FetchProfile.ViewModel(nickname: response.nickname, - introduce: response.introduce, - profileImageURL: response.profileImageURL) - viewController?.fetchProfile(viewModel: viewModel) + func present(with response: Models.FetchProfile.Response) { + viewController?.fetchProfile(viewModel: Models.FetchProfile.ViewModel(nickname: response.nickname, + introduce: response.introduce, + profileImage: response.profileImage)) } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift index 0fd44fd..2757c59 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift @@ -31,15 +31,12 @@ class ProfileRouter: NSObject, ProfileRoutingLogic, ProfileDataPassing { var destination = editProfileViewController.router?.dataStore else { return } - passProfileDataToEditProfile(source: source, destination: &destination) - viewController?.navigationController?.pushViewController(editProfileViewController, animated: true) - } - - // MARK: - Data Passing - - private func passProfileDataToEditProfile(source: ProfileDataStore, destination: inout EditProfileDataStore) { + // Data Passing destination.nickname = source.nickname destination.introduce = source.introduce - destination.profileImageURL = source.profileImageURL + destination.profileImage = source.profileImage + + viewController?.navigationController?.pushViewController(editProfileViewController, animated: true) } + } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index 15557df..12d4507 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -66,7 +66,7 @@ final class ProfileViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() setNavigationBar() - setVideoCollectionView() + setThumnailCollectionView() interactor?.fetchProfile() } @@ -117,7 +117,7 @@ final class ProfileViewController: BaseViewController { } } - private func setVideoCollectionView() { + private func setThumnailCollectionView() { thumbnailCollectionView.dataSource = videoDatasource var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([UUID()]) @@ -140,17 +140,15 @@ extension ProfileViewController: ProfileDisplayLogic { func fetchProfile(viewModel: ProfileModels.FetchProfile.ViewModel) { videoDatasource.supplementaryViewProvider = { [weak self] (collectionView, kind, indexPath) in guard kind == UICollectionView.elementKindSectionHeader else { return UICollectionReusableView() } - let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ProfileHeaderView.identifier, for: indexPath) as? ProfileHeaderView + let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, + withReuseIdentifier: ProfileHeaderView.identifier, + for: indexPath) as? ProfileHeaderView header?.editButton.addTarget(self, action: #selector(self?.editbuttonDidTap), for: .touchUpInside) - header?.configure(profileImageURL: viewModel.profileImageURL, - nickname: viewModel.nickname, - introduce: viewModel.introduce) + header?.configure(profileImage: viewModel.profileImage, + nickname: viewModel.nickname, + introduce: viewModel.introduce) return header } } } - -#Preview { - UINavigationController(rootViewController: ProfileViewController(profileType: .own)) -} diff --git a/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift b/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift index 182ee33..7269fe2 100644 --- a/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift +++ b/iOS/Layover/Layover/Scenes/Profile/Views/ProfileHeaderView.swift @@ -15,6 +15,8 @@ final class ProfileHeaderView: UICollectionReusableView { private let profileImageView: UIImageView = { let imageView: UIImageView = UIImageView() imageView.image = UIImage.profile + imageView.layer.cornerRadius = 36 + imageView.clipsToBounds = true return imageView }() @@ -54,7 +56,10 @@ final class ProfileHeaderView: UICollectionReusableView { // MARK: - Methods - func configure(profileImageURL: URL?, nickname: String?, introduce: String?) { + func configure(profileImage: UIImage?, + nickname: String?, + introduce: String?) { + profileImageView.image = profileImage nicknameLabel.text = nickname introduceLabel.text = introduce } diff --git a/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift b/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift index bbe1bd2..850dc8a 100644 --- a/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift +++ b/iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift @@ -36,7 +36,7 @@ enum SignUpModels { struct ViewModel { var canSignUp: Bool var alertDescription: String { - canSignUp ? "사용가능한 닉네임입니다." : "사용중인 닉네임입니다." + canSignUp ? "사용 가능한 닉네임입니다." : "사용 중인 닉네임입니다." } } } From 1668888f6516bb98cfcd5e728e5387e7432ea8b1 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Tue, 28 Nov 2023 19:04:20 +0900 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=94=A7=20confirmButton.isEnabled=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/EditProfile/EditProfileViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift index 21d870e..f1cb3d6 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -235,7 +235,7 @@ extension EditProfileViewController: EditProfileDisplayLogic { introduceAlertLabel.text = viewModel.introduceAlertDescription introduceAlertLabel.textColor = .error - confirmButton.isEnabled = viewModel.canEditProfile && !checkDuplicateNicknameButton.isEnabled + confirmButton.isEnabled = viewModel.canEditProfile && !viewModel.canCheckDuplication checkDuplicateNicknameButton.isEnabled = viewModel.canCheckDuplication }