Skip to content

Instantly share code, notes, and snippets.

@iva1ex
Last active August 6, 2020 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save iva1ex/8a2ee0a05916e512a36e95b058afed9f to your computer and use it in GitHub Desktop.
Save iva1ex/8a2ee0a05916e512a36e95b058afed9f to your computer and use it in GitHub Desktop.
// stage 1
import SwiftUI
import WidgetKit
struct WaterAppTimelineProvider: TimelineProvider {
func placeholder(in context: Self.Context) -> WaterAppWidgetEntry {
return .previewData
}
func getSnapshot(in context: Self.Context, completion: @escaping (WaterAppWidgetEntry) -> Void) {
completion(WaterAppWidgetEntry.previewData)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WaterAppWidgetEntry>) -> Void) {
let entry: WaterAppWidgetEntry = .init(
date: Date(),
text: "Hello, widget again")
let timeline: Timeline<WaterAppWidgetEntry> = .init(
entries: [entry],
policy: .never)
completion(timeline)
}
}
struct WaterAppWidgetEntry: TimelineEntry {
let date: Date
let text: String
static let previewData = WaterAppWidgetEntry(
date: Date(),
text: "Widget, hello world!")
}
struct WaterAppWidgetEntryView: View {
let entry: WaterAppWidgetEntry
var body: some View {
ZStack {
Color("BackgroundColor")
Text(entry.text)
.padding()
}
}
}
@main
struct WaterAppWidget: Widget {
let kind = "WaterAppWidget"
var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: WaterAppTimelineProvider()) { entry in
WaterAppWidgetEntryView(entry: entry)
}
.configurationDisplayName("your_water_results")
.description("your_water_results_description")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
struct WaterAppWidget_Previews: PreviewProvider {
static var previews: some View {
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemSmall))
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemSmall))
.redacted(reason: .placeholder)
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
.environment(\.colorScheme, .dark)
}
}
// stage 2
import SwiftUI
import WidgetKit
struct WaterAppTimelineProvider: TimelineProvider {
func placeholder(in context: Self.Context) -> WaterAppWidgetEntry {
return .previewData
}
func getSnapshot(in context: Self.Context, completion: @escaping (WaterAppWidgetEntry) -> Void) {
completion(WaterAppWidgetEntry.previewData)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WaterAppWidgetEntry>) -> Void) {
let date = Date()
guard let userDefaults = UserDefaults.sharedAppUserDefaults else { return }
let todayProgress: Measurement<UnitVolume> = userDefaults.entries.today().totalMeasurementValue
let neededProgress: Measurement<UnitVolume>? = userDefaults.dayGoal?.neededAmount(till: date)
let yesterdayProgress: Measurement<UnitVolume> = userDefaults
.entries
.yesterday()
.totalMeasurementValue(till: date)
let goal: GoalMode? = userDefaults.dayGoal
let entry: WaterAppWidgetEntry = .init(
date: date,
todayProgress: todayProgress,
neededProgress: neededProgress,
yesterdayProgress: yesterdayProgress,
goal: goal)
let timeline: Timeline<WaterAppWidgetEntry> = .init(
entries: [entry],
policy: .never)
completion(timeline)
}
}
struct WaterAppWidgetEntry: TimelineEntry {
let date: Date
let todayProgress: Measurement<UnitVolume>
let neededProgress: Measurement<UnitVolume>?
let yesterdayProgress: Measurement<UnitVolume>
let goal: GoalMode?
static let previewData = WaterAppWidgetEntry(
date: Date(),
todayProgress: .init(value: 1500, unit: .milliliters),
neededProgress: .init(value: 700, unit: .milliliters),
yesterdayProgress: .init(value: 500, unit: .milliliters),
goal: .custom(value: .init(value: 2000, unit: .milliliters)))
}
struct WaterAppWidgetEntryView: View {
let entry: WaterAppWidgetEntry
@Environment (\.widgetFamily) var widgetFamily
var todayTotalProgress: String {
guard let goal = entry.goal else { return "" }
let text = entry.todayProgress.formattedTotalCompleteness(
with: goal.measurementValue,
for: .original,
isShorten: false)
return text
}
var body: some View {
ZStack {
Color("BackgroundColor")
if let goal = entry.goal, let neededProgress = entry.neededProgress {
HStack {
VStack {
ProgressBar(progress: entry.todayProgress.percent(from: goal.measurementValue))
.padding(.bottom, 4)
Spacer()
Text(todayTotalProgress)
}
.padding([.leading, .top, .bottom, .trailing])
if widgetFamily == .systemMedium {
Spacer(minLength: 0)
VStack {
Text("water_results_at_time \(DateFormatter.short.string(from: entry.date))")
.font(.headline)
OwnResultsChartView(
needed: neededProgress,
yesterday: entry.yesterdayProgress,
today: entry.todayProgress,
type: .original)
.fixedSize(horizontal: true, vertical: false)
}
.padding([.bottom, .top, .trailing])
}
}
} else {
Text("set_goal")
.padding()
}
}
}
}
@main
struct WaterAppWidget: Widget {
let kind = "WaterAppWidget"
var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: WaterAppTimelineProvider()) { entry in
WaterAppWidgetEntryView(entry: entry)
}
.configurationDisplayName("your_water_results")
.description("your_water_results_description")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
struct WaterAppWidget_Previews: PreviewProvider {
static var previews: some View {
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemSmall))
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemSmall))
.redacted(reason: .placeholder)
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
.environment(\.colorScheme, .dark)
}
}
// stage 3
import SwiftUI
import WidgetKit
struct LeaderboardTimelineProvider: TimelineProvider {
func placeholder(in context: Self.Context) -> LeaderboardWidgetEntry {
return .previewData
}
func getSnapshot(in context: Self.Context, completion: @escaping (LeaderboardWidgetEntry) -> Void) {
completion(.previewData)
}
func getTimeline(in context: Self.Context, completion: @escaping (Timeline<LeaderboardWidgetEntry>) -> Void) {
guard let userDefaults = UserDefaults.sharedAppUserDefaults else { return }
let date = Date()
let scoreboard: Scoreboard? = {
guard let goal = userDefaults.dayGoal else { return nil }
return .init(
ownEntries: userDefaults.entries.today(),
ownGoal: goal.measurementValue,
friendsResults: userDefaults.friendsResults)
}()
let entry: LeaderboardWidgetEntry = .init(
date: date,
scoreboard: scoreboard)
let timeline: Timeline<LeaderboardWidgetEntry> = .init(
entries: [entry],
policy: .never)
completion(timeline)
}
}
struct LeaderboardWidgetEntry: TimelineEntry {
let date: Date
let scoreboard: Scoreboard?
static let previewData = LeaderboardWidgetEntry(
date: Date(),
scoreboard: previewScoreboard)
}
struct LeaderboardWidgetEntryView: View {
let entry: LeaderboardWidgetEntry
var body: some View {
ZStack {
Color("BackgroundColor")
if let scoreboard = entry.scoreboard {
VStack {
Text("scoreboard_at \(DateFormatter.short.string(from: entry.date))")
.font(.headline)
ScoreboardChartView(entries: scoreboard.entries.sorted())
}
.padding()
} else {
Text("set_goal")
.padding()
}
}
}
}
struct LeaderboardWidget: Widget {
let kind = "LeaderboardWidget"
var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: LeaderboardTimelineProvider()) { entry in
LeaderboardWidgetEntryView(entry: entry)
}
.configurationDisplayName("scoreboard")
.description("see_friends_results")
.supportedFamilies([.systemMedium])
}
}
struct LeaderboardWidget_Previews: PreviewProvider {
static var previews: some View {
LeaderboardWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
LeaderboardWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
.environment(\.colorScheme, .dark)
LeaderboardWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
.redacted(reason: .placeholder)
}
}
// stage 4
import SwiftUI
import WidgetKit
struct WaterAppTimelineProvider: IntentTimelineProvider {
func placeholder(in context: Self.Context) -> WaterAppWidgetEntry {
return .previewData
}
func measurementType(for configuration: WaterMeasurementTypeSelectionIntent) -> MeasurementDisplayType {
switch configuration.measurementType {
case .original: return .original
case .natural: return .natural
default: return .original
}
}
func getSnapshot(
for configuration: WaterMeasurementTypeSelectionIntent,
in context: Self.Context,
completion: @escaping (WaterAppWidgetEntry) -> Void) {
completion(WaterAppWidgetEntry.previewData)
}
func getTimeline(
for configuration: WaterMeasurementTypeSelectionIntent,
in context: Context,
completion: @escaping (Timeline<WaterAppWidgetEntry>) -> Void) {
let date = Date()
guard let userDefaults = UserDefaults.sharedAppUserDefaults else { return }
let todayProgress: Measurement<UnitVolume> = userDefaults.entries.today().totalMeasurementValue
let neededProgress: Measurement<UnitVolume>? = userDefaults.dayGoal?.neededAmount(till: date)
let yesterdayProgress: Measurement<UnitVolume> = userDefaults
.entries
.yesterday()
.totalMeasurementValue(till: date)
let type: MeasurementDisplayType = measurementType(for: configuration)
let score: Float = {
guard let neededProgress = neededProgress else { return 0 }
let optimalOffset: Float = 200.0
let userOffset = neededProgress - todayProgress
let userOffsetValue = Float(userOffset.converted(to: .milliliters).value) - optimalOffset
return max(userOffsetValue, 0)
}()
let goal: GoalMode? = userDefaults.dayGoal
let entry: WaterAppWidgetEntry = .init(
date: date,
todayProgress: todayProgress,
neededProgress: neededProgress,
yesterdayProgress: yesterdayProgress,
goal: goal,
type: type,
relevance: .init(score: score))
let timeline: Timeline<WaterAppWidgetEntry> = .init(
entries: [entry],
policy: .never)
completion(timeline)
}
}
struct WaterAppWidgetEntry: TimelineEntry {
let date: Date
let todayProgress: Measurement<UnitVolume>
let neededProgress: Measurement<UnitVolume>?
let yesterdayProgress: Measurement<UnitVolume>
let goal: GoalMode?
let type: MeasurementDisplayType
let relevance: TimelineEntryRelevance?
static let previewData = WaterAppWidgetEntry(
date: Date(),
todayProgress: .init(value: 1500, unit: .milliliters),
neededProgress: .init(value: 700, unit: .milliliters),
yesterdayProgress: .init(value: 500, unit: .milliliters),
goal: .custom(value: .init(value: 2000, unit: .milliliters)),
type: .original,
relevance: nil)
}
struct WaterAppWidgetEntryView: View {
let entry: WaterAppWidgetEntry
@Environment (\.widgetFamily) var widgetFamily
var todayTotalProgress: String {
guard let goal = entry.goal else { return "" }
let text = entry.todayProgress.formattedTotalCompleteness(
with: goal.measurementValue,
for: entry.type,
isShorten: false)
return text
}
var body: some View {
ZStack {
Color("BackgroundColor")
if let goal = entry.goal, let neededProgress = entry.neededProgress {
HStack {
Link(destination: DeepLinkType.main.url) {
VStack {
ProgressBar(progress: entry.todayProgress.percent(from: goal.measurementValue))
.padding(.bottom, 4)
Spacer()
Text(todayTotalProgress)
}
.padding([.leading, .top, .bottom, .trailing])
}
if widgetFamily == .systemMedium {
Spacer(minLength: 0)
Link(destination: DeepLinkType.history.url) {
VStack {
Text("water_results_at_time \(DateFormatter.short.string(from: entry.date))")
.font(.headline)
OwnResultsChartView(
needed: neededProgress,
yesterday: entry.yesterdayProgress,
today: entry.todayProgress,
type: entry.type)
.fixedSize(horizontal: true, vertical: false)
}
.padding([.bottom, .top, .trailing])
}
}
}
} else {
Text("set_goal")
.padding()
}
}
}
}
struct WaterAppWidget: Widget {
let kind = "WaterAppWidget"
var body: some WidgetConfiguration {
IntentConfiguration(
kind: kind,
intent: WaterMeasurementTypeSelectionIntent.self,
provider: WaterAppTimelineProvider()) { entry in
WaterAppWidgetEntryView(entry: entry)
}
.configurationDisplayName("your_water_results")
.description("your_water_results_description")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
struct WaterAppWidget_Previews: PreviewProvider {
static var previews: some View {
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemSmall))
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemSmall))
.redacted(reason: .placeholder)
WaterAppWidgetEntryView(entry: .previewData)
.previewContext(WidgetPreviewContext(family: .systemMedium))
.environment(\.colorScheme, .dark)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment