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

Moya 및 Combine을 사용한 Network 리펙토링 #163

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2675fd8
feat #144: FirebaseAPI 생성
minsangKang Apr 17, 2024
6072374
feat #144: AuthAPI 생성
minsangKang Apr 17, 2024
234bec5
feat #144: DailysAPI 생성
minsangKang Apr 17, 2024
0d80edf
update #144: AuthAPI: task 수정
minsangKang Apr 17, 2024
f554462
feat #144: getAppVersion RxMoya 방식 구현
minsangKang May 15, 2024
8c90a07
feat #144: getServerURL RxMoya 방식 구현
minsangKang May 22, 2024
1bdfc68
feat #144: postSignup RxMoya 방식 구현
minsangKang May 22, 2024
c6aa580
feat #144: CombineMoya 방식으로 수정 & AuthRepository 구현
minsangKang May 25, 2024
f80e638
feat #144: 200대 statusCode가 아닌 경우 ErrorResponse 값 반환 구현
minsangKang Jun 9, 2024
3435112
rename #144: Info -> Request 명칭 수정
minsangKang Jun 9, 2024
51f480a
feat #144: DailysRepository 구현
minsangKang Jun 9, 2024
4dfe1af
feat #144: RecordTimesRepository 구현
minsangKang Jun 29, 2024
516c02d
feat #144: SyncLogRepository 구현
minsangKang Jun 29, 2024
115b640
feat #144: NotificationRepository 구현
minsangKang Jun 29, 2024
82539ac
feat #144: NetworkController 내 func들 FirebaseAPI 내 반영
minsangKang Jun 29, 2024
dccb6d2
refact #144: DTO 폴더 삭제, Response 및 info로 분리
minsangKang Jun 29, 2024
c8bb161
chore #144: 디렉토리 리펙토링
minsangKang Jun 29, 2024
138d552
chore #144: 기존 UseCase들 lagacy 폴더로 이동
minsangKang Jun 29, 2024
1a7e0db
feat #144: NetworkController 내 함수들 FirebaseRepository 내 반영
minsangKang Jun 29, 2024
60b8304
feat #144: FirebaseUseCase 생성
minsangKang Jun 29, 2024
c367b9c
feat #144: Firebase 관련 UseCase 생성
minsangKang Jun 29, 2024
e83bd72
feat #144: GetAppVersionUseCase 반영
minsangKang Jun 30, 2024
ec27002
delete #144: 불필요파일 제거
minsangKang Jun 30, 2024
a1531f5
feat #144: GetServerURLUseCase 반영
minsangKang Jun 30, 2024
b77f417
delete #144: 불필요파일 제거
minsangKang Jun 30, 2024
5437678
Merge branch 'master' into feature/network_tony
minsangKang Jul 6, 2024
ebb5afa
feat #144: GetTiTiFunctionsUseCase 반영
minsangKang Jul 6, 2024
9c517cb
fix #144: TTProvider의 request를 사용하도록 수정, map operator 생성
minsangKang Jul 6, 2024
89c5ef3
feat #144: GetUpdateHistorysUseCase 반영
minsangKang Jul 6, 2024
fd8ef4d
feat #144: GetSurveysUseCase 반영
minsangKang Jul 6, 2024
e550310
feat #144: AuthRepository 관련 UseCase들 생성
minsangKang Jul 6, 2024
9238ebc
feat #144: SignupUseCase, SigninUseCase 반영
minsangKang Jul 6, 2024
0a65a67
feat #144: CheckUsernameExitUseCase 반영
minsangKang Jul 6, 2024
d4e899f
feat #144: GetDailysUseCase 셍성 & 반영
minsangKang Jul 7, 2024
2c2ca7a
feat #144: 나머지 UseCase들 생성
minsangKang Jul 7, 2024
3091d08
fix #144: statusCode에 따른 NetworkError가 그대로 전달되도록 수정
minsangKang Jul 7, 2024
9e8cd74
feat #144: UploadDailys 동작 구현, header 를 통해 gmt 전달 구현
minsangKang Aug 10, 2024
4d76aa8
feat #144: GetNotificationUseCase 반영
minsangKang Aug 11, 2024
c98a398
feat #144: CheckEmailExitUseCase, UpdatePasswordUseCase 반영
minsangKang Aug 11, 2024
c0d0892
feat #144: Post & GetRecordTimeUseCase, GetSyncLogUseCase 반영
minsangKang Aug 11, 2024
7a357a2
feat #144: Network 클래스 제거, 리펙토링 완료
minsangKang Aug 11, 2024
072325a
Merge branch 'master' into feature/network_tony
minsangKang Sep 29, 2024
10b99ef
feat #144: 코드리뷰-버전정보 수신오류시 팝업 표시
minsangKang Nov 24, 2024
bc51802
feat #144: 코드리뷰-extension TargetType static 제거
minsangKang Nov 24, 2024
aa72f11
feat #144: 코드리뷰-uppercase 변수명 lowercase 수정
minsangKang Nov 24, 2024
b78ecae
강제로 error 발생시킨 코드 제거
minsangKang Nov 24, 2024
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
597 changes: 313 additions & 284 deletions Project_Timer.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

64 changes: 37 additions & 27 deletions Project_Timer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import FirebaseAnalytics
import GoogleMobileAds
import WidgetKit
import GoogleSignIn
import Moya
import Combine

@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
Expand All @@ -21,6 +23,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
}
// 화면 회전을 제어할 변수 선언
var shouldSupportPortraitOrientation = false
// Combine binding
private var cancellables = Set<AnyCancellable>()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.checkLatestVersion(isLaunch: true)
Expand Down Expand Up @@ -101,17 +105,27 @@ extension AppDelegate: UNUserNotificationCenterDelegate {

// MARK: Configure
extension AppDelegate {
/// 최신버전 체크
private func checkLatestVersion(isLaunch: Bool) {
/// 최신버전 체크로직
let getLatestVersionUseCase = GetLatestVersionUseCase(repository: AppLatestVersionRepository())
// TODO: DI 수정
let api = TTProvider<FirebaseAPI>(session: Session(interceptor: NetworkInterceptor.shared))
let repository = FirebaseRepository(api: api)
let getAppVersionUseCase = GetAppVersionUseCase(repository: repository)

getLatestVersionUseCase.getLatestVersion { result in
switch result {
case .success(let latestVersionInfo):
let storeVersion = latestVersionInfo.latestVersion
getAppVersionUseCase.execute()
.sink { [weak self] completion in
if case .failure = completion {
// MARK: 버전 불러오기 실패 Alert 표시
let title = Localized.string(.App_Popup_FetchVersionErrorTitle)
let text = Localized.string(.App_Popup_FetchVersionErrorDesc)
let ok = UIAlertAction(title: Localized.string(.Common_Text_OK), style: .default)
self?.showAlert(title: title, text: text, actions: [ok])
}
} receiveValue: { [weak self] appLatestVersionInfo in
let storeVersion = appLatestVersionInfo.latestVersion
guard storeVersion.compare(String.currentVersion, options: .numeric) == .orderedDescending else { return }

if latestVersionInfo.forced == true {
if appLatestVersionInfo.forced == true {
// MARK: 강제 업데이트 필요 Alert 표시
let title = Localized.string(.Update_Popup_HardUpdateTitle)
let text = Localized.string(.Update_Popup_HardUpdateDesc)
Expand All @@ -124,7 +138,7 @@ extension AppDelegate {
}
}
}
self.showAlert(title: title, text: text, actions: [ok])
self?.showAlert(title: title, text: text, actions: [ok])
} else if isLaunch == true && UserDefaultsManager.get(forKey: .updatePushable) as? Bool ?? true {
// MARK: 업데이트 Alert 표시
let title = Localized.string(.Update_Pupup_SoftUpdateTitle)
Expand All @@ -137,13 +151,10 @@ extension AppDelegate {
UIApplication.shared.open(url, options: [:])
}
}
self.showAlert(title: title, text: text, actions: [pass, update])
self?.showAlert(title: title, text: text, actions: [pass, update])
}

case .failure(let networkError):
print(networkError.alertMessage)
}
}
.store(in: &self.cancellables)
}

private func showAlert(title: String, text: String?, actions: [UIAlertAction]) {
Expand Down Expand Up @@ -217,28 +228,27 @@ extension AppDelegate {
}

private func checkNotification() {
let getNotficationUseCase = GetNotificationUseCase(repository: NotificationRepository())
// TODO: DI 수정
let api = TTProvider<FirebaseAPI>(session: Session(interceptor: NetworkInterceptor.shared))
let repository = FirebaseRepository(api: api)
let getNotificationUseCase = GetNotificationUseCase(repository: repository)
let notificationUseCase = NotificationUseCase()

getNotficationUseCase.getNoti { result in
switch result {
case .success(let noti):
guard let noti = noti else { return }
getNotificationUseCase.execute()
.sink { completion in
if case .failure(let networkError) = completion {
print("ERROR", #function, networkError)
}
} receiveValue: { notificationInfo in
guard let notificationInfo = notificationInfo else { return }
guard notificationUseCase.isShowNotification() else { return }

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let notificationVC = NotificationVC(noti: noti, notificationUseCase: notificationUseCase)
let notificationVC = NotificationVC(noti: notificationInfo, notificationUseCase: notificationUseCase)
SceneDelegate.sharedWindow?.rootViewController?.present(notificationVC, animated: true)
}
case .failure(let networkError):
switch networkError {
case .NOTFOUND(_):
return
default:
print(networkError.alertMessage)
}
}
}
.store(in: &self.cancellables)
}

private func updateToLastestVersion() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// JSONDecoder+Extension.swift
// Project_Timer
//
// Created by Kang Minsang on 2022/12/23.
// Copyright © 2022 FDEE. All rights reserved.
// Created by Kang Minsang on 2024/06/29.
// Copyright © 2024 FDEE. All rights reserved.
//

import Foundation
Expand Down
3 changes: 3 additions & 0 deletions Project_Timer/Core/Localize/TLRen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct TLRen {
case .Common_Popup_Warning: value = "Warning"
case .Common_Popup_Inform: value = "Inform"

case .App_Popup_FetchVersionErrorTitle: value = "Failed to fetch the latest version information"
case .App_Popup_FetchVersionErrorDesc: value = "If the issue persists, please contact support."

case .System_Noti_StopwatchHourPassed: value = "{}hours passed."
case .System_Noti_TimerFinished: value = "Timer finished!"
case .System_Noti_Timer5Left: value = "5 minutes left"
Expand Down
5 changes: 5 additions & 0 deletions Project_Timer/Core/Localize/TLRkey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ enum TLRkey: String {
/// 안내
case Common_Popup_Inform

/// 최신버전 정보를 불러오지 못했어요
case App_Popup_FetchVersionErrorTitle
/// 문제가 계속될 경우 문의 부탁드려요.
case App_Popup_FetchVersionErrorDesc

/// {}시간 경과되었습니다.
case System_Noti_StopwatchHourPassed
/// 타이머가 종료되었습니다!
Expand Down
3 changes: 3 additions & 0 deletions Project_Timer/Core/Localize/TLRko.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct TLRko {
case .Common_Popup_Warning: value = "경고"
case .Common_Popup_Inform: value = "안내"

case .App_Popup_FetchVersionErrorTitle: value = "최신버전 정보를 불러오지 못했어요"
case .App_Popup_FetchVersionErrorDesc: value = "문제가 계속될 경우 문의 부탁드려요."

case .System_Noti_StopwatchHourPassed: value = "{}시간 경과되었습니다."
case .System_Noti_TimerFinished: value = "타이머가 종료되었습니다!"
case .System_Noti_Timer5Left: value = "5분 남았습니다"
Expand Down
3 changes: 3 additions & 0 deletions Project_Timer/Core/Localize/TLRzh.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct TLRzh {
case .Common_Popup_Warning: value = "警告"
case .Common_Popup_Inform: value = "向导"

case .App_Popup_FetchVersionErrorTitle: value = "无法获取最新版本信息。"
case .App_Popup_FetchVersionErrorDesc: value = "如果问题仍然存在,请联系客服。"

case .System_Noti_StopwatchHourPassed: value = "{}时间已过。"
case .System_Noti_TimerFinished: value = "计时器已结束 !"
case .System_Noti_Timer5Left: value = "还剩5分钟"
Expand Down
30 changes: 30 additions & 0 deletions Project_Timer/Data/API/API.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// API.swift
// Project_Timer
//
// Created by Kang Minsang on 2024/04/17.
// Copyright © 2024 FDEE. All rights reserved.
//

import Foundation
import Moya

extension TargetType {
func parameters(from encodable: Encodable) -> [String: Any] {
let encoder = JSONEncoder()
guard let data = try? encoder.encode(encodable),
let dictionary = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
return [:]
}

return dictionary
}
}

extension JSONEncoder {
static var dateFormatted: JSONEncoder {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
return encoder
}
}
27 changes: 0 additions & 27 deletions Project_Timer/Data/API/AppVersionAPI.swift

This file was deleted.

88 changes: 45 additions & 43 deletions Project_Timer/Data/API/AuthAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,65 @@
// AuthAPI.swift
// Project_Timer
//
// Created by Kang Minsang on 2023/12/15.
// Copyright © 2023 FDEE. All rights reserved.
// Created by Kang Minsang on 2024/04/17.
// Copyright © 2024 FDEE. All rights reserved.
//

import Foundation
import Moya

final class AuthAPI {
private let network = Network()
private var signupURL: String {
let base = NetworkURL.shared.serverURL ?? "nil"
return base + "/auth/signup"
}
private var signinURL: String {
let base = NetworkURL.shared.serverURL ?? "nil"
return base + "/auth/login"
}
private var checkUsersURL: String {
let base = NetworkURL.shared.serverURL ?? "nil"
return base + "/auth/users"
}
private var resetPasswordURL: String {
let base = NetworkURL.shared.serverURL ?? "nil"
return base + "/auth/users/password"
}

func signup(signupInfo: TestUserSignupInfo, completion: @escaping (NetworkResult) -> Void) {
self.network.request(url: self.signupURL, method: .post, param: nil, body: signupInfo) { result in
completion(result)
}
enum AuthAPI {
case postSignup(TestUserSignupRequest)
case postSignin(TestUserSigninRequest)
case getCheckUsername(CheckUsernameRequest)
case getCheckEmail(CheckEmailExitRequest)
case postUpdatePassword(UpdatePasswordRequest)
}

extension AuthAPI: TargetType {
var baseURL: URL {
return URL(string: NetworkURL.shared.serverURL ?? "nil")!
}

func signin(signinInfo: TestUserSigninInfo, completion: @escaping (NetworkResult) -> Void) {
self.network.request(url: self.signinURL, method: .post, param: nil, body: signinInfo) { result in
completion(result)
var path: String {
switch self {
case .postSignup:
return "/auth/signup"
case .postSignin:
return "/auth/login"
case .getCheckUsername, .getCheckEmail:
return "/auth/users"
case .postUpdatePassword:
return "/auth/users/password"
Comment on lines +27 to +34
Copy link
Contributor

@sladuf sladuf Oct 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(의견)
AuthAPI로 첫 path명과 동일하게 API 라우터 만들어 주신건 너무 좋은것 같습니다!
개인적인 추가 의견으로 API case와 path를 통일하면 좋겠다는 의견입니다,
API가 많아지다 보면 호출하는곳(사용하는곳)이 아닌 해당 파일의 path까지 와서 확인해야만 어떤 api인지 알 수 있어 이슈 디버깅하는 속도가 느려질것 같아요

회의때 의견 나누면 좋을것 같아서 코멘트 남깁니당

Copy link
Contributor

@sladuf sladuf Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

24.10.28 (iOS 회의 결과)

  • API path에 대한 컨벤션(?)을 백엔드와 논의해보자
  • 우리가 path를 따라서 네이밍을 하고 있다는것 정도만 언급하기

}
}

func checkUsername(username: String, completion: @escaping (NetworkResult) -> Void) {
self.network.request(url: self.checkUsersURL, method: .get, param: [
"username": username
]) { result in
completion(result)
var method: Moya.Method {
switch self {
case .postSignup, .postSignin, .postUpdatePassword:
return .post
case .getCheckUsername, .getCheckEmail:
return .get
}
}

func checkEmail(username: String, email: String, completion: @escaping (NetworkResult) -> Void) {
self.network.request(url: self.checkUsersURL, method: .get, param: [
"username": username,
"email": email
]) { result in
completion(result)
var task: Moya.Task {
switch self {
case .postSignup(let request):
return .requestJSONEncodable(request)
case .postSignin(let request):
return .requestJSONEncodable(request)
case .getCheckUsername(let request):
return .requestParameters(parameters: self.parameters(from: request), encoding: URLEncoding.queryString)
case .getCheckEmail(let request):
return .requestParameters(parameters: self.parameters(from: request), encoding: URLEncoding.queryString)
case .postUpdatePassword(let request):
return .requestJSONEncodable(request)
}
}

func updatePassword(request: ResetPasswordRequest, completion: @escaping (NetworkResult) -> Void) {
self.network.request(url: self.resetPasswordURL, method: .post, body: request) { result in
completion(result)
}
var headers: [String: String]? {
return nil
}
}

Loading