Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: 프로필 편집 레이아웃 vip 사이클 구현 #128

Merged
merged 10 commits into from
Nov 28, 2023
54 changes: 49 additions & 5 deletions iOS/Layover/Layover.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@
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 */; };
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 */; };
Expand Down Expand Up @@ -181,6 +188,13 @@
FC49758A2B03432800D8627F /* Pretendard-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Bold.ttf"; sourceTree = "<group>"; };
FC49758B2B03432800D8627F /* Pretendard-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Regular.ttf"; sourceTree = "<group>"; };
FC4975982B03439000D8627F /* UIFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = "<group>"; };
FC5BE1162B148D160036366D /* EditProfilePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfilePresenter.swift; sourceTree = "<group>"; };
FC5BE1172B148D160036366D /* EditProfileWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileWorker.swift; sourceTree = "<group>"; };
FC5BE1182B148D160036366D /* EditProfileRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileRouter.swift; sourceTree = "<group>"; };
FC5BE1192B148D160036366D /* EditProfileModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileModels.swift; sourceTree = "<group>"; };
FC5BE11A2B148D160036366D /* EditProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileViewController.swift; sourceTree = "<group>"; };
FC5BE11B2B148D160036366D /* EditProfileInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileInteractor.swift; sourceTree = "<group>"; };
FC5BE1222B1490660036366D /* EditProfileConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileConfigurator.swift; sourceTree = "<group>"; };
FC68E29A2B02325D001AABFF /* Requestable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Requestable.swift; sourceTree = "<group>"; };
FC68E29C2B02326A001AABFF /* Responsable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Responsable.swift; sourceTree = "<group>"; };
FC68E29E2B023315001AABFF /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -389,7 +403,7 @@
path = Cell;
sourceTree = "<group>";
};
FC2511A72B04DA9C004717BC /* Map */ = {
FC2511A72B04DA9C004717BC /* Map */ = {
isa = PBXGroup;
children = (
FC767FA62B1269980088CF9B /* Views */,
Expand All @@ -414,6 +428,29 @@
path = Fonts;
sourceTree = "<group>";
};
FC5BE1112B148AF00036366D /* Views */ = {
isa = PBXGroup;
children = (
FC930E7F2B0CFB0B00AA48E3 /* ProfileHeaderView.swift */,
FC930E7D2B0CDB2900AA48E3 /* ThumbnailCollectionViewCell.swift */,
);
path = Views;
sourceTree = "<group>";
};
FC5BE1152B148B540036366D /* EditProfile */ = {
isa = PBXGroup;
children = (
FC5BE11A2B148D160036366D /* EditProfileViewController.swift */,
FC5BE11B2B148D160036366D /* EditProfileInteractor.swift */,
FC5BE1162B148D160036366D /* EditProfilePresenter.swift */,
FC5BE1192B148D160036366D /* EditProfileModels.swift */,
FC5BE1182B148D160036366D /* EditProfileRouter.swift */,
FC5BE1172B148D160036366D /* EditProfileWorker.swift */,
FC5BE1222B1490660036366D /* EditProfileConfigurator.swift */,
);
path = EditProfile;
sourceTree = "<group>";
};
FC68E2992B02320F001AABFF /* Network */ = {
isa = PBXGroup;
children = (
Expand All @@ -437,8 +474,8 @@
FC767F9A2B12283D0088CF9B /* PatchUserName.json */,
);
path = MockData;
sourceTree = "<group>";
};
sourceTree = "<group>";
};
FC767FA62B1269980088CF9B /* Views */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -524,6 +561,7 @@
194551EB2B037F1E00299768 /* Login */,
1945521A2B0478A100299768 /* Home */,
FC930E6E2B0CD73B00AA48E3 /* Profile */,
FC5BE1152B148B540036366D /* EditProfile */,
194552272B0479B600299768 /* BaseViewController.swift */,
);
path = Scenes;
Expand Down Expand Up @@ -567,14 +605,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 = "<group>";
Expand Down Expand Up @@ -729,19 +766,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 */,
Expand All @@ -752,8 +793,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 */,
Expand All @@ -773,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 */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// 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 viewController = viewController
let interactor = EditProfileInteractor()
let presenter = EditProfilePresenter()
let worker = MockUserWorker()
let router = EditProfileRouter()

router.viewController = viewController
router.dataStore = interactor
viewController.interactor = interactor
viewController.router = router
interactor.presenter = presenter
interactor.userWorker = worker
presenter.viewController = viewController
}

}
79 changes: 79 additions & 0 deletions iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// EditProfileInteractor.swift
// Layover
//
// Created by kong on 2023/11/27.
// Copyright © 2023 CodeBomber. All rights reserved.
//

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 introduce: String? { get set }
var profileImage: UIImage? { get set }
}

final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStore {

// MARK: - Properties

typealias Models = EditProfileModels
anyukyung marked this conversation as resolved.
Show resolved Hide resolved

var userWorker: UserWorkerProtocol?
var presenter: EditProfilePresentationLogic?

// MARK: - Data Store

var nickname: String?
var introduce: String?
var profileImage: UIImage?

// MARK: - Use Case

func fetchProfile() {
presenter?.presentProfile(with: EditProfileModels.FetchProfile.Reponse(nickname: nickname,
introduce: introduce,
profileImage: profileImage))
}

func validateProfileInfo(with request: Models.ValidateProfileInfo.Request) {
let nicknameChanged = nickname != request.nickname
let introduceChanged = introduce != request.introduce
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)
}

func checkDuplication(with request: Models.CheckNicknameDuplication.Request) {
Task {
do {
let response = try await userWorker?.checkDuplication(for: request.nickname)
await MainActor.run {
presenter?.presentNicknameDuplication(with: Models.CheckNicknameDuplication.Response(isDuplicate: response ?? true))
}
} catch let error {
// TODO: present toast
print(error.localizedDescription)
}
}
}

func editProfile(with requeset: Models.EditProfile.Request) {

}

private func validateIntroduce(_ introduce: String) -> Bool {
return introduce.count <= 50
}

}
82 changes: 82 additions & 0 deletions iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// EditProfileModels.swift
// Layover
//
// Created by kong on 2023/11/27.
// Copyright © 2023 CodeBomber. All rights reserved.
//

import UIKit

enum EditProfileModels {

enum FetchProfile {
struct Request {

}

struct Reponse {
var nickname: String?
var introduce: String?
var profileImage: UIImage?
}

struct ViewModel {
var nickname: String?
var introduce: String?
var profileImage: UIImage?
}
}

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 ? "사용가능한 닉네임입니다." : "사용중인 닉네임입니다."
anyukyung marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

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 {

}
}

}
42 changes: 42 additions & 0 deletions iOS/Layover/Layover/Scenes/EditProfile/EditProfilePresenter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// EditProfilePresenter.swift
// Layover
//
// Created by kong on 2023/11/27.
// Copyright © 2023 CodeBomber. All rights reserved.
//

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 {

// MARK: - Properties

typealias Models = EditProfileModels
weak var viewController: EditProfileDisplayLogic?

func presentProfile(with response: EditProfileModels.FetchProfile.Reponse) {
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 = Models.ValidateProfileInfo.ViewModel(canCheckDuplication: response.nicknameChanged,
canEditProfile: response.isValid)
viewController?.displayProfileInfoValidation(viewModel: viewModel)
}

func presentNicknameDuplication(with response: EditProfileModels.CheckNicknameDuplication.Response) {
let viewModel = Models.CheckNicknameDuplication.ViewModel(isValidNickname: !response.isDuplicate)
viewController?.displayNicknameDuplication(viewModel: viewModel)
}

}
Loading