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
4 changes: 4 additions & 0 deletions Dietto/Dietto.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,14 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Dietto/Info.plist;
INFOPLIST_KEY_NSMotionUsageDescription = "걸음 데이터에 접근하기 위한 권한이 필요합니다.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "";
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UIUserInterfaceStyle = Light;
IPHONEOS_DEPLOYMENT_TARGET = 18.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -305,12 +307,14 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Dietto/Info.plist;
INFOPLIST_KEY_NSMotionUsageDescription = "걸음 데이터에 접근하기 위한 권한이 필요합니다.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "";
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UIUserInterfaceStyle = Light;
IPHONEOS_DEPLOYMENT_TARGET = 18.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down
4 changes: 2 additions & 2 deletions Dietto/Dietto/Apps/DiettoApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import SwiftData

@main
struct DiettoApp: App {
@AppStorage("isFirstLaunch") var isFirstLaunch: Bool = true
@AppStorage("isFirstLaunch") var isFirstLaunch: Bool = false
var body: some Scene {
WindowGroup {
if isFirstLaunch {
IntroView()
TutorialView()
}
else {
MainTabView()
Expand Down
18 changes: 18 additions & 0 deletions Dietto/Dietto/Data/DTO/InterestsDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// InterestsDTO.swift
// Dietto
//
// Created by 안정흠 on 5/28/25.
//

import Foundation
import SwiftData

@Model
final class InterestsDTO {
var title: String

init(title: String) {
self.title = title
}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion Dietto/Dietto/Data/Repository/NetworkRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class NetworkRepositoryImpl: NetworkRepository {
func fetch<T>(promptType: PromptType, rawValues: [Any], outputType: T.Type) async throws -> T where T: Decodable {
let values: [String] = rawValues.compactMap {
if let ing = $0 as? IngredientEntity { return ing.ingredient }
if let art = $0 as? ArticleEntity { return art.title }
if let art = $0 as? InterestEntity { return art.title }
return nil
}
let prompt = promptManager.makePrompt(for: promptType, with: values)
Expand Down
4 changes: 3 additions & 1 deletion Dietto/Dietto/Domain/Entities/ArticleEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ import Foundation
//MARK: - 아티클은 저장합니다.
struct ArticleEntity: Identifiable, Hashable, Decodable{
var id = UUID()
let title: String
let url : URL
let title : String
let description : String
}
12 changes: 12 additions & 0 deletions Dietto/Dietto/Domain/Entities/InterestEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// InterestEntity.swift
// Dietto
//
// Created by 안세훈 on 5/26/25.
//

import Foundation

struct InterestEntity: Hashable {
let title : String
}
5 changes: 3 additions & 2 deletions Dietto/Dietto/Domain/Usecases/AlanUsecase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation

protocol AlanUsecase {
func fetchRecommend(ingredients: [IngredientEntity]) async throws -> [RecommendEntity]
func fetchArticle(topics : [ArticleEntity]) async throws -> [ArticleEntity]
func fetchArticle(topics : [InterestEntity]) async throws -> [ArticleEntity]
}

//MARK: - UsecaseImpl
Expand All @@ -30,7 +30,8 @@ final class AlanUsecaseImpl : AlanUsecase {
return wrapper.recommendation
}

func fetchArticle(topics: [ArticleEntity]) async throws -> [ArticleEntity] {
//아티클
func fetchArticle(topics: [InterestEntity]) async throws -> [ArticleEntity] {
let wrapper = try await repository.fetch(promptType: .article, rawValues: topics, outputType: ArticleDTO.self)

return wrapper.articles
Expand Down
49 changes: 49 additions & 0 deletions Dietto/Dietto/Domain/Usecases/InterestsUsecase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// InterestsUsecaseImpl.swift
// Dietto
//
// Created by 안정흠 on 5/27/25.
//

import Foundation

protocol InterestsUsecase {
func insertInterests(_ interests: InterestEntity)
func deleteInterests(_ interests: InterestEntity)
func fetchInterests() -> [InterestEntity]
}

final class InterestsUsecaseImpl<Repository: StorageRepository>: InterestsUsecase where Repository.T == InterestsDTO {
private let repository: Repository

init(repository: Repository) {
self.repository = repository
}

func insertInterests(_ interests: InterestEntity) {
repository.insertData(data: InterestsDTO(title: interests.title))
}

func deleteInterests(_ interests: InterestEntity) {
do {
let title = interests.title
let predicate = #Predicate<InterestsDTO> { $0.title == title }
try repository.deleteData(where: predicate)
}
catch {
print("\(#function) : \(error.localizedDescription)")
}

}

func fetchInterests() -> [InterestEntity] {
do {
let result = try repository.fetchData(where: nil, sort: [])
return result.map{InterestEntity(title: $0.title)}
}
catch {
print("\(#function) : \(error.localizedDescription)")
return []
}
}
}
2 changes: 0 additions & 2 deletions Dietto/Dietto/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSMotionUsageDescription</key>
<string>걸음 데이터에 접근하기 위한 권한이 필요합니다.</string>
<key>UIAppFonts</key>
<array>
<string>NerkoOne-regular.otf</string>
Expand Down
113 changes: 113 additions & 0 deletions Dietto/Dietto/Presentation/Article/View/ArticleView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// ArticleView.swift
// Dietto
//
// Created by 안세훈 on 5/26/25.
//

import SwiftUI

struct ArticleView: View {
let articles = [
"https://www.youtube.com/watch?v=3p8EBPVZ2Iw",
"https://www.news1.kr/articles/?5301234",
"https://www.youtube.com/watch?v=FdYIvEc7e-0",
"https://www.youtube.com/watch?v=2tM1LFFxeKg"
]
@StateObject var viewModel = ArticleViewModel()
var body: some View {
NavigationView {
VStack(alignment: .leading){
HStack {
//MARK: - 로고
Text("Dietto")
.font(.NerkoOne40)
.foregroundStyle(.text)
Spacer()

NavigationLink(destination: InterestsView(viewModel: viewModel)) {
Text("관심사 수정")
.font(.pretendardMedium16)
.foregroundColor(.white)
.padding(.vertical, 4)
.padding(.horizontal, 12)
.background(Color.appMain)
.clipShape(Capsule())
}
}
.padding([.leading, .trailing], 16)
Text("내 관심사")
.font(.pretendardBold20)
.padding(.leading, 16)
Interests(
topic: "",
titles: viewModel.selectedInterests.map{$0.title},
onClicked: {_ in },
isSelected: {_ in true}
).padding(.leading, 16)

List(articles, id: \.self) { item in
LinkRow(previewURL: URL(string: item)!)
}
.listStyle(.plain)

}
}
.toolbarVisibility(.hidden, for: .navigationBar)
}
}

#Preview {
NavigationView {
ArticleView()
}

}



//
//
//
/*
[
{
"title": "계룡시, 등굣길 금연 캠페인 성황리에 마무리",
"link": "https://www.news1.kr/articles/?5301234"
},
{
"title": "영도구보건소, 금연구역 인증 걷기 챌린지 운영",
"link": "https://www.news2.kr/articles/?5301235"
},
{
"title": "광주시의회, 세계 금연의 날 맞아 금연 결의 대회 개최",
"link": "https://www.news3.kr/articles/?5301236"
},
{
"title": "팔굽혀펴기로 치매 예방하기?...근육량 증가하면 당뇨·치매 위험 낮아져 [Health Recipe]",
"link": "https://www.healthnews.kr/articles/?5301237"
},
{
"title": "노년층 복병 ‘골근감소증’ “장내 미생물 먹었더니”…뼈밀도·근육량 증가↑",
"link": "https://www.healthnews.kr/articles/?5301238"
},
{
"title": "체중 재증가하는 여성은 근육량보다 지방량 증가가 원인",
"link": "https://www.healthnews.kr/articles/?5301239"
},
{
"title": "금연 무기력함 극복하는 꿀팁 3가지를 소개합니다! : 네이버 블로그",
"link": "https://blog.naver.com/PostView.nhn?blogId=example1&logNo=222123456789"
},
{
"title": "근육량 증가, 근성장을 극대화해줄 꿀팁! - 네이버 블로그",
"link": "https://blog.naver.com/PostView.nhn?blogId=example2&logNo=222123456790"
},
{
"title": "지속 가능한 근육량 증가 운동 프로그램 : 네이버 블로그",
"link": "https://blog.naver.com/PostView.nhn?blogId=example3&logNo=222123456791"
}
]


*/
67 changes: 67 additions & 0 deletions Dietto/Dietto/Presentation/Article/View/InterestView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// InterestsView.swift
// Dietto
//
// Created by InTak Han on 5/14/25.
//

import SwiftUI

struct InterestsView: View {

@ObservedObject var viewModel : ArticleViewModel
@Environment(\.dismiss) private var dismiss

var body: some View {
VStack {
VStack(alignment: .leading) {
Text("건강에 관련된\n")
+ Text("관심사").foregroundColor(.appMain)
+ Text("를 알려주세요 !")
}
.font(.pretendardBold24)
.padding(.top, 20)
.padding(.horizontal, 20)
.frame(maxWidth: .infinity, alignment: .leading)

// ScrollView {
// VStack(alignment: .leading, spacing: 30) {
ForEach(viewModel.interestData, id: \.topic) { block in
Interests(
topic: block.topic,
titles: block.titles,
onClicked: { title in
viewModel.toggleInterest(title)
},
isSelected: { title in
viewModel.selectedInterests.contains(where: { $0.title == title })
}
)
}
.padding()
Spacer()
Button {
print("Submit!")
dismiss()
} label: {
Text("저장")
.frame(maxWidth: .infinity)
.padding()
.background(Color.appMain)
.foregroundColor(.white)
.cornerRadius(13)
.font(.pretendardMedium16)
}
.padding()
}
.navigationTitle("관심사 수정")
.toolbarTitleDisplayMode(.inline)
.background(Color.backGround)
}
}

#Preview {
NavigationView {
InterestsView(viewModel: ArticleViewModel())
}
}
42 changes: 42 additions & 0 deletions Dietto/Dietto/Presentation/Article/View/LinkRow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// LinkRow.swift
// Dietto
//
// Created by 안정흠 on 5/27/25.
//


import SwiftUI
import LinkPresentation

struct LinkRow : UIViewRepresentable {

var previewURL:URL

func makeUIView(context: Context) -> LPLinkView {
let view = LPLinkView(url: previewURL)

let provider = LPMetadataProvider()
provider.startFetchingMetadata(for: previewURL) { (metadata, error) in
if let md = metadata {
DispatchQueue.main.async {
view.metadata = md
view.sizeToFit()
}
}
else if error != nil
{
let md = LPLinkMetadata()
md.title = "Custom title"
view.metadata = md
view.sizeToFit()
}
}

return view
}

func updateUIView(_ view: LPLinkView, context: Context) {
// New instance for each update
}
}
Loading