Skip to content

Commit

Permalink
Merge pull request #325 from OMZigak/feat/#324-MyPageAPI
Browse files Browse the repository at this point in the history
[Feat] 마이페이지 API 연결
  • Loading branch information
hooni0918 authored Aug 23, 2024
2 parents f44ac8a + 1b8acc5 commit c50329f
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 18 deletions.
20 changes: 20 additions & 0 deletions KkuMulKum.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
784824F72C6E1C9900FE07A0 /* AuthServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824F62C6E1C9900FE07A0 /* AuthServiceProtocol.swift */; };
784824FC2C75BF7900FE07A0 /* MyPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824FB2C75BF7900FE07A0 /* MyPageViewModel.swift */; };
784824FE2C75F25900FE07A0 /* MyPageEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */; };
784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825082C77623C00FE07A0 /* MypageTargetType.swift */; };
7848250B2C77627200FE07A0 /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250A2C77627200FE07A0 /* UserService.swift */; };
784825112C77666500FE07A0 /* MyPageUserServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825102C77666500FE07A0 /* MyPageUserServiceType.swift */; };
784825012C77016400FE07A0 /* MyPageEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825002C77016400FE07A0 /* MyPageEditView.swift */; };
784825032C77016F00FE07A0 /* MyPageEditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825022C77016F00FE07A0 /* MyPageEditViewModel.swift */; };
784825052C7716E900FE07A0 /* MyPageAskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825042C7716E900FE07A0 /* MyPageAskViewController.swift */; };
Expand Down Expand Up @@ -262,6 +265,9 @@
784824F62C6E1C9900FE07A0 /* AuthServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthServiceProtocol.swift; sourceTree = "<group>"; };
784824FB2C75BF7900FE07A0 /* MyPageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageViewModel.swift; sourceTree = "<group>"; };
784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditViewController.swift; sourceTree = "<group>"; };
784825082C77623C00FE07A0 /* MypageTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageTargetType.swift; sourceTree = "<group>"; };
7848250A2C77627200FE07A0 /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = "<group>"; };
784825102C77666500FE07A0 /* MyPageUserServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageUserServiceType.swift; sourceTree = "<group>"; };
784825002C77016400FE07A0 /* MyPageEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditView.swift; sourceTree = "<group>"; };
784825022C77016F00FE07A0 /* MyPageEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditViewModel.swift; sourceTree = "<group>"; };
784825042C7716E900FE07A0 /* MyPageAskViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageAskViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -573,6 +579,14 @@
path = ViewModel;
sourceTree = "<group>";
};
7848250F2C77664400FE07A0 /* MyPageServiceProtocol */ = {
isa = PBXGroup;
children = (
784825102C77666500FE07A0 /* MyPageUserServiceType.swift */,
);
path = MyPageServiceProtocol;
sourceTree = "<group>";
};
789196392C49697F00FF8CDF /* Auth */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -737,6 +751,7 @@
A3DD9C672C45C78300E58A13 /* HomeService.swift */,
DD3F9DC52C484DEB008E1FF7 /* PromiseService.swift */,
DD3F9DD12C485753008E1FF7 /* UtilService.swift */,
7848250A2C77627200FE07A0 /* UserService.swift */,
);
path = Service;
sourceTree = "<group>";
Expand Down Expand Up @@ -1174,6 +1189,7 @@
DDE7D2C92C47EE81005A921F /* PromiseTargetType.swift */,
A39F2B1C2C47F3D0008DA5F5 /* HomeTargetType.swift */,
DD3F9DD32C4858A3008E1FF7 /* UtilTargetType.swift */,
784825082C77623C00FE07A0 /* MypageTargetType.swift */,
);
path = TargetType;
sourceTree = "<group>";
Expand Down Expand Up @@ -1246,6 +1262,7 @@
DE159D312C406E1600425101 /* MyPage */ = {
isa = PBXGroup;
children = (
7848250F2C77664400FE07A0 /* MyPageServiceProtocol */,
784824FA2C75BF6800FE07A0 /* ViewModel */,
DE159D2E2C406E1600425101 /* View */,
DE159D302C406E1600425101 /* ViewController */,
Expand Down Expand Up @@ -1877,6 +1894,7 @@
DE6D4D0F2C3F14D80005584B /* MeetingInfoService.swift in Sources */,
DD3F9DD62C4988E2008E1FF7 /* RegisterMeetingsResponseModel.swift in Sources */,
DD3F9DCC2C485614008E1FF7 /* HomeServiceType.swift in Sources */,
7848250B2C77627200FE07A0 /* UserService.swift in Sources */,
DD39768A2C41C2AD00E2A4C4 /* HomeViewController.swift in Sources */,
DED5DBF42C34539A006ECE7E /* BaseTableViewCell.swift in Sources */,
DDE7D2CA2C47EE81005A921F /* PromiseTargetType.swift in Sources */,
Expand All @@ -1898,7 +1916,9 @@
DE32D1D22C3BF703006848DF /* LoginUserResponseModel.swift in Sources */,
DD8626692C4606A300E4F980 /* ReadyStatusProgressView.swift in Sources */,
DE9E18892C3BC91000DB76B4 /* ResponseBodyDTO.swift in Sources */,
784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */,
DD8626642C4606A300E4F980 /* ReadyPlanInfoView.swift in Sources */,
784825112C77666500FE07A0 /* MyPageUserServiceType.swift in Sources */,
DD3976682C41769400E2A4C4 /* CreateMeetingViewModel.swift in Sources */,
DECB84582C43FBEB0022A003 /* AddPromiseViewController.swift in Sources */,
DDE7D2C62C47D2BB005A921F /* MeetingTargetType.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion KkuMulKum/Network/Service/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ enum NetworkErrorMapper {
}
}

class AuthService: AuthServiceType {
class AuthService: AuthServiceProtocol {
private var keychainService: KeychainService
private var provider = MoyaProvider<AuthTargetType>()

Expand Down
44 changes: 44 additions & 0 deletions KkuMulKum/Network/Service/UserService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// UserService.swift
// KkuMulKum
//
// Created by 이지훈 on 8/22/24.
//

import Foundation

import Moya

final class MyPageUserService: MyPageUserServiceType {
private var provider = MoyaProvider<UserTargetType>()

init(provider: MoyaProvider<UserTargetType> = MoyaProvider(plugins: [MoyaLoggingPlugin()])) {
self.provider = provider
}

func getUserInfo() async throws -> LoginUserModel {
return try await performRequest(.getUserInfo)
}

func performRequest<T: ResponseModelType>(_ target: UserTargetType) async throws -> T {
return try await withCheckedThrowingContinuation { continuation in
provider.request(target) { result in
switch result {
case .success(let response):
do {
let decodedResponse = try JSONDecoder().decode(ResponseBodyDTO<T>.self, from: response.data)
guard decodedResponse.success, let data = decodedResponse.data else {
throw decodedResponse.error.map(NetworkErrorMapper.mapErrorResponse) ??
NetworkError.unknownError("Unknown error occurred")
}
continuation.resume(returning: data)
} catch {
continuation.resume(throwing: error is NetworkError ? error : NetworkError.decodingError)
}
case .failure(let error):
continuation.resume(throwing: NetworkError.networkError(error))
}
}
}
}
}
56 changes: 56 additions & 0 deletions KkuMulKum/Network/TargetType/MypageTargetType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// MypageTargetType.swift
// KkuMulKum
//
// Created by 이지훈 on 8/22/24.
//

import Foundation

import Moya

enum UserTargetType {
case getUserInfo
}

extension UserTargetType: TargetType {
var baseURL: URL {
guard let privacyInfo = Bundle.main.privacyInfo,
let urlString = privacyInfo["BASE_URL"] as? String,
let url = URL(string: urlString) else {
fatalError("Invalid BASE_URL in PrivacyInfo.plist")
}
return url
}

var path: String {
switch self {
case .getUserInfo:
return "/api/v1/users/me"
}
}

var method: Moya.Method {
switch self {
case .getUserInfo:
return .get
}
}

var task: Task {
switch self {
case .getUserInfo:
return .requestPlain
}
}

var headers: [String : String]? {
guard let token = DefaultKeychainService.shared.accessToken else {
fatalError("No access token available")
}
return [
"Content-Type": "application/json",
"Authorization": "Bearer \(token)"
]
}
}
4 changes: 2 additions & 2 deletions KkuMulKum/Source/Core/Auth/AuthInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ enum AuthError: Error {
}

class AuthInterceptor: RequestInterceptor {
let authService: AuthServiceType
let authService: AuthServiceProtocol
let provider: MoyaProvider<AuthTargetType>

init(authService: AuthServiceType, provider: MoyaProvider<AuthTargetType>) {
init(authService: AuthServiceProtocol, provider: MoyaProvider<AuthTargetType>) {
self.authService = authService
self.provider = provider
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// MyPageUserServiceType.swift
// KkuMulKum
//
// Created by 이지훈 on 8/22/24.
//

import Foundation

protocol MyPageUserServiceType {
func getUserInfo() async throws -> LoginUserModel
func performRequest<T: ResponseModelType>(_ target: UserTargetType) async throws -> T
}
34 changes: 31 additions & 3 deletions KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import UIKit
import RxSwift
import RxCocoa


class MyPageViewController: BaseViewController, CustomActionSheetDelegate {
private let rootView = MyPageView()
private let viewModel = MyPageViewModel()
Expand All @@ -25,6 +24,7 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate {
view.backgroundColor = .green1

bindViewModel()
viewModel.fetchUserInfo()
}

override func setupView() {
Expand All @@ -33,6 +33,7 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate {
}

private func bindViewModel() {
// Inputs
rootView.contentView.editButton.rx.tap
.bind(to: viewModel.editButtonTapped)
.disposed(by: disposeBag)
Expand All @@ -44,7 +45,7 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate {
bindRowTapGesture(for: rootView.etcSettingView.unsubscribeRow)
.bind(to: viewModel.unsubscribeButtonTapped)
.disposed(by: disposeBag)

// Other rows
bindRowTapGesture(for: rootView.etcSettingView.versionInfoRow)
.subscribe(onNext: { print("버전정보 탭됨") })
.disposed(by: disposeBag)
Expand All @@ -63,7 +64,8 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate {
viewModel.pushEditProfileVC
.emit(onNext: { [weak self] in
self?.pushEditProfileViewController()
}) .disposed(by: disposeBag)
})
.disposed(by: disposeBag)

viewModel.showActionSheet
.emit(onNext: { [weak self] kind in
Expand All @@ -82,6 +84,31 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate {
self?.viewModel.unsubscribe()
})
.disposed(by: disposeBag)

viewModel.userInfo
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] userInfo in
self?.updateUI(with: userInfo)
})
.disposed(by: disposeBag)
}

private func updateUI(with userInfo: LoginUserModel?) {
guard let userInfo = userInfo else { return }

rootView.contentView.nameLabel.text = userInfo.name ?? "꾸물리안 님"
rootView.contentView.levelLabel.setText("Lv. \(userInfo.level) 지각대장 꾸물이", style: .body05, color: .white)
rootView.contentView.levelLabel.setHighlightText("Lv. \(userInfo.level)", style: .body05, color: .lightGreen)

if let profileImageURL = userInfo.profileImageURL {
loadImage(from: profileImageURL, into: rootView.contentView.profileImageView)
} else {
rootView.contentView.profileImageView.image = UIImage.imgProfile
}
}

private func loadImage(from urlString: String, into imageView: UIImageView) {

}

private func bindRowTapGesture(for view: UIView) -> Observable<Void> {
Expand All @@ -90,6 +117,7 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate {
.first?
.rx.event
.map { _ in }

?? Observable.empty()
}

Expand Down
4 changes: 2 additions & 2 deletions KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import RxCocoa
import Moya

class MyPageEditViewModel: ViewModelType {
private let authService: AuthServiceType
private let authService: AuthServiceProtocol

struct Input {
let profileImageTap: Observable<Void>
Expand All @@ -28,7 +28,7 @@ class MyPageEditViewModel: ViewModelType {
let serverResponse: Driver<String?>
}

init(authService: AuthServiceType) {
init(authService: AuthServiceProtocol) {
self.authService = authService
}

Expand Down
21 changes: 18 additions & 3 deletions KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import RxSwift
import RxCocoa

class MyPageViewModel {
private let userService: MyPageUserServiceType
private let disposeBag = DisposeBag()

let editButtonTapped = PublishRelay<Void>()
let logoutButtonTapped = PublishRelay<Void>()
Expand All @@ -21,10 +23,12 @@ class MyPageViewModel {
let showActionSheet: Signal<ActionSheetKind>
let performLogout: Signal<Void>
let performUnsubscribe: Signal<Void>
private let disposeBag = DisposeBag()

let userInfo: BehaviorRelay<LoginUserModel?>

init() {
init(userService: MyPageUserServiceType = MyPageUserService()) {
self.userService = userService
self.userInfo = BehaviorRelay<LoginUserModel?>(value: nil)

pushEditProfileVC = editButtonTapped.asSignal()

showActionSheet = Observable.merge(
Expand All @@ -45,6 +49,17 @@ class MyPageViewModel {
.asSignal(onErrorJustReturn: ())
}

func fetchUserInfo() {
Task {
do {
let info = try await userService.getUserInfo()
userInfo.accept(info)
} catch {
print("Failed to fetch user info: \(error)")
}
}
}

func logout() {
print("로그아웃 눌름 ㅂㅂ")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ class LoginViewModel: NSObject {
var userName: ObservablePattern<String?> = ObservablePattern(nil)

private let provider: MoyaProvider<AuthTargetType>
private var authService: AuthServiceType
private var authService: AuthServiceProtocol
private let authInterceptor: AuthInterceptor
private let keychainAccessible: KeychainAccessible

init(
provider: MoyaProvider<AuthTargetType> = MoyaProvider<AuthTargetType>(
plugins: [NetworkLoggerPlugin(configuration: .init(logOptions: .verbose))]
),
authService: AuthServiceType = AuthService(),
authService: AuthServiceProtocol = AuthService(),
keychainAccessible: KeychainAccessible = DefaultKeychainAccessible()
) {
self.provider = provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class NicknameViewModel {
private let disposeBag = DisposeBag()
private let nicknameRegex = "^[가-힣a-zA-Z]{1,5}$"
private let provider: MoyaProvider<AuthTargetType>
private let authService: AuthServiceType
private let authService: AuthServiceProtocol

init(provider: MoyaProvider<AuthTargetType> = MoyaProvider<AuthTargetType>(),
authService: AuthServiceType = AuthService()) {
authService: AuthServiceProtocol = AuthService()) {
self.provider = provider
self.authService = authService

Expand Down
Loading

0 comments on commit c50329f

Please sign in to comment.