Skip to content

Commit 0a5ac99

Browse files
authored
[NL-69] : 프로필 수정 API 연결 (#45)
* [NL-69] : profile 수정 * [NL-69] : 정보 수정 API 연결 * [NL-69] : PopUpComponent 생성 * [NL-69] : Popup 적용 * [NL-69] : 파일 이름 수정
1 parent 405b4c8 commit 0a5ac99

File tree

17 files changed

+720
-57
lines changed

17 files changed

+720
-57
lines changed

App/Sources/AppDependencyHandler.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct AppDependencyHandler: DependencyRegistrable {
2727
// TODO: 이미 TabBarController 가 있는 경우에 대한 예외 처리
2828
let tabBarController = BaseTabBarController()
2929
tabBarController.viewControllers = [
30+
3031
BaseNavigationController(rootViewController: HomeViewController(viewModel: HomeViewModel())),
3132
BaseNavigationController(
3233
rootViewController: FortuneViewController(viewModel: FortuneViewModel())),

Common/Auth/Sources/UserDataManager.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public final class UserDataManager {
2020

2121
@discardableResult
2222
public func fetch() async throws -> UserDTO {
23+
2324
let target = AuthTarget.GetUser(userID: userID)
2425
do {
2526
let user = try await networkProvider.request(target: target)

Common/Base/Sources/Model/UserDTO.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ public struct UserDTO: Decodable {
2020
public let birthDate: String?
2121
public let birthTime: [String]?
2222
public let gender: GenderDTO
23+
24+
public init(id: String, name: String, birthDate: String?, birthTime: [String]?, gender: GenderDTO) {
25+
self.id = id
26+
self.name = name
27+
self.birthDate = birthDate
28+
self.birthTime = birthTime
29+
self.gender = gender
30+
}
2331

2432
// TODO: 사주 정보?
2533
}

Common/Lib/Sources/Router/SettingRoute.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ public enum SettingRoute {
1010
case pushSetting
1111
case editProfile
1212
case timePicker
13+
case back
14+
case pop
1315
}

DesignSystem/DesignSystem/Sources/GenderSelectionView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ public final class GenderSelectionView: UIView {
8686
}
8787

8888
public func setInitialSelection(gender: String) {
89-
if gender == "남성", let index = radioButtons.firstIndex(where: { $0.title == "남성" }) {
89+
if gender == "M", let index = radioButtons.firstIndex(where: { $0.title == "남성" }) {
9090
selectRadioButton(at: index)
91-
} else if gender == "여성", let index = radioButtons.firstIndex(where: { $0.title == "여성" }) {
91+
} else if gender == "F", let index = radioButtons.firstIndex(where: { $0.title == "여성" }) {
9292
selectRadioButton(at: index)
9393
}
9494
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//
2+
// PopUp.swift
3+
// DesignSystemLayer
4+
//
5+
// Created by 최재혁 on 8/18/25.
6+
//
7+
8+
import SnapKit
9+
import Then
10+
import UIKit
11+
12+
public final class PopUp : UIView {
13+
14+
public enum Style : CaseIterable {
15+
case one
16+
case two
17+
18+
fileprivate var isHidden: Bool {
19+
switch self {
20+
case .one:
21+
return true
22+
case .two:
23+
return false
24+
}
25+
}
26+
}
27+
private lazy var contentStackView = UIStackView().then {
28+
$0.axis = .vertical
29+
$0.alignment = .center
30+
}
31+
32+
private(set) lazy var deleteButton = UIButton().then {
33+
$0.setImage(STImages.xMark.image, for: .normal)
34+
}
35+
36+
private lazy var titleLabel = UILabel().then {
37+
$0.style = Typography.Heading_20_B
38+
$0.textColor = STColors.gray1.color
39+
$0.textAlignment = .center
40+
}
41+
42+
private lazy var descriptionLabel = UILabel().then {
43+
$0.style = Typography.Body_16_M
44+
$0.textColor = STColors.gray4.color
45+
$0.textAlignment = .center
46+
$0.numberOfLines = 2
47+
}
48+
49+
private lazy var buttonStackView = UIStackView().then {
50+
$0.axis = .horizontal
51+
$0.spacing = 8
52+
$0.alignment = .center
53+
$0.distribution = .fillEqually
54+
}
55+
56+
public private(set) lazy var outButton = UIButton().then {
57+
$0.backgroundColor = STColors.gray7.color
58+
$0.layer.cornerRadius = 8
59+
}
60+
61+
public private(set) lazy var actionButton = UIButton().then {
62+
$0.backgroundColor = STColors.primary2.color
63+
$0.layer.cornerRadius = 8
64+
}
65+
66+
public init(style : Style? = nil) {
67+
super.init(frame: .zero)
68+
setupUI()
69+
}
70+
71+
required init?(coder: NSCoder) {
72+
fatalError("init(coder:) has not been implemented")
73+
}
74+
75+
private func setupUI() {
76+
layer.cornerRadius = 12
77+
layer.masksToBounds = true
78+
backgroundColor = STColors.white.color
79+
80+
addSubview(contentStackView)
81+
addSubview(deleteButton)
82+
contentStackView.addArrangedSubview(titleLabel)
83+
contentStackView.addArrangedSubview(descriptionLabel)
84+
contentStackView.addArrangedSubview(buttonStackView)
85+
buttonStackView.addArrangedSubview(outButton)
86+
buttonStackView.addArrangedSubview(actionButton)
87+
88+
contentStackView.snp.makeConstraints { make in
89+
make.edges.equalToSuperview().inset(24)
90+
}
91+
92+
contentStackView.setCustomSpacing(8, after: titleLabel)
93+
contentStackView.setCustomSpacing(24, after: descriptionLabel)
94+
95+
deleteButton.snp.makeConstraints { make in
96+
make.top.equalToSuperview().inset(24)
97+
make.trailing.equalToSuperview().inset(24)
98+
make.width.height.equalTo(16)
99+
}
100+
101+
buttonStackView.snp.makeConstraints { make in
102+
make.leading.trailing.equalToSuperview()
103+
}
104+
105+
outButton.snp.makeConstraints { make in
106+
make.height.equalTo(48)
107+
}
108+
109+
actionButton.snp.makeConstraints { make in
110+
make.height.equalTo(48)
111+
}
112+
}
113+
114+
public func update(titile : String, description: String? = nil, actionButtonTitle: String, outButtonTitle: String = "취소") {
115+
titleLabel.styledText = titile
116+
descriptionLabel.styledText = description
117+
let style = Typography.Body_16_B
118+
style.color = STColors.white.color
119+
actionButton.setAttributedTitle(actionButtonTitle.set(style: style), for: .normal)
120+
style.color = STColors.gray1.color
121+
outButton.setAttributedTitle(outButtonTitle.set(style: style), for: .normal)
122+
}
123+
124+
public func update(style : Style) {
125+
outButton.isHidden = style.isHidden
126+
}
127+
}
128+
129+
@available(iOS 17.0, *)
130+
#Preview {
131+
let stackView = UIStackView().then {
132+
$0.axis = .vertical
133+
$0.spacing = 16
134+
$0.alignment = .center
135+
$0.backgroundColor = .systemBackground
136+
}
137+
138+
let popup = PopUp().then {
139+
$0.update(titile: "제목", description: "설명 텍스트입니다.", actionButtonTitle: "확인")
140+
$0.update(style: .one)
141+
}
142+
143+
let popupTwo = PopUp().then {
144+
$0.update(titile: "제목", description: "설명 \n텍스트입니다.", actionButtonTitle: "확인", outButtonTitle: "취소")
145+
$0.update(style: .two)
146+
}
147+
148+
stackView.addArrangedSubview(popup)
149+
stackView.addArrangedSubview(popupTwo)
150+
151+
popup.snp.makeConstraints { make in
152+
make.width.equalTo(300)
153+
make.height.equalTo(200)
154+
}
155+
156+
popupTwo.snp.makeConstraints { make in
157+
make.width.equalTo(300)
158+
make.height.equalTo(200)
159+
}
160+
161+
return stackView
162+
}

Feature/Onboarding/Sources/Onboarding/OnboardingViewModel.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ public class OnboardingViewModel {
8282
do {
8383
let test = try await self.userDataManager.create(
8484
name: name, birthDate: birthDate, birthTime: birthTime, gender: genderDTO)
85-
print(test)
8685
} catch {
8786
// TODO: API 호출 에러처리
8887
print(error)

Feature/Setting/Sources/EditPage/EditProfileViewController.swift

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ import Combine
1010
import DesignSystem
1111
import Foundation
1212
import UIKit
13+
import DIInjector
1314

1415
final class EditProfileViewController: BaseViewController {
1516

1617
private var store: Set<AnyCancellable> = []
17-
private let router: SettingRouter
1818
private let viewModel: EditProfileViewModel
19+
@Injected private var router : SettingRouter
1920

2021
private lazy var scrollView: UIScrollView = UIScrollView().then {
2122
$0.showsHorizontalScrollIndicator = false
@@ -30,6 +31,16 @@ final class EditProfileViewController: BaseViewController {
3031
$0.spacing = 28
3132
$0.alignment = .leading
3233
}
34+
35+
private lazy var backgourndView = UIView().then {
36+
$0.isHidden = true
37+
$0.backgroundColor = STColors.black.color.withAlphaComponent(0.5)
38+
}
39+
40+
private lazy var popup = PopUp().then {
41+
$0.isHidden = true
42+
$0.update(titile: "수정 중인 내용이 있소", description: "저장하지 않고 화면을 벗어나면\n감쪽같이 사라질 것이오", actionButtonTitle: "계속 수정하기", outButtonTitle: "나가기")
43+
}
3344

3445
private lazy var nameStack = UIStackView().then {
3546
$0.axis = .vertical
@@ -134,14 +145,14 @@ final class EditProfileViewController: BaseViewController {
134145
}
135146

136147
private lazy var saveButton: UIButton = UIButton().then {
137-
$0.setTitle("저장", for: .normal)
138-
$0.setTitleColor(STColors.white.color, for: .normal)
148+
let style = Typography.Body_18_B
149+
style.color = STColors.white.color
150+
$0.setAttributedTitle("저장하기".set(style: style), for: .normal)
139151
$0.backgroundColor = STColors.primary2.color
140152
$0.layer.cornerRadius = 8
141153
}
142154

143-
init(router: SettingRouter, viewModel: EditProfileViewModel = EditProfileViewModel()) {
144-
self.router = router
155+
init(viewModel: EditProfileViewModel = EditProfileViewModel()) {
145156
self.viewModel = viewModel
146157
super.init(nibName: nil, bundle: nil)
147158
}
@@ -165,6 +176,11 @@ extension EditProfileViewController {
165176
title = "프로필 수정"
166177
navigationBar.backgroundColor = STColors.white.color
167178
let backButtonItem = NaivgationBarButtonItem.back
179+
backButtonItem.tapPublisher
180+
.sink { [weak self] in
181+
self?.viewModel.send(input: .backButtonTapped)
182+
}
183+
.store(in: &store)
168184
setNavigationBarLeftButtonItems(items: [backButtonItem])
169185
}
170186
}
@@ -181,6 +197,8 @@ extension EditProfileViewController {
181197
self.view.backgroundColor = STColors.white.color
182198
self.view.addSubview(scrollView)
183199
self.view.addSubview(saveButton)
200+
self.view.addSubview(backgourndView)
201+
backgourndView.addSubview(popup)
184202
scrollView.addSubview(contentView)
185203
contentView.addSubview(contentStackView)
186204

@@ -215,6 +233,16 @@ extension EditProfileViewController {
215233
make.top.bottom.equalToSuperview()
216234
make.leading.trailing.equalToSuperview().inset(24)
217235
}
236+
237+
backgourndView.snp.makeConstraints { make in
238+
make.edges.equalToSuperview()
239+
}
240+
241+
popup.snp.makeConstraints { make in
242+
make.center.equalToSuperview()
243+
make.width.equalTo(327)
244+
make.height.equalTo(206)
245+
}
218246

219247
nameStack.snp.makeConstraints {
220248
$0.leading.trailing.equalToSuperview()
@@ -266,10 +294,11 @@ extension EditProfileViewController {
266294
self.nameTextField.text = user.name
267295
self.genderSelectionView.setInitialSelection(gender: user.gender.rawValue)
268296
self.birthTextField.text = user.birthDate
269-
if let bornTime = user.bornTime {
270-
self.bornTimeSetButton.selectedItem = bornTime
297+
if let bornTime = user.birthTime {
298+
self.bornTimeSetButton.selectedItem = "\(bornTime[0]) ~ \(bornTime[1])"
271299
} else {
272300
self.dontKnowButton.isSelected = true
301+
self.bornTimeSetButton.isEnabled = false
273302
}
274303
}
275304
.store(in: &store)
@@ -332,7 +361,47 @@ extension EditProfileViewController {
332361
self.birthStack.layoutIfNeeded()
333362
}
334363
.store(in: &store)
364+
365+
viewModel.output.setTimePickerLabel
366+
.receive(on: RunLoop.main)
367+
.sink { [weak self] time in
368+
guard let self else { return }
369+
self.bornTimeSetButton.selectedItem = time
370+
}
371+
.store(in: &store)
372+
373+
viewModel.output.navigate
374+
.receive(on: RunLoop.main)
375+
.sink { [weak self ] route in
376+
guard let self else { return }
377+
if route == .pop { self.navigationController?.popViewController(animated: true)}
378+
}
379+
.store(in: &store)
380+
381+
viewModel.output.updatePopupHiden
382+
.receive(on: RunLoop.main)
383+
.sink { [weak self] isHidden in
384+
guard let self else { return }
385+
self.popup.isHidden = isHidden
386+
self.backgourndView.isHidden = isHidden
387+
}
388+
.store(in: &store)
389+
390+
popup.outButton.tapPublisher
391+
.sink { [weak self] _ in
392+
guard let self else { return }
393+
self.navigationController?.popViewController(animated: true)
394+
}
395+
.store(in: &store)
335396

397+
popup.actionButton.tapPublisher
398+
.sink { [weak self] _ in
399+
guard let self else { return }
400+
self.popup.isHidden = true
401+
self.backgourndView.isHidden = true
402+
}
403+
.store(in: &store)
404+
336405
saveButton.tapPublisher
337406
.sink { [weak self] _ in
338407
guard let self else { return }
@@ -350,7 +419,7 @@ extension EditProfileViewController {
350419
dontKnowButton.gesturePublisher(gestureRecognizer: UITapGestureRecognizer())
351420
.sink { [weak self] _ in
352421
guard let self else { return }
353-
self.viewModel.send(input: .bornTimeSelected(bornTime: .dontKnow(isSelected: true)))
422+
self.viewModel.send(input: .bornTimeSelected(bornTime: .dontKnow(isSelected: self.dontKnowButton.isSelected)))
354423
bornTimeSetButton.isEnabled.toggle()
355424
self.dontKnowButton.isSelected.toggle()
356425
}
@@ -463,6 +532,7 @@ extension EditProfileViewController: UITextFieldDelegate {
463532
}
464533

465534
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
535+
textField.resignFirstResponder()
466536
if textField === nameTextField {
467537
guard let name = textField.text, !name.isEmpty else {
468538
return false
@@ -472,8 +542,3 @@ extension EditProfileViewController: UITextFieldDelegate {
472542
return true
473543
}
474544
}
475-
476-
@available(iOS 17.0, *)
477-
#Preview {
478-
return EditProfileViewController(router: SettingRouter())
479-
}

0 commit comments

Comments
 (0)