-
Notifications
You must be signed in to change notification settings - Fork 2
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
feat: Create meal widget #116
Merged
Merged
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
d3f049b
feat: Create Widget module
hhhello0507 85efe4d
fix: Widget module target setting
hhhello0507 d42a31c
feat: Create DodamMealWidget
hhhello0507 4e31750
fix: Meal -> MealResponse
hhhello0507 b06e59a
fix: Module dependency
hhhello0507 6cceea0
feat: Fetch Meal Widget
hhhello0507 3a46d7e
Merge branch 'develop' into feature/widget
hhhello0507 f2ab092
Merge branch 'develop' into feature/widget
hhhello0507 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// | ||
// AppMainWidget.swift | ||
// ProjectDescriptionHelpers | ||
// | ||
// Created by hhhello0507 on 7/23/24. | ||
// | ||
|
||
import Foundation | ||
import WidgetKit | ||
import SwiftUI | ||
import DDS | ||
import DIContainer | ||
|
||
@main | ||
struct AppMainWidget: WidgetBundle { | ||
|
||
init() { | ||
Pretendard.register() | ||
DependencyProvider.shared.register() | ||
} | ||
|
||
var body: some Widget { | ||
DodamMealWidget() | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
Projects/App/iOS-Widget/Source/DI/Assembly/DataSourceAssembly.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// DataSourceAssembly.swift | ||
// DodamDodam | ||
// | ||
// Created by Mercen on 3/28/24. | ||
// | ||
|
||
import Swinject | ||
import DataSource | ||
import Network | ||
import Repository | ||
|
||
struct DataSourceAssembly: Assembly { | ||
|
||
func assemble(container: Container) { | ||
container.register(MealDataSource.self) { | ||
.init(remote: $0.resolve(MealRemote.self)!) | ||
}.inObjectScope(.container) | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
Projects/App/iOS-Widget/Source/DI/Assembly/RemoteAssembly.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// | ||
// RemoteAssembly.swift | ||
// DodamDodam | ||
// | ||
// Created by Mercen on 3/28/24. | ||
// | ||
|
||
import Swinject | ||
import Network | ||
|
||
struct RemoteAssembly: Assembly { | ||
|
||
func assemble(container: Container) { | ||
container.register(MealRemote.self) { _ in | ||
.init() | ||
}.inObjectScope(.container) | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
Projects/App/iOS-Widget/Source/DI/Assembly/RepositoryAssembly.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// RepositoryAssembly.swift | ||
// DodamDodam | ||
// | ||
// Created by Mercen on 3/28/24. | ||
// | ||
|
||
import Swinject | ||
import Repository | ||
import DataSource | ||
import Domain | ||
|
||
struct RepositoryAssembly: Assembly { | ||
|
||
func assemble(container: Container) { | ||
container.register((any MealRepository).self) { | ||
MealRepositoryImpl(dataSource: $0.resolve(MealDataSource.self)!) | ||
}.inObjectScope(.container) | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
Projects/App/iOS-Widget/Source/DI/Provider/DependencyProviderExt.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// | ||
// DependencyProvider.swift | ||
// DodamDodam | ||
// | ||
// Created by Mercen on 3/13/24. | ||
// | ||
|
||
import Swinject | ||
import DIContainer | ||
|
||
public extension DependencyProvider { | ||
|
||
func register() { | ||
Container.loggingFunction = nil | ||
_ = Assembler( | ||
[ | ||
DataSourceAssembly(), | ||
RemoteAssembly(), | ||
RepositoryAssembly() | ||
], | ||
container: self.container | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// | ||
// MealEntry.swift | ||
// DodamDodam | ||
// | ||
// Created by hhhello0507 on 7/23/24. | ||
// | ||
|
||
import WidgetKit | ||
import Domain | ||
|
||
struct MealEntry: TimelineEntry { | ||
let date: Date | ||
let meal: MealResponse | ||
} | ||
|
||
extension MealEntry { | ||
static let empty = MealEntry( | ||
date: .now, | ||
meal: MealResponse( | ||
exists: true, | ||
date: .now, | ||
breakfast: nil, | ||
lunch: nil, | ||
dinner: nil | ||
) | ||
) | ||
} |
90 changes: 90 additions & 0 deletions
90
Projects/App/iOS-Widget/Source/Provider/MealProvider.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// | ||
// DodamMealProvider.swift | ||
// DodamDodam | ||
// | ||
// Created by hhhello0507 on 7/23/24. | ||
// | ||
|
||
import WidgetKit | ||
import Domain | ||
import Shared | ||
import DIContainer | ||
|
||
struct MealProvider: TimelineProvider { | ||
|
||
@Inject var mealRepository: any MealRepository | ||
func placeholder(in context: Context) -> MealEntry { | ||
|
||
let meal = Meal( | ||
details: [ | ||
.init(name: "퀴노아녹두죽", allergies: []), | ||
.init(name: "채소샐러드", allergies: []), | ||
.init(name: "우자드레싱", allergies: []), | ||
.init(name: "깍두기", allergies: []), | ||
.init(name: "초코첵스시리얼+우ㅁㅁㅁㅁㅁ유", allergies: []), | ||
.init(name: "초코크로와상", allergies: []) | ||
], | ||
calorie: 941 | ||
) | ||
let entry = MealEntry( | ||
date: .now, | ||
meal: MealResponse( | ||
exists: true, | ||
date: .now, | ||
breakfast: meal, | ||
lunch: meal, | ||
dinner: meal | ||
) | ||
) | ||
return entry | ||
} | ||
|
||
func getSnapshot(in context: Context, completion: @escaping (MealEntry) -> Void) { | ||
Task { | ||
var currentDate = Date.now | ||
if getDate(.hour, date: currentDate) >= 20 { | ||
currentDate = Calendar.current.date(byAdding: .day, value: 1, to: currentDate)! | ||
} | ||
do { | ||
let year = getDate(.year, date: currentDate) | ||
let month = getDate(.month, date: currentDate) | ||
let day = getDate(.day, date: currentDate) | ||
let request = FetchMealRequest(year: year, month: month, day: day) | ||
let meal = try await mealRepository.fetchMeal(request) | ||
let entry = MealEntry( | ||
date: currentDate, | ||
meal: meal | ||
) | ||
completion(entry) | ||
} catch { | ||
let entry = MealEntry.empty | ||
completion(entry) | ||
} | ||
} | ||
} | ||
|
||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) { | ||
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? .init() | ||
Task { | ||
var currentDate = Date() | ||
// 오후 8시가 지나면 다음날로 | ||
if getDate(.hour, date: currentDate) >= 20 { | ||
currentDate = Calendar.current.date(byAdding: .day, value: 1, to: currentDate)! | ||
} | ||
|
||
do { | ||
let meal = try await mealRepository.fetchMeal(.init(year: getDate(.year, date: currentDate), month: getDate(.month, date: currentDate), day: getDate(.day, date: currentDate))) | ||
let entry = MealEntry( | ||
date: currentDate, | ||
meal: meal | ||
) | ||
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate)) | ||
completion(timeline) | ||
} catch { | ||
let entry = MealEntry.empty | ||
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate)) | ||
completion(timeline) | ||
} | ||
} | ||
} | ||
} |
129 changes: 129 additions & 0 deletions
129
Projects/App/iOS-Widget/Source/Widget/DodamMealWidget.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// | ||
// DodamMealWidget.swift | ||
// DodamDodam | ||
// | ||
// Created by hhhello0507 on 7/23/24. | ||
// | ||
|
||
import SwiftUI | ||
import WidgetKit | ||
import Shared | ||
import DDS | ||
import Domain | ||
|
||
struct DodamMealWidget: Widget { | ||
|
||
@Environment(\.widgetFamily) private var widgetFamily | ||
|
||
private let widgetFamilyList: [WidgetFamily] = if #available(iOSApplicationExtension 16.0, *) { | ||
[.systemSmall, .systemMedium, .accessoryRectangular, .accessoryCircular] | ||
} else { | ||
[.systemSmall, .systemMedium] | ||
} | ||
|
||
var body: some WidgetConfiguration { | ||
StaticConfiguration( | ||
kind: "DodamMealWidget", | ||
provider: MealProvider() | ||
) { entry in | ||
SmallDodamMealWidget(entry: entry) | ||
} | ||
.configurationDisplayName("급식") | ||
.description("아침, 점심, 저녁 위젯으로 빠르고 쉽게 확인해요") | ||
.contentMarginsDisabled() | ||
.supportedFamilies(widgetFamilyList) | ||
} | ||
} | ||
|
||
struct SmallDodamMealWidget: View { | ||
|
||
@State private var selection = 0 | ||
|
||
private let entry: MealProvider.Entry | ||
|
||
init(entry: MealProvider.Entry) { | ||
self.entry = entry | ||
Pretendard.register() | ||
} | ||
|
||
var body: some View { | ||
Group { | ||
if #available(iOSApplicationExtension 17.0, *) { | ||
label(meal: entry.meal) | ||
.containerBackground(for: .widget) { | ||
Dodam.color(DodamColor.Background.neutral) | ||
} | ||
} else { | ||
label(meal: entry.meal) | ||
} | ||
} | ||
.padding(8) | ||
} | ||
|
||
@ViewBuilder | ||
private func label(meal: MealResponse) -> some View { | ||
let idx = switch (getDate(.hour, date: .now), getDate(.minute, date: .now)) { | ||
// 아침: ~ 8:20 | ||
case (0...8, _), (8, ..<20): 0 | ||
// 점심: 8:21 ~ 13:30 | ||
case (8, 21...60), (8...13, _), (13, 0..<30): 1 | ||
// 저녁: 13:31 ~ 19:10 | ||
case (13, 0...30), (13...19, _), (19, 0..<10): 2 | ||
default: -1 | ||
} | ||
let (tag, meal): (String, Meal?) = switch idx { | ||
case 0: ("아침", meal.breakfast) | ||
case 1: ("점심", meal.lunch) | ||
case 2: ("저녁", meal.dinner) | ||
default: ("", nil) | ||
} | ||
content(tag: tag, meal: meal) | ||
} | ||
|
||
@ViewBuilder | ||
private func content(tag: String, meal: Meal?) -> some View { | ||
VStack(spacing: 4) { | ||
HStack { | ||
Text(tag) | ||
.foreground(DodamColor.Static.white) | ||
.padding(.horizontal, 10) | ||
.padding(.vertical, 4) | ||
.background(DodamColor.Primary.normal) | ||
.clipShape(.large) | ||
.font(.footnote) | ||
Spacer() | ||
if let meal { | ||
Text("\(Int(meal.calorie.rounded()))Kcal") | ||
.font(.caption) | ||
.foreground(DodamColor.Label.alternative) | ||
} | ||
} | ||
VStack(alignment: .leading, spacing: 0) { | ||
if let meal { | ||
ForEach(meal.details, id: \.self) { | ||
Text($0.name) | ||
.lineLimit(1) | ||
.truncationMode(.tail) | ||
.font(.caption) | ||
.foreground(DodamColor.Label.normal) | ||
.frame(maxWidth: .infinity, alignment: .leading) | ||
} | ||
} else { | ||
Text("급식이 없어요") | ||
.caption1(.medium) | ||
.foreground(DodamColor.Label.normal) | ||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) | ||
} | ||
} | ||
.padding(8) | ||
.frame(maxHeight: .infinity) | ||
.background(DodamColor.Background.normal) | ||
.clipShape(.large) | ||
} | ||
.background(DodamColor.Background.neutral) | ||
} | ||
} | ||
|
||
#Preview { | ||
SmallDodamMealWidget(entry: .empty) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
코드 패치에 대한 간단한 리뷰입니다:
의존성 중복:
Repository
와DIContainer
가 기존 의존성과 새로운 타겟 모두에 추가되었습니다. 각 타겟에서 필요 유무를 검토하여 중복을 제거하는 것이 좋습니다.배포 대상: iOS 15.0에 배포하도록 설정한 것은 좋은 접근이지만, 사용자 기반에 따라 이전 버전과의 호환성을 고려할 필요가 있습니다.
스크립트 지정:
.swiftLint
스크립트를 추가하였는데, 이를 통해 코드 일관성을 유지할 수 있습니다. 그러나 해당 스크립트 실행이 실패할 경우 빌드 오류가 발생할 수 있으므로, 적절한 환경설정을 필요합니다.잘못된 정보 플리스트 키:
infoPlist
의"NSExtensionPointIdentifier"
형식은 맞으나 값이 올바른지 확인하고, 추가적인 필수가 필요한지 검토해야 합니다.리소스 및 소스 경로:
"iOS-Widget/Source/**"
및"iOS-Widget/Resource/**"
경로가 존재하는지 확인하고, 확장자로 필터링이 제대로 이루어지는지도 체크해야 합니다.제안: