diff --git a/Dietto/Dietto.xcodeproj/project.pbxproj b/Dietto/Dietto.xcodeproj/project.pbxproj index 2fb9184..aa489a2 100644 --- a/Dietto/Dietto.xcodeproj/project.pbxproj +++ b/Dietto/Dietto.xcodeproj/project.pbxproj @@ -14,8 +14,6 @@ 2203D0AC2DD32098000EA331 /* Exceptions for "Dietto" folder in "Dietto" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( - Apps/AppCoordinator.swift, - Apps/Destination.swift, Info.plist, ); target = 22C73C312DD1968A001EDF35 /* Dietto */; diff --git a/Dietto/Dietto/Apps/AppCoordinator.swift b/Dietto/Dietto/Apps/AppCoordinator.swift deleted file mode 100644 index 8401298..0000000 --- a/Dietto/Dietto/Apps/AppCoordinator.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// AppCoordinator.swift -// Dietto -// -// Created by 안세훈 on 5/13/25. -// - -import SwiftUI - -struct AppCoordinator: View { - @StateObject private var coordinator = Coordinator() - private let content: () -> Content - - init(@ViewBuilder content: @escaping () -> Content) { - self.content = content - } - - var body: some View { - NavigationStack(path: $coordinator.paths) { - content() - .frame(maxWidth: .infinity, maxHeight: .infinity) - .navigationDestination(for: Destination.self) { destination in - coordinator.view(for: destination) - } - } - .environmentObject(coordinator) - } -} diff --git a/Dietto/Dietto/Apps/DIContainer.swift b/Dietto/Dietto/Apps/DIContainer.swift new file mode 100644 index 0000000..4b75d1a --- /dev/null +++ b/Dietto/Dietto/Apps/DIContainer.swift @@ -0,0 +1,51 @@ +// +// DIContainer.swift +// Dietto +// +// Created by 안정흠 on 5/29/25. +// + +import Foundation +import Observation +@Observable +final class DIContainer { + private let alanUsecase: AlanUsecase + private let interestsUsecase: InterestsUsecase + private let pedometerUsecase: PedometerUsecase + private let userStorageUsecase: UserStorageUsecase + private let weightHistoryUsecase: WeightHistoryUsecase + + init() { + self.alanUsecase = AlanUsecaseImpl(repository: NetworkRepositoryImpl()) + self.pedometerUsecase = PedometerUsecaseImpl(pedometer: PedometerRepositoryImpl()) + self.interestsUsecase = InterestsUsecaseImpl(repository: StorageRepositoryImpl()) + self.userStorageUsecase = UserStorageUsecaseImpl(storage: StorageRepositoryImpl()) + self.weightHistoryUsecase = WeightHistoryUsecaseImpl(repository: StorageRepositoryImpl()) + } + + func getHomeViewModel() -> HomeViewModel { + HomeViewModel( + pedometerUsecase: pedometerUsecase, + weightHistroyUsecase: weightHistoryUsecase, + userStorageUsecase: userStorageUsecase + ) + } + + func getArticleViewModel() -> ArticleViewModel { + ArticleViewModel( + alanUsecase: alanUsecase, + storageUsecase: interestsUsecase + ) + } + + func getDietaryViewModel() -> DietaryViewModel { + DietaryViewModel(usecase: alanUsecase) + } + + func getOnboardingViewModel() -> OnboardingViewModel { + OnboardingViewModel( + weightHistroyUsecase: weightHistoryUsecase, + userStorageUsecase: userStorageUsecase + ) + } +} diff --git a/Dietto/Dietto/Apps/Destination.swift b/Dietto/Dietto/Apps/Destination.swift deleted file mode 100644 index 1fd044a..0000000 --- a/Dietto/Dietto/Apps/Destination.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// Destination.swift -// Dietto -// -// Created by 안세훈 on 5/13/25. -// - -import SwiftUI -import Combine - -enum Destination: Hashable { - case goLoginView - case goOnboardingView - case goHomeView -} - -final class Coordinator: ObservableObject { - @Published var paths: [T] = [] - - @ViewBuilder - func view(for destination: Destination) -> some View { - switch destination { - case .goLoginView: - LoginView() - case .goOnboardingView: - OnboardingView() - case .goHomeView: - HomeView() - } - } - - func push(_ path: T) { - paths.append(path) - } - - func pop() { - paths.removeLast() - } - - func pop(to: T) { - guard let found = paths.firstIndex(where: { $0 == to }) else { return } - - let numToPop = (found.. [WeightEntity] + func deleteAllWeightHistory() } final class WeightHistoryUsecaseImpl: WeightHistoryUsecase where Repository.T == WeightDTO { @@ -35,6 +36,15 @@ final class WeightHistoryUsecaseImpl: WeightHisto } } + func deleteAllWeightHistory() { + do { + try storage.deleteAll() + } + catch { + print("\(#function) : \(error.localizedDescription)") + } + } + private func getDateRange(range: ChartTimeType) -> Predicate { let now = Date() let calendar = Calendar.current diff --git a/Dietto/Dietto/Presentation/Article/View/ArticleView.swift b/Dietto/Dietto/Presentation/Article/View/ArticleView.swift index 85d25ed..a451ddd 100644 --- a/Dietto/Dietto/Presentation/Article/View/ArticleView.swift +++ b/Dietto/Dietto/Presentation/Article/View/ArticleView.swift @@ -14,7 +14,7 @@ struct ArticleView: View { "https://www.youtube.com/watch?v=FdYIvEc7e-0", "https://www.youtube.com/watch?v=2tM1LFFxeKg" ] - @StateObject var viewModel = ArticleViewModel() + @StateObject var viewModel: ArticleViewModel var body: some View { NavigationView { VStack(alignment: .leading){ @@ -59,7 +59,7 @@ struct ArticleView: View { #Preview { NavigationView { - ArticleView() + ArticleView(viewModel: ArticleViewModel()) } } diff --git a/Dietto/Dietto/Presentation/Article/ViewModel/ArticleViewModel.swift b/Dietto/Dietto/Presentation/Article/ViewModel/ArticleViewModel.swift index 7e7388a..721aa46 100644 --- a/Dietto/Dietto/Presentation/Article/ViewModel/ArticleViewModel.swift +++ b/Dietto/Dietto/Presentation/Article/ViewModel/ArticleViewModel.swift @@ -8,7 +8,8 @@ import SwiftUI final class ArticleViewModel: ObservableObject { - @Published private(set) var selectedInterests: [InterestEntity] = [] + @Published var selectedInterests: [InterestEntity] = [] + @Published var articles: [ArticleEntity] = [] private let alanUsecase: AlanUsecase private let storageUsecase: InterestsUsecase @@ -29,9 +30,17 @@ final class ArticleViewModel: ObservableObject { } // MARK: - 아티클 로드 - func loadArticles() async throws -> [ArticleEntity] { -// alanUsecase.fetchArticle(topics: <#T##[ArticleEntity]#>) - [] + func loadArticles() { + Task { + do { + let result = try await alanUsecase.fetchArticle(topics: selectedInterests) + await MainActor.run{ articles = result } + } + catch { + + } + } + } // MARK: - 관심사 추가 / 삭제 diff --git a/Dietto/Dietto/Presentation/Dietary/View/DietaryView.swift b/Dietto/Dietto/Presentation/Dietary/View/DietaryView.swift index c87588a..fce246b 100644 --- a/Dietto/Dietto/Presentation/Dietary/View/DietaryView.swift +++ b/Dietto/Dietto/Presentation/Dietary/View/DietaryView.swift @@ -9,7 +9,7 @@ import SwiftUI struct DietaryView: View { - @StateObject private var dietartViewModel = DietaryViewModel() + @StateObject var dietartViewModel: DietaryViewModel @State private var newfood : String = "" @@ -205,5 +205,5 @@ struct DietaryView: View { } #Preview { - DietaryView() + DietaryView(dietartViewModel: DietaryViewModel()) } diff --git a/Dietto/Dietto/Presentation/Home/HomeViewModel.swift b/Dietto/Dietto/Presentation/Home/HomeViewModel.swift index 26458e9..54e77f9 100644 --- a/Dietto/Dietto/Presentation/Home/HomeViewModel.swift +++ b/Dietto/Dietto/Presentation/Home/HomeViewModel.swift @@ -28,20 +28,11 @@ enum ChartTimeType: String, CaseIterable { @Observable final class HomeViewModel { var isAnimating: Bool = false - var chartTimeType: ChartTimeType = .monthly + var chartTimeType: ChartTimeType = .weekly var bodyScaleHistory: [WeightEntity] = [] var pedometerData: PedometerModel? - var userData: UserEntity = UserEntity( - id: UUID(), - name: "홍길동", - gender: .male, - height: 170, - startWeight: 68, - currentWeight: 67, - targetWeight: 62, - targetDistance: 5 - ) + var userData: UserEntity private let pedometerUsecase: PedometerUsecase @@ -59,9 +50,12 @@ final class HomeViewModel { self.pedometerUsecase = pedometerUsecase self.weightHistroyUsecase = weightHistroyUsecase self.userStorageUsecase = userStorageUsecase + if let userData = userStorageUsecase.getUserData() { + self.userData = userData +#warning("데이터 없으면 온보딩으로") + } + else { fatalError("데이터 없음") } bodyScaleHistoryFetch(type: chartTimeType) - - userStorageUsecase.createUserData(userData) } func fetchPedometer() { @@ -80,55 +74,30 @@ final class HomeViewModel { } func updateCurrentBodyScale(_ value: String) { - print("Update!!!") - weightHistroyUsecase.addNewWeight(weight: Int(value)!, date: Date()) - userStorageUsecase.updateCurrentWeight(id: userData.id, currentWeight: Int(value)!) + guard let value = Int(value) else { + print("\(#function) : FAILED to update current body scale") + return + } - print("Update: \(weightHistroyUsecase.getWeightHistory(chartRange: .weekly))") - print("Update: \(userStorageUsecase.getUserData()?.currentWeight)") - // 마지막에 bodyScaleHistoryFetch 필요함 + #warning("업데이트 한 날짜가 같으면 기존 데이터 replace") + weightHistroyUsecase.addNewWeight(weight: value, date: Date()) + userStorageUsecase.updateCurrentWeight(id: userData.id, currentWeight: value) + userData.currentWeight = value + bodyScaleHistoryFetch(type: chartTimeType) } func bodyScaleHistoryFetch(type: ChartTimeType) { chartTimeType = type - var result = weightHistroyUsecase.getWeightHistory(chartRange: type) - - if type == .weekly { - result = [ - WeightEntity(date: Date()-(86400*3), scale: 70), - WeightEntity(date: Date()-(86400*2), scale: 65), - WeightEntity(date: Date()-86400, scale: 55), - WeightEntity(date: Date(), scale: 50), - WeightEntity(date: Date()+86400, scale: 55) - ] - } - else if type == .monthly { - result = [ - WeightEntity(date: Date()-(86400*3), scale: 70), - WeightEntity(date: Date()-(86400*2), scale: 65), - WeightEntity(date: Date()-86400, scale: 55), - WeightEntity(date: Date(), scale: 50), - WeightEntity(date: Date()+86400, scale: 55), - WeightEntity(date: Date()+(86400*2), scale: 63), - WeightEntity(date: Date()+(86400*3), scale: 72), - WeightEntity(date: Date()+(86400*4), scale: 82), - WeightEntity(date: Date()+(86400*5), scale: 72), - WeightEntity(date: Date()+(86400*6), scale: 62), - WeightEntity(date: Date()+(86400*7), scale: 52), - WeightEntity(date: Date()+(86400*8), scale: 52), - WeightEntity(date: Date()+(86400*9), scale: 52), - WeightEntity(date: Date()+(86400*10), scale: 52), - WeightEntity(date: Date()+(86400*11), scale: 58) - ] - } + let result = weightHistroyUsecase.getWeightHistory(chartRange: type) if result.count >= type.limitDataCount() { bodyScaleHistory = result + chartAnimate() } - else { bodyScaleHistory = []} + else { bodyScaleHistory = [] } } - func chartAnimate() { + private func chartAnimate() { guard !bodyScaleHistory.isEmpty else { return } isAnimating = true for (index, _) in bodyScaleHistory.enumerated() { @@ -145,9 +114,36 @@ final class HomeViewModel { } } -// ProfileView Function -extension HomeViewModel { - func updateUserData() {} - func updateGoal() {} - func deleteAllData() {} -} + +// result.forEach { item in +// print(item) +// } +// print() +// if type == .weekly { +// result = [ +// WeightEntity(date: Date()-(86400*3), scale: 70), +// WeightEntity(date: Date()-(86400*2), scale: 65), +// WeightEntity(date: Date()-86400, scale: 55), +// WeightEntity(date: Date(), scale: 50), +// WeightEntity(date: Date()+86400, scale: 55) +// ] +// } +// else if type == .monthly { +// result = [ +// WeightEntity(date: Date()-(86400*3), scale: 70), +// WeightEntity(date: Date()-(86400*2), scale: 65), +// WeightEntity(date: Date()-86400, scale: 55), +// WeightEntity(date: Date(), scale: 50), +// WeightEntity(date: Date()+86400, scale: 55), +// WeightEntity(date: Date()+(86400*2), scale: 63), +// WeightEntity(date: Date()+(86400*3), scale: 72), +// WeightEntity(date: Date()+(86400*4), scale: 82), +// WeightEntity(date: Date()+(86400*5), scale: 72), +// WeightEntity(date: Date()+(86400*6), scale: 62), +// WeightEntity(date: Date()+(86400*7), scale: 52), +// WeightEntity(date: Date()+(86400*8), scale: 52), +// WeightEntity(date: Date()+(86400*9), scale: 52), +// WeightEntity(date: Date()+(86400*10), scale: 52), +// WeightEntity(date: Date()+(86400*11), scale: 58) +// ] +// } diff --git a/Dietto/Dietto/Presentation/Home/View/HomeView.swift b/Dietto/Dietto/Presentation/Home/View/HomeView.swift index afd32d8..d90a750 100644 --- a/Dietto/Dietto/Presentation/Home/View/HomeView.swift +++ b/Dietto/Dietto/Presentation/Home/View/HomeView.swift @@ -9,7 +9,7 @@ import SwiftUI import Charts struct HomeView: View { - @State var viewModel = HomeViewModel() + @State var viewModel: HomeViewModel @State var isTapModify: Bool = false var body: some View { VStack(spacing: 0) { @@ -60,7 +60,7 @@ struct HomeView: View { } .sheet(isPresented: $isTapModify, content: { NavigationView { - WeightChangeView(viewModel: $viewModel) + WeightChangeView(viewModel: viewModel) } }) .background(Color.backGround) @@ -71,7 +71,7 @@ struct HomeView: View { } #Preview { - HomeView() + HomeView(viewModel: HomeViewModel()) } diff --git a/Dietto/Dietto/Presentation/Home/View/SubViews/WeightHistoryView.swift b/Dietto/Dietto/Presentation/Home/View/SubViews/WeightHistoryView.swift index b7d902e..8759357 100644 --- a/Dietto/Dietto/Presentation/Home/View/SubViews/WeightHistoryView.swift +++ b/Dietto/Dietto/Presentation/Home/View/SubViews/WeightHistoryView.swift @@ -12,7 +12,7 @@ struct WeightHistoryView: View { @State private var trigger: Bool = false @State private var isAnimated: Bool = false - @State var weightHistory: [WeightEntity] = [ + private let weightHistory: [WeightEntity] = [ WeightEntity(date: Date()-(86400*3), scale: 70), WeightEntity(date: Date()-(86400*2), scale: 65), WeightEntity(date: Date()-86400, scale: 55), @@ -40,7 +40,7 @@ struct WeightHistoryView: View { Button(type.rawValue) { if !viewModel.isAnimating { viewModel.bodyScaleHistoryFetch(type: type) - viewModel.chartAnimate() +// viewModel.chartAnimate() } } } @@ -58,7 +58,7 @@ struct WeightHistoryView: View { Chart(weightHistory, id: \.date) { item in LineMark( x: .value("Date", item.date, unit: .day), - y: .value("Scale", item.isAnimated ? item.scale : 1) + y: .value("Scale", item.isAnimated ? item.scale : 0) ) .symbol(.circle) .foregroundStyle(.red) @@ -90,6 +90,6 @@ struct WeightHistoryView: View { } } .padding() - .onAppear(perform: viewModel.chartAnimate) +// .onAppear(perform: viewModel.chartAnimate) } } diff --git a/Dietto/Dietto/Presentation/Home/View/WeightChangeView.swift b/Dietto/Dietto/Presentation/Home/View/WeightChangeView.swift index 167fb53..d69a5dd 100644 --- a/Dietto/Dietto/Presentation/Home/View/WeightChangeView.swift +++ b/Dietto/Dietto/Presentation/Home/View/WeightChangeView.swift @@ -9,7 +9,7 @@ import SwiftUI struct WeightChangeView: View { @Environment(\.dismiss) private var dismiss - @Binding var viewModel: HomeViewModel + var viewModel: HomeViewModel @State private var value: [String] = [] @State private var shake: CGFloat = 0 @@ -56,7 +56,10 @@ struct WeightChangeView: View { .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("저장") { - guard !value.isEmpty else { return } + guard !value.isEmpty else { + shake += 1 + return + } viewModel.updateCurrentBodyScale(value.joined()) dismiss() } @@ -70,7 +73,7 @@ struct WeightChangeView: View { } @ViewBuilder - func CustomKeypad() -> some View { + private func CustomKeypad() -> some View { LazyVGrid(columns: Array(repeating: GridItem(), count: 3)) { ForEach(1...9, id: \.self) { number in Button { diff --git a/Dietto/Dietto/Presentation/MainTabView.swift b/Dietto/Dietto/Presentation/MainTabView.swift index ad25293..f0b8148 100644 --- a/Dietto/Dietto/Presentation/MainTabView.swift +++ b/Dietto/Dietto/Presentation/MainTabView.swift @@ -8,16 +8,17 @@ import SwiftUI struct MainTabView: View { + @Environment(DIContainer.self) private var diContainer var body: some View { TabView { Tab("홈", systemImage: "house.fill") { - HomeView() + HomeView(viewModel: diContainer.getHomeViewModel()) } Tab("식단", systemImage: "leaf.fill") { - DietaryView() + DietaryView(dietartViewModel: diContainer.getDietaryViewModel()) } Tab("아티클", systemImage: "newspaper") { - ArticleView() + ArticleView(viewModel: diContainer.getArticleViewModel()) } Tab("프로필", systemImage: "person.circle.fill") { ProfileView() diff --git a/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift b/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift index 6b94b16..8fd5955 100644 --- a/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift +++ b/Dietto/Dietto/Presentation/Onboarding/View/GoalView.swift @@ -14,9 +14,6 @@ struct GoalView: View { @ObservedObject var viewModel : OnboardingViewModel - @State private var targetWeight = 60 - @State private var targetDistance = 1 - var body: some View { VStack(spacing: 40) { @@ -37,7 +34,7 @@ struct GoalView: View { .foregroundStyle(.textFieldGray) ContainerView(paddingSize: 20, height: 100) { HStack{ - Picker("", selection: $viewModel.weight) { + Picker("", selection: $viewModel.targetWeight) { ForEach(viewModel.weights, id: \.self) { w in Text("\(w)").tag(w) .font(.pretendardBold20) @@ -60,7 +57,7 @@ struct GoalView: View { .foregroundStyle(.textFieldGray) ContainerView(paddingSize: 20, height: 100) { HStack{ - Picker("", selection: $viewModel.distance) { + Picker("", selection: $viewModel.targetDistance) { ForEach(viewModel.distances, id: \.self) { d in Text("\(d)").tag(d) .font(.pretendardBold20) diff --git a/Dietto/Dietto/Presentation/Onboarding/View/ProfileEditView.swift b/Dietto/Dietto/Presentation/Onboarding/View/ProfileEditView.swift index 86f57fa..232f204 100644 --- a/Dietto/Dietto/Presentation/Onboarding/View/ProfileEditView.swift +++ b/Dietto/Dietto/Presentation/Onboarding/View/ProfileEditView.swift @@ -18,112 +18,113 @@ struct ProfileEditView: View { ZStack { Color(.backGround) .ignoresSafeArea() - - VStack(spacing: 30) { - // MARK: - 프로필 이미지 - ZStack(alignment: .bottomTrailing) { - Button { viewModel.showPhotoSheet.toggle() } label: { - Circle() - .stroke(Color.appMain, lineWidth: 2) - .frame(width: 180, height: 180) - .background( - Image(systemName: "person.crop.circle.fill") - .resizable() - .scaledToFit() - .foregroundColor(.gray) - .frame(width: 180, height: 180) - ) - } - Button { viewModel.showPhotoSheet.toggle() } label: { - Circle() - .fill(Color.black) - .frame(width: 45, height: 45) - .overlay( - Image(systemName: "camera") - .font(.system(size: 20, weight: .bold)) - .foregroundColor(.white) - ) - } - .offset(x: -5, y: -5) - } - - // MARK: - 이름 입력란 - VStack(alignment: .leading, spacing: 6) { - Text("이름") - .font(.pretendardBold16) - TextField("이름을 입력해주세요", text: $viewModel.name) - .font(.pretendardMedium16) - .padding(.horizontal, 20) - .frame(height: 54) - .background( - RoundedRectangle(cornerRadius: 20) + ScrollView { + VStack(spacing: 30) { + // MARK: - 프로필 이미지 + ZStack(alignment: .bottomTrailing) { + Button { viewModel.showPhotoSheet.toggle() } label: { + Circle() .stroke(Color.appMain, lineWidth: 2) - ) - } - - // MARK: - 성별 선택 - VStack(alignment: .leading, spacing: 6) { - Text("성별") - .font(.pretendardBold16) - ZStack { - RoundedRectangle(cornerRadius: 20) - .stroke(Color.appMain, lineWidth: 2) - .frame(height: 54) - - HStack { - Text(viewModel.gender.isEmpty ? "남성" : viewModel.gender) - .font(.pretendardMedium16) - .foregroundColor(viewModel.gender.isEmpty ? .black : .primary) - .padding(.leading, 20) - - Spacer() - - Menu { - Button { viewModel.selectGender("남성") } label: { Text("남성") } - Button { viewModel.selectGender("여성") } label: { Text("여성") } - } label: { - Image(systemName: "chevron.up.chevron.down") - .font(.pretendardBold16) - .foregroundColor(.gray) - .padding(.trailing, 12) - } + .frame(width: 180, height: 180) + .background( + Image(systemName: "person.crop.circle.fill") + .resizable() + .scaledToFit() + .foregroundColor(.gray) + .frame(width: 180, height: 180) + ) } + Button { viewModel.showPhotoSheet.toggle() } label: { + Circle() + .fill(Color.black) + .frame(width: 45, height: 45) + .overlay( + Image(systemName: "camera") + .font(.system(size: 20, weight: .bold)) + .foregroundColor(.white) + ) + } + .offset(x: -5, y: -5) } - } - - // MARK: - 키 수정 - VStack(alignment: .leading, spacing: 6) { - Text("키") - .font(.pretendardBold16) - HStack(spacing: 0) { - TextField("170", text: $numberInput) - .keyboardType(.numberPad) - .multilineTextAlignment(.center) - .frame(height: 54) + + // MARK: - 이름 입력란 + VStack(alignment: .leading, spacing: 6) { + Text("이름") + .font(.pretendardBold16) + TextField("이름을 입력해주세요", text: $viewModel.name) + .font(.pretendardMedium16) .padding(.horizontal, 20) + .frame(height: 54) .background( RoundedRectangle(cornerRadius: 20) .stroke(Color.appMain, lineWidth: 2) ) - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - Spacer() - Button("완료") { - UIApplication.shared.sendAction( - #selector(UIResponder.resignFirstResponder), - to: nil, from: nil, for: nil - ) - } + } + + // MARK: - 성별 선택 + VStack(alignment: .leading, spacing: 6) { + Text("성별") + .font(.pretendardBold16) + ZStack { + RoundedRectangle(cornerRadius: 20) + .stroke(Color.appMain, lineWidth: 2) + .frame(height: 54) + + HStack { + Text(viewModel.gender.rawValue) + .font(.pretendardMedium16) + .foregroundColor(.black) + .padding(.leading, 20) + + Spacer() + + Menu { + Button { viewModel.selectGender(.male) } label: { Text("남성") } + Button { viewModel.selectGender(.female) } label: { Text("여성") } + } label: { + Image(systemName: "chevron.up.chevron.down") + .font(.pretendardBold16) + .foregroundColor(.gray) + .padding(.trailing, 12) } } - Text("cm") - .font(.custom("NerkoOne-regular", size: 50)) - .foregroundColor(Color.appMain) - .padding(.leading, 12) + } } + + // MARK: - 키 수정 + VStack(alignment: .leading, spacing: 6) { + Text("키") + .font(.pretendardBold16) + HStack(spacing: 0) { + TextField("170", text: $numberInput) + .keyboardType(.numberPad) + .multilineTextAlignment(.center) + .frame(height: 54) + .padding(.horizontal, 20) + .background( + RoundedRectangle(cornerRadius: 20) + .stroke(Color.appMain, lineWidth: 2) + ) + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button("완료") { + UIApplication.shared.sendAction( + #selector(UIResponder.resignFirstResponder), + to: nil, from: nil, for: nil + ) + } + } + } + Text("cm") + .font(.custom("NerkoOne-regular", size: 50)) + .foregroundColor(Color.appMain) + .padding(.leading, 12) + } + } + + Spacer() } - - Spacer() } .padding(.top, 48) .padding(.horizontal, 20) diff --git a/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift b/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift index c03c3bf..9f33c93 100644 --- a/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift +++ b/Dietto/Dietto/Presentation/Onboarding/View/TutorialView.swift @@ -9,7 +9,7 @@ import SwiftUI struct TutorialView: View { - @AppStorage("isFirstLaunch") var isFirstLaunch: Bool = true +// @AppStorage("isFirstLaunch") var isFirstLaunch: Bool = true @State private var selection = 0 @StateObject private var viewModel = OnboardingViewModel() @@ -41,7 +41,8 @@ struct TutorialView: View { if selection < 1 { selection += 1 }else{ - isFirstLaunch = false + viewModel.saveProfile() +// isFirstLaunch = false } } label: { Text(selection < 1 ? "다음" : "완료") diff --git a/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift b/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift index 882f467..3969668 100644 --- a/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift +++ b/Dietto/Dietto/Presentation/Onboarding/ViewModel/OnboardingViewModel.swift @@ -8,58 +8,62 @@ import SwiftUI final class OnboardingViewModel: ObservableObject { - + @AppStorage("isFirstLaunch") private var isFirstLaunch: Bool = true @Published var name: String = "name" - @Published var birthString: String = "birthString" - @Published var gender: String = "남성" - @Published var weight: Int = 60 - @Published var distance: Int = 60 + @Published var gender: Gender = .male + @Published var targetWeight: Int = 60 + @Published var targetDistance: Int = 60 + @Published var height: Int = 170 + @Published var weight: Int = 65 + @Published var showPhotoSheet: Bool = false @Published var showDatePicker: Bool = false let weights: [Int] = Array(20...100).reversed() let distances: [Int] = Array(1...10).reversed() + private let userStorageUsecase: UserStorageUsecase + private let weightHistroyUsecase: WeightHistoryUsecase + init( + weightHistroyUsecase: WeightHistoryUsecase = WeightHistoryUsecaseImpl(repository: StorageRepositoryImpl()), + userStorageUsecase: UserStorageUsecase = UserStorageUsecaseImpl(storage: StorageRepositoryImpl()) + ) { + self.weightHistroyUsecase = weightHistroyUsecase + self.userStorageUsecase = userStorageUsecase + } //MARK: - 프로필 설정 func saveProfile() { - print("프로필이 저장되었습니다.") - print("이름: \(name), 생일: \(birthString), 성별: \(gender), 몸무게: \(weight), 거리: \(distance)") + userStorageUsecase.createUserData( + UserEntity(id: UUID(), + name: name, + gender: gender, + height: height, + startWeight: weight, + currentWeight: weight, + targetWeight: targetWeight, + targetDistance: targetDistance) + ) + weightHistroyUsecase.addNewWeight(weight: weight, date: Date()) + isFirstLaunch = false } - func selectGender(_ gender: String) { + func selectGender(_ gender: Gender) { self.gender = gender } - //MARK: - 목표 설정 func setGoals(weight: Int, distance: Int) { - self.weight = weight - self.distance = distance + self.targetWeight = weight + self.targetDistance = distance + } + + func deleteAllUserData() { + userStorageUsecase.deleteUserData() + weightHistroyUsecase.deleteAllWeightHistory() + isFirstLaunch = true } - // MARK: - 관심사 추가 / 삭제 -// -// func addInterest(_ title: String) { -// let entity = ArticleEntity(title: title) -// guard !selectedArticles.contains(where: { $0.title == title }) else { return } -// selectedArticles.append(entity) -// } -// -// func removeInterest(_ title: String) { -// if let index = selectedArticles.firstIndex(where: { $0.title == title }) { -// selectedArticles.remove(at: index) -// } -// } -// -// func toggleInterest(_ title: String) { -// if selectedArticles.contains(where: { $0.title == title }) { -// removeInterest(title) -// } else { -// addInterest(title) -// } -// print(selectedArticles) -// } }