Skip to content

Commit

Permalink
Merge pull request #140 from boostcampwm-2022/feature/my-page-flow
Browse files Browse the repository at this point in the history
MyPage와 관련된 플로우 작성
  • Loading branch information
radiantchoi authored Dec 1, 2022
2 parents 4cbc493 + c03d2fd commit 8e29fea
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 23 deletions.
20 changes: 20 additions & 0 deletions Segno/Segno.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
666E6F8E291CFADD00CECD4B /* MyPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 666E6F8D291CFADD00CECD4B /* MyPageViewController.swift */; };
6692A9EF292F605E00DDA835 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6692A9EE292F605E00DDA835 /* SettingsViewController.swift */; };
66A8CF612935F44100C17F84 /* MyPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A8CF602935F44100C17F84 /* MyPageViewModel.swift */; };
66A8CF652937934A00C17F84 /* MyPageRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A8CF642937934A00C17F84 /* MyPageRepository.swift */; };
66A8CF672937937300C17F84 /* UserDetailItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A8CF662937937300C17F84 /* UserDetailItem.swift */; };
66A8CF692937945300C17F84 /* UserDetailUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A8CF682937945300C17F84 /* UserDetailUseCase.swift */; };
66A8CF6B2937947A00C17F84 /* UserDetailDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A8CF6A2937947A00C17F84 /* UserDetailDTO.swift */; };
66A8CF6D29379A9900C17F84 /* UserDetailEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A8CF6C29379A9900C17F84 /* UserDetailEndpoint.swift */; };
66F0D7EE2925FF8B0074872E /* DiaryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F0D7ED2925FF8B0074872E /* DiaryCell.swift */; };
66F0D7F729260BA20074872E /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 66F0D7F629260BA20074872E /* GoogleSignIn */; };
791529D82932F364005A8DDB /* DiaryEditUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 791529D72932F364005A8DDB /* DiaryEditUseCase.swift */; };
Expand Down Expand Up @@ -125,6 +130,11 @@
666E6F8D291CFADD00CECD4B /* MyPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageViewController.swift; sourceTree = "<group>"; };
6692A9EE292F605E00DDA835 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
66A8CF602935F44100C17F84 /* MyPageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageViewModel.swift; sourceTree = "<group>"; };
66A8CF642937934A00C17F84 /* MyPageRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageRepository.swift; sourceTree = "<group>"; };
66A8CF662937937300C17F84 /* UserDetailItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailItem.swift; sourceTree = "<group>"; };
66A8CF682937945300C17F84 /* UserDetailUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailUseCase.swift; sourceTree = "<group>"; };
66A8CF6A2937947A00C17F84 /* UserDetailDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailDTO.swift; sourceTree = "<group>"; };
66A8CF6C29379A9900C17F84 /* UserDetailEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailEndpoint.swift; sourceTree = "<group>"; };
66F0D7ED2925FF8B0074872E /* DiaryCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiaryCell.swift; sourceTree = "<group>"; };
66F0D7EF292605AE0074872E /* Segno.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Segno.entitlements; sourceTree = "<group>"; };
791529D72932F364005A8DDB /* DiaryEditUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryEditUseCase.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -258,6 +268,7 @@
4FEBFAAE291CF9F300E78139 /* MusicInfo.swift */,
4F6F74B0292C9BF3007E7AC1 /* UserInfo.swift */,
98FDF8A0292F56580083FA05 /* Location.swift */,
66A8CF662937937300C17F84 /* UserDetailItem.swift */,
);
path = Entity;
sourceTree = "<group>";
Expand Down Expand Up @@ -298,6 +309,7 @@
9841D6162925FACC00318EA9 /* LoginUseCase.swift */,
7940FB32292E065F00276EFC /* DiaryDetailUseCase.swift */,
988414DA2923606B007C9132 /* DiaryListUseCase.swift */,
66A8CF682937945300C17F84 /* UserDetailUseCase.swift */,
791529D72932F364005A8DDB /* DiaryEditUseCase.swift */,
9894EAF429373385005F2B15 /* SettingsUseCase.swift */,
9825F41A29377875005F2163 /* ChangeUserNameUseCase.swift */,
Expand Down Expand Up @@ -378,6 +390,7 @@
4FA32427292363AA00DB04D5 /* DiaryRepository.swift */,
982A2A462924AE74006F6ACD /* UserDefaultsKey.swift */,
4F4E0D3D2924B925005ABA8F /* LoginRepository.swift */,
66A8CF642937934A00C17F84 /* MyPageRepository.swift */,
9825F41C29377ACF005F2163 /* SettingsRepository.swift */,
);
path = Repository;
Expand All @@ -398,6 +411,7 @@
4F4E0D73292521D0005ABA8F /* UserInfoDTO.swift */,
7940FB2E292E063100276EFC /* DiaryDetailDTO.swift */,
4F4E0D7A29252526005ABA8F /* TokenDTO.swift */,
66A8CF6A2937947A00C17F84 /* UserDetailDTO.swift */,
791529DB29332CF2005A8DDB /* ImageDTO.swift */,
);
path = DTO;
Expand All @@ -410,6 +424,7 @@
7940FB30292E065100276EFC /* DiaryDetailEndpoint.swift */,
791529DF293344E8005A8DDB /* DiaryPostEndpoint.swift */,
4F4E0D7529252236005ABA8F /* LoginEndpoint.swift */,
66A8CF6C29379A9900C17F84 /* UserDetailEndpoint.swift */,
791529DD29333D40005A8DDB /* ImageEndpoint.swift */,
);
path = Endpoints;
Expand Down Expand Up @@ -546,11 +561,14 @@
66F0D7EE2925FF8B0074872E /* DiaryCell.swift in Sources */,
4F9A00202922337F007D9057 /* LoginViewController.swift in Sources */,
982A2A472924AE74006F6ACD /* UserDefaultsKey.swift in Sources */,
66A8CF6B2937947A00C17F84 /* UserDetailDTO.swift in Sources */,
4F9A001B292227D7007D9057 /* NetworkManager.swift in Sources */,
9894EAF529373385005F2B15 /* SettingsUseCase.swift in Sources */,
9841D6172925FACC00318EA9 /* LoginUseCase.swift in Sources */,
66A8CF6D29379A9900C17F84 /* UserDetailEndpoint.swift in Sources */,
4FEBFAAD291CF62E00E78139 /* DiaryDetail.swift in Sources */,
4FEBFAB1291CFB5500E78139 /* SHMediaItem+.swift in Sources */,
66A8CF692937945300C17F84 /* UserDetailUseCase.swift in Sources */,
988414AE2922235B007C9132 /* LocalUtilityRepository.swift in Sources */,
66A8CF612935F44100C17F84 /* MyPageViewModel.swift in Sources */,
988414D929235345007C9132 /* DiaryCollectionViewModel.swift in Sources */,
Expand All @@ -570,11 +588,13 @@
988414DB2923606B007C9132 /* DiaryListUseCase.swift in Sources */,
982B3B7F292E68FB0077A44B /* DiaryDetailViewModel.swift in Sources */,
4FCAC5C2292B5C9000BF9CDD /* LoginSession.swift in Sources */,
66A8CF672937937300C17F84 /* UserDetailItem.swift in Sources */,
666E6F7D291CF45600CECD4B /* Coordinator.swift in Sources */,
982A3699292C905300FDC6CF /* DiaryDetailViewController.swift in Sources */,
9825F41B29377875005F2163 /* ChangeUserNameUseCase.swift in Sources */,
666E6F8C291CFAC200CECD4B /* DiaryCollectionViewController.swift in Sources */,
666E6F8A291CF82700CECD4B /* TabBarPageCase.swift in Sources */,
66A8CF652937934A00C17F84 /* MyPageRepository.swift in Sources */,
79183800292225DC00BC6992 /* UIFont+.swift in Sources */,
666E6F83291CF4A600CECD4B /* TabBarCoordinator.swift in Sources */,
791529E0293344E9005A8DDB /* DiaryPostEndpoint.swift in Sources */,
Expand Down
32 changes: 32 additions & 0 deletions Segno/Segno/Data/Network/Endpoints/UserDetailEndpoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// UserDetailEndpoint.swift
// Segno
//
// Created by 이예준 on 2022/11/30.
//

import Foundation

enum UserDetailEndpoint: Endpoint {
case item

var baseURL: URL? {
return URL(string: BaseURL.urlString)
}

var httpMethod: HTTPMethod {
return .GET
}

var path: String {
// TODO: 서버에 맞춰서 path 조정
return ""
}

var parameters: HTTPRequestParameter? {
switch self {
case .item:
return HTTPRequestParameter.queries([:]) // TODO: queries로 무언가 들어가야함
}
}
}
27 changes: 27 additions & 0 deletions Segno/Segno/Data/Repository/DTO/UserDetailDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// UserDetailDTO.swift
// Segno
//
// Created by 이예준 on 2022/11/30.
//

struct UserDetailDTO: Decodable {
// TODO: 일단은 UserDetailItem과 동일하게 작성. 이후 서버 사이드에 따라 바꾸겠습니다.
let identifier: String
let nickname: String
let writtenDiary: String

enum CodingKeys: String, CodingKey {
case identifier = "_id"
case nickname
case writtenDiary = "diary"
}

#if DEBUG
static let example = UserDetailDTO(
identifier: "id",
nickname: "test123",
writtenDiary: "50000"
)
#endif
}
30 changes: 30 additions & 0 deletions Segno/Segno/Data/Repository/MyPageRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// MyPageRepository.swift
// Segno
//
// Created by 이예준 on 2022/11/30.
//

import RxSwift

protocol MyPageRepository {
func getUserDetail() -> Single<UserDetailDTO>
}

final class MyPageRepositoryImpl: MyPageRepository {
func getUserDetail() -> Single<UserDetailDTO> {
// let endpoint = UserDetailEndpoint.item(id)
//
// return NetworkManager.shared.call(endpoint)
// .map {
// try JSONDecoder().decode(UserDetailDTO.self, from: $0)
// }

// TODO: 추후에 NetworkManager로 변경
return Single.create { observer -> Disposable in
observer(.success(UserDetailDTO.example))

return Disposables.create()
}
}
}
27 changes: 27 additions & 0 deletions Segno/Segno/Domain/UseCase/UserDetailUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// UserDetailUseCase.swift
// Segno
//
// Created by 이예준 on 2022/11/30.
//

import RxSwift

protocol UserDetailUseCase {
func getUserDetail() -> Single<UserDetailItem>
}

final class UserDetailUseCaseImpl: UserDetailUseCase {
let repository: MyPageRepository
private let disposeBag = DisposeBag()

init(repository: MyPageRepository = MyPageRepositoryImpl()) {
self.repository = repository
}

func getUserDetail() -> Single<UserDetailItem> {
return repository.getUserDetail().map {
UserDetailItem(identifier: $0.identifier, nickname: $0.nickname, writtenDiary: $0.writtenDiary)
}
}
}
12 changes: 12 additions & 0 deletions Segno/Segno/Entity/UserDetailItem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// UserDetailItem.swift
// Segno
//
// Created by 이예준 on 2022/11/30.
//

struct UserDetailItem: Hashable {
let identifier: String
let nickname: String
let writtenDiary: String
}
75 changes: 52 additions & 23 deletions Segno/Segno/Presentation/ViewController/MyPageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protocol MyPageViewDelegate: AnyObject {

final class MyPageViewController: UIViewController {
private let disposeBag = DisposeBag()

private let viewModel: MyPageViewModel
weak var delegate: MyPageViewDelegate?

private enum Metric {
Expand Down Expand Up @@ -61,12 +61,23 @@ final class MyPageViewController: UIViewController {
return tableView
}()

init(viewModel: MyPageViewModel = MyPageViewModel()) {
self.viewModel = MyPageViewModel()

super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

setupView()
setupLayout()
bindTableView()
getUserDetail()
}

private func setupView() {
Expand Down Expand Up @@ -94,29 +105,43 @@ final class MyPageViewController: UIViewController {
}

private func bindTableView() {
let dataSource = Observable<[MyPageCellModel]>.just([
.writtenDiary(title: "작성한 일기 수", subtitle: "123,456,789개"), // TODO: subtitle 불러오기
.settings(title: "설정"),
.logout(title: "logout", color: .red)
])
viewModel.nicknameObservable
.observe(on: MainScheduler.instance)
.map { return "안녕하세요,\n"+$0+"님!" }
.bind(to: titleLabel.rx.text)
.disposed(by: disposeBag)

dataSource
.bind(to: tableView.rx.items) { (tableView, row, element) in
switch element {
case .writtenDiary(let title, let subtitle):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "writtenDiary") as? SettingsActionSheetCell else { return UITableViewCell() }
cell.configure(left: title, right: subtitle)
return cell
case .settings(let title):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "settings") as? SettingsActionSheetCell else { return UITableViewCell() }
cell.configure(center: title)
return cell
case .logout(let title, let color):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "logout") as? SettingsActionSheetCell else { return UITableViewCell() }
cell.configure(center: title, color: color)
return cell
}
}
viewModel.writtenDiaryObservable
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] writtenDiary in
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal

let price = Double(writtenDiary)
let result = numberFormatter.string(from: NSNumber(value:price!))! + ""

let dataSource = Observable<[MyPageCellModel]>.just([
.writtenDiary(title: "작성한 일기 수", subtitle: result),
.settings(title: "설정"),
.logout(title: "logout", color: .red)
])
.bind(to: (self?.tableView.rx.items)!) { (tableView, row, element) in
switch element {
case .writtenDiary(let title, let subtitle):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "writtenDiary") as? SettingsActionSheetCell else { return UITableViewCell() }
cell.configure(left: title, right: subtitle)
return cell
case .settings(let title):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "settings") as? SettingsActionSheetCell else { return UITableViewCell() }
cell.configure(center: title)
return cell
case .logout(let title, let color):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "logout") as? SettingsActionSheetCell else { return UITableViewCell() }
cell.configure(center: title, color: color)
return cell
}
}
})
.disposed(by: disposeBag)

tableView.rx.itemSelected
Expand All @@ -133,6 +158,10 @@ final class MyPageViewController: UIViewController {
.disposed(by: disposeBag)
}

private func getUserDetail() {
viewModel.getUserDetail()
}

private func settingButtonTapped() {
delegate?.settingButtonTapped()
}
Expand Down
21 changes: 21 additions & 0 deletions Segno/Segno/Presentation/ViewModel/MyPageViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@
// Created by 이예준 on 2022/11/29.
//

import RxSwift

final class MyPageViewModel {
let useCase: UserDetailUseCase
var userDetailItem = PublishSubject<UserDetailItem>()
lazy var nicknameObservable = userDetailItem.map { $0.nickname }
lazy var writtenDiaryObservable = userDetailItem.map { $0.writtenDiary }

private let disposeBag = DisposeBag()

init(useCase: UserDetailUseCase = UserDetailUseCaseImpl()) {
self.useCase = useCase
}

func getUserDetail() {
useCase.getUserDetail()
.subscribe(onSuccess: { [weak self] userDetail in
self?.userDetailItem.onNext(userDetail)
}, onFailure: { error in
print(error.localizedDescription)
})
.disposed(by: disposeBag)
}
}

0 comments on commit 8e29fea

Please sign in to comment.