Skip to content

Commit

Permalink
refactor/#225 authtarget 통합
Browse files Browse the repository at this point in the history
  • Loading branch information
hooni0918 committed Jul 18, 2024
1 parent b66b122 commit efa3aab
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 46 deletions.
4 changes: 4 additions & 0 deletions KkuMulKum.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
785AE1BE2C2E878600677CA0 /* FirebaseStorageCombine-Community in Frameworks */ = {isa = PBXBuildFile; productRef = 785AE1BD2C2E878600677CA0 /* FirebaseStorageCombine-Community */; };
785AE1C02C2E878600677CA0 /* FirebaseVertexAI-Preview in Frameworks */ = {isa = PBXBuildFile; productRef = 785AE1BF2C2E878600677CA0 /* FirebaseVertexAI-Preview */; };
785AE1D12C3B07A600677CA0 /* PrivacyInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 785AE1D02C3B07A600677CA0 /* PrivacyInfo.plist */; };
789196362C492F8600FF8CDF /* AuthTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789196352C492F8600FF8CDF /* AuthTargetType.swift */; };
789873322C3D1A7B00435E96 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7898732F2C3D1A7B00435E96 /* LoginViewController.swift */; };
789873332C3D1A7B00435E96 /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789873302C3D1A7B00435E96 /* LoginViewModel.swift */; };
789873342C3D1A7B00435E96 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789873312C3D1A7B00435E96 /* LoginView.swift */; };
Expand Down Expand Up @@ -237,6 +238,7 @@
782B407E2C3E44B7008B0CA7 /* WelcomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewModel.swift; sourceTree = "<group>"; };
782B40812C3E4925008B0CA7 /* NicknameViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameViewModel.swift; sourceTree = "<group>"; };
785AE1D02C3B07A600677CA0 /* PrivacyInfo.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = PrivacyInfo.plist; sourceTree = "<group>"; };
789196352C492F8600FF8CDF /* AuthTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTargetType.swift; sourceTree = "<group>"; };
7898732F2C3D1A7B00435E96 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = "<group>"; };
789873302C3D1A7B00435E96 /* LoginViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = "<group>"; };
789873312C3D1A7B00435E96 /* LoginView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1120,6 +1122,7 @@
DDE7D2BE2C470A58005A921F /* TargetType */ = {
isa = PBXGroup;
children = (
789196352C492F8600FF8CDF /* AuthTargetType.swift */,
DDE7D2BF2C470A58005A921F /* LoginTargetType.swift */,
DDE7D2C02C470A58005A921F /* ProfileTargetType.swift */,
DDE7D2C12C470A58005A921F /* NicknameTargetType.swift */,
Expand Down Expand Up @@ -1805,6 +1808,7 @@
DD3976862C41C2AD00E2A4C4 /* HomeView.swift in Sources */,
DD8626672C4606A300E4F980 /* SetReadyInfoView.swift in Sources */,
DD41BEFC2C41D54D0095A068 /* TardyPenaltyView.swift in Sources */,
789196362C492F8600FF8CDF /* AuthTargetType.swift in Sources */,
789873322C3D1A7B00435E96 /* LoginViewController.swift in Sources */,
78BD61342C45B4A7005752FD /* AuthService.swift in Sources */,
DE32D1D22C3BF703006848DF /* LoginUserResponseModel.swift in Sources */,
Expand Down
62 changes: 61 additions & 1 deletion KkuMulKum/Network/Service/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@
//
// Created by 이지훈 on 7/14/24.
//

import Foundation
import Moya

protocol AuthServiceType {
func saveAccessToken(_ token: String) -> Bool
func saveRefreshToken(_ token: String) -> Bool
func getAccessToken() -> String?
func getRefreshToken() -> String?
func clearTokens() -> Bool
func performRequest<T: ResponseModelType>(
_ target: AuthTargetType,
completion: @escaping (
Result<T, NetworkError>
) -> Void
)
}

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

init(keychainService: KeychainService = DefaultKeychainService.shared) {
self.keychainService = keychainService
Expand Down Expand Up @@ -45,4 +52,57 @@ class AuthService: AuthServiceType {
keychainService.refreshToken = nil
return keychainService.accessToken == nil && keychainService.refreshToken == nil
}

func performRequest<T: ResponseModelType>(
_ target: AuthTargetType,
completion: @escaping (
Result<
T,
NetworkError
>
) -> Void
) {
provider.request(target) { result in
switch result {
case .success(let response):
do {
let decodedResponse = try JSONDecoder().decode(
ResponseBodyDTO<T>.self,
from: response.data
)
if decodedResponse.success {
if let data = decodedResponse.data {
completion(.success(data))
} else {
completion(.failure(.decodingError))
}
} else {
let networkError = self.handleErrorResponse(decodedResponse.error)
completion(.failure(networkError))
}
} catch {
completion(.failure(.decodingError))
}
case .failure:
completion(.failure(.networkError))
}
}
}

private func handleErrorResponse(_ error: ErrorResponse?) -> NetworkError {
guard let error = error else {
return .unknownError("Unknown error occurred")
}

switch error.code {
case 40080:
return .invalidImageFormat
case 40081:
return .imageSizeExceeded
case 40420:
return .userNotFound
default:
return .unknownError(error.message)
}
}
}
130 changes: 130 additions & 0 deletions KkuMulKum/Network/TargetType/AuthTargetType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//
// AuthTargetType.swift
// KkuMulKum
//
// Created by 이지훈 on 7/18/24.
//

import Foundation

import Moya

enum NetworkError: Error {
case invalidImageFormat
case imageSizeExceeded
case userNotFound
case decodingError
case networkError
case unknownError(String)

var message: String {
switch self {
case .invalidImageFormat:
return "이미지 확장자는 jpg, png, webp만 가능합니다."
case .imageSizeExceeded:
return "이미지 사이즈는 5MB를 넘을 수 없습니다."
case .userNotFound:
return "유저를 찾을 수 없습니다."
case .decodingError:
return "데이터 디코딩 중 오류가 발생했습니다."
case .networkError:
return "네트워크 오류가 발생했습니다."
case .unknownError(let message):
return message
}
}
}

enum AuthTargetType {
case appleLogin(identityToken: String, fcmToken: String)
case kakaoLogin(accessToken: String, fcmToken: String)
case refreshToken(refreshToken: String)
case updateProfileImage(image: Data, fileName: String, mimeType: String)
case updateName(name: String)
}

extension AuthTargetType: 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 .appleLogin, .kakaoLogin:
return "/api/v1/auth/signin"
case .refreshToken:
return "/api/v1/auth/reissue"
case .updateProfileImage:
return "/api/v1/users/me/image"
case .updateName:
return "/api/v1/users/me/name"
}
}

var method: Moya.Method {
switch self {
case .appleLogin, .kakaoLogin, .refreshToken:
return .post
case .updateProfileImage, .updateName:
return .patch
}
}

var task: Task {
switch self {
case let .appleLogin(_, fcmToken):
return .requestJSONEncodable(SocialLoginRequestModel(provider: "APPLE", fcmToken: fcmToken))
case let .kakaoLogin(_, fcmToken):
return .requestJSONEncodable(SocialLoginRequestModel(provider: "KAKAO", fcmToken: fcmToken))
case .refreshToken:
return .requestPlain
case let .updateProfileImage(imageData, fileName, mimeType):
let formData: [MultipartFormData] = [
MultipartFormData(
provider: .data(imageData),
name: "image",
fileName: fileName,
mimeType: mimeType
)
]
return .uploadMultipart(formData)
case let .updateName(name):
return .requestParameters(
parameters: ["name": name],
encoding: JSONEncoding.default
)
}
}

var headers: [String : String]? {
switch self {
case .appleLogin(let identityToken, _):
return ["Authorization": identityToken, "Content-Type": "application/json"]
case .kakaoLogin(let accessToken, _):
return ["Authorization": accessToken, "Content-Type": "application/json"]
case .refreshToken(let refreshToken):
return ["Authorization": refreshToken, "Content-Type": "application/json"]
case .updateProfileImage:
guard let token = DefaultKeychainService.shared.accessToken else {
return ["Content-Type": "multipart/form-data"]
}
return [
"Authorization": "Bearer \(token)",
"Content-Type": "multipart/form-data"
]
case .updateName:
guard let token = DefaultKeychainService.shared.accessToken else {
fatalError("No access token available")
}
return [
"Content-Type": "application/json",
"Authorization": "Bearer \(token)"
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@
// Created by 이지훈 on 7/10/24.
//

import UIKit

import Moya
import MobileCoreServices
import UIKit

class ProfileSetupViewModel {
let profileImage = ObservablePattern<UIImage?>(UIImage.imgProfile)
let isConfirmButtonEnabled = ObservablePattern<Bool>(false)
let nickname: String
let serverResponse = ObservablePattern<String?>(nil)

private let provider = MoyaProvider<ProfileTargetType>()
private let authService: AuthServiceType

init(nickname: String) {
init(nickname: String, authService: AuthServiceType = AuthService()) {
self.nickname = nickname
self.authService = authService
}

func updateProfileImage(_ image: UIImage?) {
Expand All @@ -42,49 +41,16 @@ class ProfileSetupViewModel {
let fileName = "profile_image.jpg"
let mimeType = "image/jpeg"

provider.request(.updateProfileImage(image: imageData, fileName: fileName, mimeType: mimeType)) { [weak self] result in
authService.performRequest(.updateProfileImage(image: imageData, fileName: fileName, mimeType: mimeType)) { [weak self] (result: Result<EmptyModel, NetworkError>) in
print("네트워크 요청 완료")
switch result {
case .success(let response):
print("서버 응답 상태 코드: \(response.statusCode)")
print("서버 응답 데이터: \(String(data: response.data, encoding: .utf8) ?? "디코딩 불가")")
do {
let decodedResponse = try JSONDecoder().decode(
ResponseBodyDTO<EmptyModel>.self,
from: response.data
)
if decodedResponse.success {
self?.serverResponse.value = "프로필 이미지가 성공적으로 업로드되었습니다."
print("프로필 이미지 업로드 성공")
completion(true)
} else {
if let errorCode = decodedResponse.error?.code {
switch errorCode {
case 40080:
self?.serverResponse.value = "이미지 확장자는 jpg, png, webp만 가능합니다."
case 40081:
self?.serverResponse.value = "이미지 사이즈는 5MB를 넘을 수 없습니다."
case 40420:
self?.serverResponse.value = "유저를 찾을 수 없습니다."
default:
self?.serverResponse.value = decodedResponse.error?.message ??
"알 수 없는 오류가 발생했습니다."
}
} else {
self?.serverResponse.value = decodedResponse.error?.message ??
"알 수 없는 오류가 발생했습니다."
}
print("프로필 이미지 업로드 실패: \(self?.serverResponse.value ?? "")")
completion(false)
}
} catch {
self?.serverResponse.value = "데이터 디코딩 중 오류가 발생했습니다."
print("데이터 디코딩 오류: \(error)")
completion(false)
}
case .success:
self?.serverResponse.value = "프로필 이미지가 성공적으로 업로드되었습니다."
print("프로필 이미지 업로드 성공")
completion(true)
case .failure(let error):
self?.serverResponse.value = "네트워크 오류: \(error.localizedDescription)"
print("네트워크 오류: \(error.localizedDescription)")
self?.serverResponse.value = error.message
print("프로필 이미지 업로드 실패: \(error.message)")
completion(false)
}
}
Expand Down

0 comments on commit efa3aab

Please sign in to comment.