Skip to content
Merged
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
23 changes: 23 additions & 0 deletions Projects/DataSource/Sources/DTO/OnboardingDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// OnboardingDTO.swift
// DataSource
//
// Created by 최정인 on 8/27/25.
//

import Domain

struct OnboardingDTO: Encodable {
let timeSlot: String
let emotionType: [String]
let realOutingFrequency: String?
let targetOutingFrequency: String

func toOnboardingEntity() -> OnboardingEntity {
return OnboardingEntity(
time: timeSlot,
feeling: emotionType,
frequency: realOutingFrequency,
outdoor: targetOutingFrequency)
}
}
21 changes: 21 additions & 0 deletions Projects/DataSource/Sources/DTO/OnboardingResponseDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// OnboardingResponseDTO.swift
// DataSource
//
// Created by 최정인 on 9/1/25.
//

import Domain

struct OnboardingResponseDTO: Decodable {
let timeSlot: String
let emotionTypes: [String]
let targetOutingFrequency: String

func toOnboardingEntity() -> OnboardingEntity {
return OnboardingEntity(
time: timeSlot,
feeling: emotionTypes,
outdoor: targetOutingFrequency)
}
}
9 changes: 7 additions & 2 deletions Projects/DataSource/Sources/DTO/RecommendedRoutineDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct RecommendedRoutineDTO: Decodable {
let routineName: String
let routineDescription: String
let routineLevel: String?
let routineType: String
let routineType: String?
let subRoutines: [RecommendedSubRoutineDTO]

enum CodingKeys: String, CodingKey {
Expand All @@ -32,6 +32,11 @@ extension RecommendedRoutineDTO {
routineCategory = RoutineCategoryType(rawValue: category)
}

var type: RoutineCategoryType?
if let routineType {
type = RoutineCategoryType(rawValue: routineType)
}

var level: RoutineLevelType?
if let routineLevel {
level = RoutineLevelType(rawValue: routineLevel)
Expand All @@ -41,7 +46,7 @@ extension RecommendedRoutineDTO {
title: routineName,
description: routineDescription,
category: routineCategory,
type: RoutineCategoryType(rawValue: routineType) ?? .rest,
type: type,
level: level,
subRoutines: subRoutines.compactMap({ $0.toRecommendedSubRoutineEntity() }))
}
Expand Down
29 changes: 17 additions & 12 deletions Projects/DataSource/Sources/Endpoint/OnboardingEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,32 @@
//

enum OnboardingEndpoint {
case registerOnboarding(choices: [String: String])
case loadOnboardingResult
case registerOnboarding(onboarding: OnboardingDTO)
case registerRecommendedRoutine(selectedRoutines: [Int])
}

extension OnboardingEndpoint: Endpoint {
var baseURL: String {
switch self {
case .registerOnboarding:
return AppProperties.baseURL + "/api/v1/onboardings"
case .registerRecommendedRoutine:
return AppProperties.baseURL + "/api/v2/onboardings"
}
return AppProperties.baseURL + "/api/v2/onboardings"
}

var path: String {
switch self {
case .registerOnboarding: baseURL
case .registerRecommendedRoutine: baseURL + "/routines"
case .registerOnboarding, .loadOnboardingResult:
return baseURL
case .registerRecommendedRoutine:
return baseURL + "/routines"
}
}

var method: HTTPMethod {
return .post
switch self {
case .loadOnboardingResult:
return .get
case .registerOnboarding, .registerRecommendedRoutine:
return .post
}
}

var headers: [String : String] {
Expand All @@ -45,10 +48,12 @@ extension OnboardingEndpoint: Endpoint {

var bodyParameters: [String : Any] {
switch self {
case .registerOnboarding(let choices):
return choices
case .registerOnboarding(let onboarding):
return onboarding.dictionary
case .registerRecommendedRoutine(let selectedRoutines):
return ["recommendedRoutineIds": selectedRoutines]
default:
return [:]
}
}

Expand Down
30 changes: 17 additions & 13 deletions Projects/DataSource/Sources/Repository/OnboardingRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,29 @@ final class OnboardingRepository: OnboardingRepositoryProtocol {
private let networkService = NetworkService.shared
private let userDefaultsStorage = UserDefaultsStorage.shared

func registerOnboarding(onboardingChoices: [String : String]) async throws -> [RecommendedRoutineEntity] {
let endpoint = OnboardingEndpoint.registerOnboarding(choices: onboardingChoices)
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineListResponseDTO.self)
else { return [] }
func loadOnboardingResult() async throws -> OnboardingEntity {
let endpoint = OnboardingEndpoint.loadOnboardingResult
guard let response = try await networkService.request(endpoint: endpoint, type: OnboardingResponseDTO.self)
else { throw UserError.onboardingLoadFailed }

guard userDefaultsStorage.save(true, forKey: UserDefaultsKey.onboarding.rawValue)
else { throw UserError.onboardingSaveFailed }
let onboardingEntity = response.toOnboardingEntity()
return onboardingEntity
}

func registerOnboarding(onboardingEntity: OnboardingEntity) async throws -> [RecommendedRoutineEntity] {
let onboardingDTO = OnboardingDTO(
timeSlot: onboardingEntity.time,
emotionType: onboardingEntity.feeling,
realOutingFrequency: onboardingEntity.frequency,
targetOutingFrequency: onboardingEntity.outdoor)
let endpoint = OnboardingEndpoint.registerOnboarding(onboarding: onboardingDTO)
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineListResponseDTO.self)
else { return [] }

let recommendedRoutineEntity = response.recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity() })
return recommendedRoutineEntity
}

func isOnboardingDone() -> Bool {
guard let isOnboardingDone: Bool = userDefaultsStorage.load(forKey: UserDefaultsKey.onboarding.rawValue)
else { return false }

return isOnboardingDone
}

func registerRecommendedRoutines(selectedRoutines: [Int]) async throws {
let endpoint = OnboardingEndpoint.registerRecommendedRoutine(selectedRoutines: selectedRoutines)
_ = try await networkService.request(endpoint: endpoint, type: EmptyResponseDTO.self)
Expand Down
61 changes: 19 additions & 42 deletions Projects/Domain/Sources/Entity/Enum/OnboardingChoiceType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@
// Created by 최정인 on 7/15/25.
//

public enum OnboardingChoiceType: CaseIterable {
case morningTime
case eveningTime
case allTime

case never
case rarely
case sometimes
case often

case stability
case connection
case growth
case vitality

case once
case twoToThree
case fourOrMore
case notSure
public enum OnboardingChoiceType: String, CaseIterable {
case morningTime = "08:00:00"
case eveningTime = "20:00:00"
case allTime = "00:00:00"

case never = "NEVER"
case rarely = "SHORT"
case sometimes = "SOMETIMES"
case often = "OFTEN"

case stability = "STABILITY"
case connection = "CONNECTEDNESS"
case growth = "GROWTH"
case vitality = "VITALITY"

case once = "ONE_PER_WEEK"
case twoToThree = "TWO_TO_THREE_PER_WEEK"
case fourOrMore = "MORE_THAN_FOUR_PER_WEEK"
case notSure = "UNKNOWN"

public var onboardingType: OnboardingType {
switch self {
Expand All @@ -47,27 +47,4 @@ public enum OnboardingChoiceType: CaseIterable {
case .notSure: .outdoor
}
}

var value: String {
switch self {
case .morningTime: "08:00:00"
case .eveningTime: "20:00:00"
case .allTime: "00:00:00"

case .never: "NEVER"
case .rarely: "SHORT"
case .sometimes: "SOMETIMES"
case .often: "OFTEN"

case .stability: "STABILITY"
case .connection: "CONNECTEDNESS"
case .growth: "GROWTH"
case .vitality: "VITALITY"

case .once: "ONE_PER_WEEK"
case .twoToThree: "TWO_TO_THREE_PER_WEEK"
case .fourOrMore: "MORE_THAN_FOUR_PER_WEEK"
case .notSure: "UNKNOWN"
}
}
}
25 changes: 25 additions & 0 deletions Projects/Domain/Sources/Entity/OnboardingEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// OnboardingEntity.swift
// Domain
//
// Created by 최정인 on 8/27/25.
//

public struct OnboardingEntity {
public let time: String
public let feeling: [String]
public let frequency: String?
public let outdoor: String

public init(
time: String,
feeling: [String],
frequency: String? = nil,
outdoor: String
) {
self.time = time
self.feeling = feeling
self.frequency = frequency
self.outdoor = outdoor
}
}
4 changes: 2 additions & 2 deletions Projects/Domain/Sources/Entity/RecommendedRoutineEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public struct RecommendedRoutineEntity {
public let title: String
public let description: String
public let category: RoutineCategoryType?
public let type: RoutineCategoryType
public let type: RoutineCategoryType?
public let level: RoutineLevelType?
public let subRoutines: [RecommendedSubRoutineEntity]

Expand All @@ -19,7 +19,7 @@ public struct RecommendedRoutineEntity {
title: String,
description: String,
category: RoutineCategoryType?,
type: RoutineCategoryType,
type: RoutineCategoryType?,
level: RoutineLevelType?,
subRoutines: [RecommendedSubRoutineEntity]
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
//

public protocol OnboardingRepositoryProtocol {
/// 이전에 선택했던 온보딩 결과를 불러옵니다.
/// - Returns: 온보딩 선택 결과
func loadOnboardingResult() async throws -> OnboardingEntity

/// 선택한 온보딩 결과를 저장하고, 추천 루틴을 받습니다.
/// - Parameter onboardingChoices: 선택한 온보딩 항목 Dictionary
/// - Parameter onboardingEntity: 선택한 온보딩 항목
/// - Returns: 온보딩 결과를 바탕으로 받은 추천루틴 목록
func registerOnboarding(onboardingChoices: [String: String]) async throws -> [RecommendedRoutineEntity]
func registerOnboarding(onboardingEntity: OnboardingEntity) async throws -> [RecommendedRoutineEntity]

/// 선택한 추천 루틴을 등록합니다.
/// - Parameter selectedRoutines: 선택한 추천 루틴 ID 목록
func registerRecommendedRoutines(selectedRoutines: [Int]) async throws

/// 온보딩 여부를 반환합니다.
/// - Returns: 온보딩 여부
func isOnboardingDone() -> Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import Foundation

public protocol ResultRecommendedRoutineUseCaseProtocol {
/// 선택한 온보딩 결과를 저장하고, 추천 루틴을 받습니다.
/// - Parameter onboardingChoices: 선택한 온보딩 항목 list
/// - Parameter onboardingEntity: 선택한 온보딩 항목
/// - Returns: 온보딩 결과를 바탕으로 받은 추천루틴 목록
func fetchResultRecommendedRoutines(onboardingChoices: [OnboardingChoiceType]) async throws -> [RecommendedRoutineEntity]
func fetchResultRecommendedRoutines(onboardingEntity: OnboardingEntity) async throws -> [RecommendedRoutineEntity]

/// 감정 구슬을 등록하고 그에 따른 추천 루틴 리스트를 받습니다.
/// - Parameter emotion: 감정 구슬 타입 String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ public final class ResultRecommendedRoutineUseCase: ResultRecommendedRoutineUseC
self.emotionRepository = emotionRepository
}

public func fetchResultRecommendedRoutines(onboardingChoices: [OnboardingChoiceType]) async throws -> [RecommendedRoutineEntity] {
let choices = convertToDictionary(onboardingChoices: onboardingChoices)
let recommendedRoutines = try await onboardingRepository.registerOnboarding(onboardingChoices: choices)
public func fetchResultRecommendedRoutines(onboardingEntity: OnboardingEntity) async throws -> [RecommendedRoutineEntity] {
let recommendedRoutines = try await onboardingRepository.registerOnboarding(onboardingEntity: onboardingEntity)
return recommendedRoutines
}

Expand All @@ -29,15 +28,4 @@ public final class ResultRecommendedRoutineUseCase: ResultRecommendedRoutineUseC
public func registerRecommendedRoutines(selectedRoutines: [Int]) async throws {
try await onboardingRepository.registerRecommendedRoutines(selectedRoutines: selectedRoutines)
}

private func convertToDictionary(onboardingChoices: [OnboardingChoiceType]) -> [String: String] {
var result: [String: String] = [:]
let onboardingTypes: [OnboardingType] = [.time, .frequency, .feeling, .outdoor]
for type in onboardingTypes {
guard let choice = onboardingChoices.first(where: { $0.onboardingType == type })
else { break }
result[choice.onboardingType.key] = choice.value
}
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public final class ToastMessageView: UIView {
self.alpha = 0
self.isHidden = true

backgroundColor = BitnagilColor.navy400
backgroundColor = BitnagilColor.gray30
layer.cornerRadius = 8
layer.masksToBounds = true

Expand Down
Loading