-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #116 from Team-B1ND/feature/widget
feat: Create meal widget
- Loading branch information
Showing
12 changed files
with
404 additions
and
15 deletions.
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.