-
Notifications
You must be signed in to change notification settings - Fork 0
Hotfix/#132 챌린지 api 연동 #133
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||||
| // | ||||||||||||||||||||||
| // ChallengeRoutineRequestDTO.swift | ||||||||||||||||||||||
| // Cherrish-iOS | ||||||||||||||||||||||
| // | ||||||||||||||||||||||
| // Created by sumin Kong on 1/19/26. | ||||||||||||||||||||||
| // | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import Foundation | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| struct ChallengeRoutineRequestDTO: Decodable { | ||||||||||||||||||||||
| let id: Int | ||||||||||||||||||||||
| let name: String | ||||||||||||||||||||||
| let description: String | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+10
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DTO 네이밍 검토 필요 이 DTO는 서버 응답을 디코딩하는 용도( ♻️ 네이밍 수정 제안-struct ChallengeRoutineRequestDTO: Decodable {
+struct ChallengeRoutineResponseDTO: Decodable {
let id: Int
let name: String
let description: String
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| extension ChallengeRoutineRequestDTO { | ||||||||||||||||||||||
| func toEntity() -> RoutineEntity { | ||||||||||||||||||||||
| RoutineEntity( | ||||||||||||||||||||||
| id: id, | ||||||||||||||||||||||
| name: name, | ||||||||||||||||||||||
| description: description | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // | ||
| // Encodable+.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/21/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| extension Encodable { | ||
| func toDictionary() throws -> [String: Any] { | ||
| let data = try JSONEncoder().encode(self) | ||
| let json = try JSONSerialization.jsonObject(with: data) | ||
| guard let dictionary = json as? [String: Any] else { | ||
| throw CherrishError.encodingError | ||
| } | ||
| return dictionary | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // | ||
| // MakeChallengeRequestDTO.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/21/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| struct MakeChallengeRequestDTO: Encodable { | ||
| let homecareRoutineId: Int | ||
| let routineNames: [String] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // | ||
| // RecommendMissionsRequestDTO.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/20/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| struct RecommendMissionsRequestDTO: Encodable { | ||
| let homecareRoutineId: Int | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // | ||
| // RecommentMisssionsResponseDTO.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/20/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| struct RecommendMissionsResponseDTO: Decodable { | ||
| let routines: [String] | ||
| } | ||
|
Comment on lines
2
to
12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 타입/파일명 오탈자 정리 권장 🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오타 수정부탁드립니둥 |
||
|
|
||
| extension RecommendMissionsResponseDTO { | ||
| func toEntities() -> [ChallengeMissionEntity] { | ||
| return routines.enumerated().map { index, title in | ||
| ChallengeMissionEntity(id: index, title: title) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,76 @@ | ||||||||||||||||||||||||
| // | ||||||||||||||||||||||||
| // ChallengeAPI.swift | ||||||||||||||||||||||||
| // Cherrish-iOS | ||||||||||||||||||||||||
| // | ||||||||||||||||||||||||
| // Created by sumin Kong on 1/19/26. | ||||||||||||||||||||||||
| // | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import Foundation | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import Alamofire | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 한 줄 띄워주세요 |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| enum ChallengeAPI: EndPoint { | ||||||||||||||||||||||||
| case fetchRoutines | ||||||||||||||||||||||||
| case aiRecommendations(homecareRoutineId: Int) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var basePath: String { | ||||||||||||||||||||||||
| return "/api/challenges" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var path: String { | ||||||||||||||||||||||||
| switch self { | ||||||||||||||||||||||||
| case .fetchRoutines: | ||||||||||||||||||||||||
| return "/homecare-routines" | ||||||||||||||||||||||||
| case .aiRecommendations: | ||||||||||||||||||||||||
| return "/ai-recommendations" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var method: Alamofire.HTTPMethod{ | ||||||||||||||||||||||||
| switch self { | ||||||||||||||||||||||||
| case .fetchRoutines: | ||||||||||||||||||||||||
| return .get | ||||||||||||||||||||||||
| case .aiRecommendations: | ||||||||||||||||||||||||
| return .post | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var headers: HeaderType { | ||||||||||||||||||||||||
| switch self { | ||||||||||||||||||||||||
| case .fetchRoutines: | ||||||||||||||||||||||||
| return .basic | ||||||||||||||||||||||||
| case .aiRecommendations: | ||||||||||||||||||||||||
| return .basic | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+40
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial
모든 케이스에서 동일한 ♻️ 제안하는 수정 var headers: HeaderType {
- switch self {
- case .fetchRoutines:
- return .basic
- case .aiRecommendations:
- return .basic
- }
+ return .basic
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var parameterEncoding: any Alamofire.ParameterEncoding { | ||||||||||||||||||||||||
| switch self { | ||||||||||||||||||||||||
| case .fetchRoutines: | ||||||||||||||||||||||||
| return URLEncoding.default | ||||||||||||||||||||||||
| case .aiRecommendations: | ||||||||||||||||||||||||
| return JSONEncoding.default | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var queryParameters: [String : Any]? { | ||||||||||||||||||||||||
| switch self { | ||||||||||||||||||||||||
| case .fetchRoutines: | ||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||
| case .aiRecommendations: | ||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var bodyParameters: Parameters? { | ||||||||||||||||||||||||
| switch self { | ||||||||||||||||||||||||
| case .fetchRoutines: | ||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||
| case .aiRecommendations(let homecareRoutineId): | ||||||||||||||||||||||||
| return ["homecareRoutineId" : homecareRoutineId] | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요거 extension 만든 거 사용하시면 될 거 가탕요 |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| // | ||
| // ChallengeDemoAPI.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/21/26. | ||
| // | ||
|
|
||
| import Foundation | ||
| import Alamofire | ||
|
|
||
| enum ChallengeDemoAPI: EndPoint { | ||
| case createChallenge(userID: Int, requestDTO: MakeChallengeRequestDTO) | ||
|
|
||
| var basePath: String { | ||
| return "/api/demo" | ||
| } | ||
|
|
||
| var path: String { | ||
| switch self { | ||
| case .createChallenge: | ||
| return "/challenges" | ||
| } | ||
| } | ||
|
|
||
| var method: Alamofire.HTTPMethod{ | ||
| switch self { | ||
| case .createChallenge: | ||
| return .post | ||
| } | ||
| } | ||
|
|
||
|
|
||
| var headers: HeaderType { | ||
| switch self { | ||
| case .createChallenge(let userID, _): | ||
| return .withAuth(userID: userID) | ||
| } | ||
| } | ||
|
|
||
| var parameterEncoding: any Alamofire.ParameterEncoding { | ||
| switch self { | ||
| case .createChallenge: | ||
| return JSONEncoding.default | ||
| } | ||
| } | ||
|
|
||
| var queryParameters: [String : Any]? { | ||
| switch self { | ||
| case .createChallenge: | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| var bodyParameters: Parameters? { | ||
| switch self { | ||
| case .createChallenge(_, let dto): | ||
| return try? dto.toDictionary() | ||
| } | ||
| } | ||
|
Comment on lines
+54
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인코딩 실패 시 nil을 반환하여 디버깅이 어려울 수 있습니다.
🔧 수정 제안옵션 1: 에러 로깅 추가 var bodyParameters: Parameters? {
switch self {
case .createChallenge(_, let dto):
- return try? dto.toDictionary()
+ do {
+ return try dto.toDictionary()
+ } catch {
+ CherrishLogger.error(error)
+ return nil
+ }
}
}옵션 2: EndPoint 프로토콜이 throwing을 지원한다면 에러를 전파하는 것이 더 좋습니다. 🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @y-eonee 요고 확인 부탁드려요 |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // | ||
| // ChallengeRepository.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/19/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| import Alamofire | ||
|
|
||
| struct DefaultChallengeRepository: ChallengeInterface { | ||
| private let networkService: NetworkService | ||
| private let userDefaultService: UserDefaultService | ||
|
|
||
| init(networkService: NetworkService, userDefaultService: UserDefaultService) { | ||
| self.networkService = networkService | ||
| self.userDefaultService = userDefaultService | ||
| } | ||
|
|
||
| func fetchHomecareRoutines() async throws -> [RoutineEntity] { | ||
| let response = try await networkService.request(ChallengeAPI.fetchRoutines, decodingType: [ChallengeRoutineRequestDTO].self) | ||
|
|
||
| return response.map { $0.toEntity() } | ||
| } | ||
|
|
||
| func aiRecommendations(id: Int) async throws -> [ChallengeMissionEntity] { | ||
| let response = try await | ||
| networkService.request(ChallengeAPI.aiRecommendations(homecareRoutineId: id), | ||
| decodingType: RecommendMissionsResponseDTO.self) | ||
| CherrishLogger.debug(response) | ||
| return response.toEntities() | ||
| } | ||
|
|
||
| func createChallenge(missionIds: Int, routineNames: [String]) async throws { | ||
| let userID: Int = userDefaultService.load(key: .userID) ?? 1 | ||
| let response: () = try await networkService.request( | ||
| ChallengeDemoAPI.createChallenge(userID: userID, requestDTO: | ||
| .init( | ||
| homecareRoutineId: missionIds, | ||
| routineNames: routineNames | ||
| ) | ||
| ) | ||
| ) | ||
| } | ||
|
Comment on lines
+35
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사용하지 않는 변수와 userID 폴백 값 검토 필요 두 가지 이슈가 있습니다:
🔧 제안하는 수정 func createChallenge(missionIds: Int, routineNames: [String]) async throws {
- let userID: Int = userDefaultService.load(key: .userID) ?? 1
- let response: () = try await networkService.request(
+ guard let userID: Int = userDefaultService.load(key: .userID) else {
+ throw ChallengeError.userNotAuthenticated
+ }
+ try await networkService.request(
ChallengeDemoAPI.createChallenge(userID: userID, requestDTO:
.init(
homecareRoutineId: missionIds,
routineNames: routineNames
)
)
)
}🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,5 +92,22 @@ final class DomainDependencyAssembler: DependencyAssembler { | |
| DIContainer.shared .register(type: CreateUserProcedureUseCase.self) { | ||
| return DefaultCreateUserProcedureUseCase(repository: treatmentRepository) | ||
| } | ||
|
|
||
| guard let challengeRepository = DIContainer.shared.resolve(type: ChallengeInterface.self) else { | ||
| return | ||
| } | ||
|
|
||
| DIContainer.shared.register(type: FetchChllengeHomecareRoutinesUseCase.self) { | ||
| return DefaultFetchChallengeHomecareRoutinesUseCase(repository: challengeRepository) | ||
| } | ||
|
Comment on lines
+100
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial UseCase 이름에 오타 존재: "Chllenge" → "Challenge"
🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확인해주세요!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오타 수정 해주세요 |
||
|
|
||
|
|
||
| DIContainer.shared.register(type: PostChallengeRecommendUseCase.self) { | ||
| return DefaultSubmitChallengRecommendUseCase(repository: challengeRepository) | ||
| } | ||
|
|
||
| DIContainer.shared.register(type: CreateChallengeUseCase.self) { | ||
| return DefaultCreateChallengeUseCase(repository: challengeRepository) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // | ||
| // ChallengeInterface.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/19/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| protocol ChallengeInterface { | ||
| func fetchHomecareRoutines() async throws -> [RoutineEntity] | ||
| func aiRecommendations(id: Int) async throws -> [ChallengeMissionEntity] | ||
| func createChallenge(missionIds: Int, routineNames: [String]) async throws | ||
| } | ||
|
Comment on lines
+10
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 파라미터 명명이 타입과 일치하지 않음
명확성을 위해 파라미터 이름을 실제 의미에 맞게 수정하는 것을 권장합니다: protocol ChallengeInterface {
func fetchHomecareRoutines() async throws -> [RoutineEntity]
func aiRecommendations(id: Int) async throws -> [ChallengeMissionEntity]
- func createChallenge(missionIds: Int, routineNames: [String]) async throws
+ func createChallenge(homecareRoutineId: Int, routineNames: [String]) async throws
}🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // | ||
| // ChallengeEntity.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/21/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| struct ChallengeEntity: Identifiable { | ||
| let id: Int | ||
| let routineNames: [String] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // | ||
| // ChallengeMissionEntity.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/20/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| struct ChallengeMissionEntity: Identifiable, Equatable, Hashable { | ||
| let id: Int | ||
| let title: String | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // | ||
| // RoutineEntity.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/19/26. | ||
| // | ||
|
|
||
| struct RoutineEntity: Identifiable, Equatable { | ||
| let id: Int | ||
| let name: String | ||
| let description: String | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // | ||
| // CreateChallengeUseCase.swift | ||
| // Cherrish-iOS | ||
| // | ||
| // Created by sumin Kong on 1/21/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| protocol CreateChallengeUseCase { | ||
| func execute(id: Int, routines: [String]) async throws | ||
| } | ||
|
|
||
| struct DefaultCreateChallengeUseCase: CreateChallengeUseCase { | ||
| private let repository: ChallengeInterface | ||
|
|
||
| init(repository: ChallengeInterface) { | ||
| self.repository = repository | ||
| } | ||
|
|
||
| func execute(id: Int, routines: [String]) async throws { | ||
| _ = try await repository.createChallenge(missionIds: id, routineNames: routines) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Repository 네이밍 컨벤션 불일치
다른 Repository들은
DefaultCalendarRepository,DefaultHomeRepository,DefaultOnboardingRepository등Default접두사를 사용하고 있지만,ChallengeRepository는 이 컨벤션을 따르지 않습니다.일관성을 위해
DefaultChallengeRepository로 이름을 변경하는 것을 권장합니다.DIContainer.shared.register(type: ChallengeInterface.self) { - return ChallengeRepository( + return DefaultChallengeRepository( networkService: self.networkService, userDefaultService: self.userDefaultService) }🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요고 네이민 수정 부탁드려용