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

[Refactor]#71 UserAPI URL Session으로 변경 #72

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Keyneez/Keyneez.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
0233C6FC296C8E4600A177E9 /* JellyDetailBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0233C6FB296C8E4600A177E9 /* JellyDetailBottomSheetViewController.swift */; };
0233C6FE296C8F4A00A177E9 /* JellyDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0233C6FD296C8F4A00A177E9 /* JellyDetailView.swift */; };
023B559429655CD200FB2462 /* CGFloat+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023B559329655CD200FB2462 /* CGFloat+Extension.swift */; };
024EF5B429ABCE5A00085D0D /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024EF5B329ABCE5A00085D0D /* NetworkError.swift */; };
0250056E296DB58600DE08BE /* PhoneLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0250056D296DB58600DE08BE /* PhoneLoginViewController.swift */; };
02500573296DDF8F00DE08BE /* SignUpConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02500572296DDF8F00DE08BE /* SignUpConstant.swift */; };
02500575296DEF7F00DE08BE /* SimplePwdCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02500574296DEF7F00DE08BE /* SimplePwdCollectionViewCell.swift */; };
Expand Down Expand Up @@ -194,6 +195,7 @@
0233C6FB296C8E4600A177E9 /* JellyDetailBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyDetailBottomSheetViewController.swift; sourceTree = "<group>"; };
0233C6FD296C8F4A00A177E9 /* JellyDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyDetailView.swift; sourceTree = "<group>"; };
023B559329655CD200FB2462 /* CGFloat+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Extension.swift"; sourceTree = "<group>"; };
024EF5B329ABCE5A00085D0D /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = "<group>"; };
0250056D296DB58600DE08BE /* PhoneLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneLoginViewController.swift; sourceTree = "<group>"; };
02500572296DDF8F00DE08BE /* SignUpConstant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpConstant.swift; sourceTree = "<group>"; };
02500574296DEF7F00DE08BE /* SimplePwdCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimplePwdCollectionViewCell.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -736,6 +738,7 @@
B14D10B92959CC340004946F /* NetworkLayer */ = {
isa = PBXGroup;
children = (
024EF5B329ABCE5A00085D0D /* NetworkError.swift */,
B1A80379296E9D680007DDD9 /* Encodable.swift */,
B1A8037B296E9DB60007DDD9 /* NetworkLoggerPlugin.swift */,
B8FC6639296F22EA00AEE9C3 /* ContentAPI.swift */,
Expand Down Expand Up @@ -1292,6 +1295,7 @@
022CB224296A2B370084DEF5 /* SignUpContentModel.swift in Sources */,
B8C02F052962F5D4005612CE /* SettingViewController.swift in Sources */,
B1A80363296DA9A40007DDD9 /* IDIssuedViewActionables.swift in Sources */,
024EF5B429ABCE5A00085D0D /* NetworkError.swift in Sources */,
0233C6F8296C07A300A177E9 /* JellyProductCollectionViewCell.swift in Sources */,
B18B60D2296552DA00AF14F5 /* NiblessViewController.swift in Sources */,
B10AAD64296967F300AB8475 /* BenefitCollectionViewCell.swift in Sources */,
Expand Down
32 changes: 32 additions & 0 deletions Keyneez/Keyneez/Global/NetworkLayer/NetworkError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// NetworkError.swift
// Keyneez
//
// Created by 최효원 on 2023/02/27.
//

import Foundation

enum NetworkError: LocalizedError {
case unknownError
case invalidStatusCode(Int)
case invalidResponse(Error)
case components
case badRequest(Error)
case parsing(Error)
case emptyData
case decodeError

var errorDescription: String? {
switch self {
case .unknownError: return "알수 없는 에러입니다."
case .invalidStatusCode: return "status코드가 200~299가 아닙니다."
case .invalidResponse: return "잘못된 응답입니다."
case .components: return "components를 생성 에러가 발생했습니다."
case .badRequest: return "URL request 관련 에러가 발생했습니다."
case .parsing: return "데이터 parsing 중에 에러가 발생했습니다."
case .emptyData: return "data가 비어있습니다."
case .decodeError: return "decode 에러가 발생했습니다."
}
}
}
103 changes: 60 additions & 43 deletions Keyneez/Keyneez/Global/NetworkLayer/UserAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import Moya

enum UserAPIError: LocalizedError {
case encodingError
Expand All @@ -23,7 +22,7 @@ enum UserAPI {
case patchUserWithOCRTeenID(token: String, param: UserCheckYouthIDRequestDto)
}

extension UserAPI: TargetType {
extension UserAPI {

var baseURL: URL {
return URL(string: "http://15.165.186.200:3000")!
Expand All @@ -33,71 +32,89 @@ extension UserAPI: TargetType {
switch self {
case .getUserInfo:
return URLConstant.user
case .postUserInfo, .patchUserPickInfo:
case .postUserInfo,
.patchUserPickInfo:
return "/user/signup"
case .patchUserPwdInfo, .postPwdFetch:
case .patchUserPwdInfo,
.postPwdFetch:
return "/user/signup/pw"
case .postUserLoginInfo:
return "/user/signin"
case .patchUserWithOCRSchoolID, .patchUserWithOCRTeenID, .getUserInfo:
case .patchUserWithOCRSchoolID,
.patchUserWithOCRTeenID,
.getUserInfo:
return "/user"
default:
return ""
}
}

var method: Moya.Method {
var method: String {
switch self {
case .postUserInfo:
return .post
case .patchUserPickInfo:
return .patch
case .patchUserPwdInfo, .patchUserWithOCRTeenID, .patchUserWithOCRSchoolID:
return .patch
case .postPwdFetch:
return .post
case .postUserLoginInfo:
return .post
return "POST"
case .patchUserPickInfo,
.patchUserPwdInfo,
.patchUserWithOCRTeenID,
.patchUserWithOCRSchoolID:
return "PATCH"
case .postPwdFetch,
.postUserLoginInfo:
return "POST"
case .getUserInfo:
return .get
return "GET"
}
}

var headers: [String: String]? {
switch self {
case .postUserInfo,
.postUserLoginInfo:
return ["Content-Type": "application/json"]
case .patchUserPickInfo(let token, _),
.patchUserPwdInfo(let token, _),
.postPwdFetch(let token, _),
.getUserInfo(let token),
.patchUserWithOCRTeenID(let token, _),
.patchUserWithOCRSchoolID(let token, _):
return ["Content-Type": "application/json", "Authorization": token]
default:
return nil
}
}

var task: Moya.Task {
private func body() -> [String: Any]? {
switch self {
case .postUserInfo(let param):
return .requestParameters(parameters: try! param.asParameter(), encoding: JSONEncoding.default)
return try? param.asParameter()
case .patchUserPickInfo(_, let param):
return .requestParameters(parameters: try! param.asParameter(), encoding: JSONEncoding.default)
case .patchUserPwdInfo(_, let param):
return .requestParameters(parameters: try! param.asParameter(), encoding: JSONEncoding.default)
return try? param.asParameter()
case .patchUserPwdInfo(_, param: let param):
return try? param.asParameter()
case .postPwdFetch(_, param: let param):
return .requestParameters(parameters: try! param.asParameter(), encoding: JSONEncoding.default)
case .postUserLoginInfo(let param):
return .requestParameters(parameters: try! param.asParameter(), encoding: JSONEncoding.default)
return try? param.asParameter()
case .postUserLoginInfo(param: let param):
return try? param.asParameter()
case .patchUserWithOCRSchoolID(_, param: let param):
return try? param.asParameter()
case .patchUserWithOCRTeenID(token: let token, param: let param):
return try? param.asParameter()
case .getUserInfo:
return .requestPlain
case .patchUserWithOCRSchoolID(_, let param):
return .requestParameters(parameters: try! param.asParameter(), encoding: JSONEncoding.default)
case .patchUserWithOCRTeenID(_, let param):
return .requestParameters(parameters: try! param.asParameter(), encoding: JSONEncoding.default)
return nil
}
}

private var vaildationType: Moya.ValidationType {
return .successAndRedirectCodes
}

var headers: [String: String]? {
switch self {
case .postUserInfo, .postUserLoginInfo:
return ["Content-Type": "application/json"]
case .patchUserPickInfo(let token, _), .patchUserPwdInfo(let token, _),
.postPwdFetch(let token, _), .getUserInfo(let token), .patchUserWithOCRTeenID(let token, _), .patchUserWithOCRSchoolID(let token, _):
return ["Content-Type": "application/json", "Authorization": token]
default:
return nil
// Url Request setting 함수
func asURLRequest() -> URLRequest {
let url = baseURL.appendingPathComponent(path)
var request = URLRequest(url: url)
request.httpMethod = method
request.allHTTPHeaderFields = headers

if let param = body() {
let jsonData = try? JSONSerialization.data(withJSONObject: param)
request.httpBody = jsonData
}
return request
}

}
82 changes: 42 additions & 40 deletions Keyneez/Keyneez/Global/NetworkLayer/UserAPIProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Created by Jung peter on 1/11/23.
//

import Moya
import Foundation

enum DecodeError: Error {
Expand All @@ -21,71 +20,74 @@ struct UserInfo: Codable {
final class UserAPIProvider {

static let shared: UserAPIProvider = .init()
let userProvider = MoyaProvider<UserAPI>(plugins: [NetworkLoggerPlugin(verbose: true)])

private init() { }

func postUserInfo(param: ProductDanalRequestDto, completion: @escaping (Result<ProductDanalResponseDto?, Error>) -> Void) {
let target = UserAPI.postUserInfo(param: param)
responseFrom(target, modelType: ProductDanalResponseDto.self, completion: completion)
makeRequest(UserAPI.postUserInfo(param: param), modelType: ProductDanalResponseDto.self, completion: completion)
}

func patchUserInfo(token: String, param: ProductJellyRequstDto, completion: @escaping (Result<ProductJellyResponseDto?, Error>) -> Void) {
let target = UserAPI.patchUserPickInfo(token: token, param: param)
responseFrom(target, modelType: ProductJellyResponseDto.self, completion: completion)
makeRequest(UserAPI.patchUserPickInfo(token: token, param: param), modelType: ProductJellyResponseDto.self, completion: completion)
}

func patchPwdInfo(token: String, param: ProductPwdRequestDto, completion: @escaping (Result<ProductPwdResponseDto?, Error>) -> Void) {
let target = UserAPI.patchUserPwdInfo(token: token, param: param)
responseFrom(target, modelType: ProductPwdResponseDto.self, completion: completion)
makeRequest(UserAPI.patchUserPwdInfo(token: token, param: param), modelType: ProductPwdResponseDto.self, completion: completion)
}

func postLoginInfo(param: LoginRequestDto, completion: @escaping (Result<LoginResponseDto?, Error>) -> Void) {
let target = UserAPI.postUserLoginInfo(param: param)
responseFrom(target, modelType: LoginResponseDto.self, completion: completion)
makeRequest(UserAPI.postUserLoginInfo(param: param), modelType: LoginResponseDto.self, completion: completion)
}

func patchInfoWithStudentIDOCR(token: String, param: UserCheckStudentIDRequestDto, completion: @escaping(Result<EditUserResponseDto?, Error>) -> Void) {
let target = UserAPI.patchUserWithOCRSchoolID(token: token, param: param)
responseFrom(target, modelType: EditUserResponseDto.self, completion: completion)
makeRequest(UserAPI.patchUserWithOCRSchoolID(token: token, param: param), modelType: EditUserResponseDto.self, completion: completion)
}

func patchInfoWithTeenIDOCR(token: String, param: UserCheckYouthIDRequestDto, completion: @escaping(Result<EditUserResponseDto?, Error>) -> Void) {
let target = UserAPI.patchUserWithOCRTeenID(token: token, param: param)
responseFrom(target, modelType: EditUserResponseDto.self, completion: completion)
makeRequest(UserAPI.patchUserWithOCRTeenID(token: token, param: param), modelType: EditUserResponseDto.self, completion: completion)
}

func getUserInfo(token: String, completion: @escaping (Result<UserInquiryResponseDto?, Error>) -> Void) {
let target = UserAPI.getUserInfo(token: token)
responseFrom(target, modelType: UserInquiryResponseDto.self, completion: completion)
makeRequest(UserAPI.getUserInfo(token: token), modelType: UserInquiryResponseDto.self, completion: completion)
}
}

extension UserAPIProvider {

func responseFrom<T: Codable>(_ target: UserAPI, modelType: T.Type, completion: @escaping (Result<T?, Error>) -> Void) {
userProvider.request(target) { result in
self.process(type: modelType, result: result, completion: completion)
}
}

func process<T: Codable>(
type: T.Type,
result: Result<Response, MoyaError>,
completion: @escaping (Result<T?, Error>) -> Void
) {
switch result {
case .success(let response):
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
if let data = try? decoder.decode(GenericResponse<T>.self, from: response.data), data.status >= 200 && data.status < 400 {
let body = data.data
completion(.success(body as? T))
} else {
completion(.failure(DecodeError.decodeError))

func makeRequest<T: Codable>(_ target: UserAPI, modelType: T.Type, completion: @escaping (Result<T?, Error>) -> Void) {
//세션 생성
let session = URLSession.shared
//task 지정
let task = session.dataTask(with: target.asURLRequest()) { data, response, error in

//에러 처리 - Response
if let error = error {
completion(.failure(error))
return
}

guard let httpResponse = response as? HTTPURLResponse else {
completion(.failure(NetworkError.invalidResponse as! Error))
return
}

let statusCode = httpResponse.statusCode
guard (200..<300).contains(statusCode) else {
completion(.failure(NetworkError.invalidStatusCode(statusCode)))
return
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

if let data = data, let response = try? decoder.decode(GenericResponse<T>.self, from: data) {
let body = response.data
completion(.success(body as? T))
} else {
completion(.failure(DecodeError.decodeError))
}
}
case .failure(let error):
completion(.failure(error))
task.resume()
}
}
}