Skip to content

Instantly share code, notes, and snippets.

@obito
Created June 8, 2023 00:08
Show Gist options
  • Save obito/2f750bc0df1f1b2faac350315dc88f88 to your computer and use it in GitHub Desktop.
Save obito/2f750bc0df1f1b2faac350315dc88f88 to your computer and use it in GitHub Desktop.
//
// love_letter_widget.swift
// love-letter-widget
//
// Created by youss on 08/06/2023.
//
import WidgetKit
import SwiftUI
import Intents
import Supabase
struct Message: Identifiable, Hashable, Decodable {
let id: Int
let message: String
}
struct MessageEntry: TimelineEntry {
var message: String
var date: Date
}
struct MessageFetcher {
private static var supabase = SupabaseClient(supabaseURL: URL(string: "https://XXX.supabase.co")!, supabaseKey: "XXX")
/// The path where the cached message located
private static var cachePath: URL {
URL.cachesDirectory.appending(path: "message.txt")
}
/// The cached message
static var cachedMessage: String? {
guard let message = try? Data(contentsOf: cachePath) else {
return nil
}
return String(decoding: message, as: UTF8.self)
}
/// Is cached message currently available
static var cachedMessageAvailable: Bool {
cachedMessage != nil
}
static func getMessage() async throws -> Message {
do {
let latestMessage: [Message] = try await supabase.database
.from("messages")
.select()
.order(column: "id", ascending: false)
.limit(count: 1)
.execute()
.value
return latestMessage[0]
} catch {
throw error
}
}
/// Call the Database and then get last message and cache it
static func fetchLastMessage() async throws -> Message {
// Parse the JSON data
let message = try await getMessage()
// Spawn another task to cache the downloaded image
Task {
try? await cache(message.message.data(using: .utf8)!)
}
return message
}
/// Save the message locally
private static func cache(_ messageData: Data) async throws {
try messageData.write(to: cachePath)
}
}
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> MessageEntry {
MessageEntry(message: "Je t'aime", date: Date())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (MessageEntry) -> ()) {
var snapshotMessage: String
if context.isPreview && !MessageFetcher.cachedMessageAvailable {
snapshotMessage = "Je t'aime"
} else {
snapshotMessage = MessageFetcher.cachedMessage!
}
let entry = MessageEntry(message: snapshotMessage, date: Date())
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
Task {
// Fetch a random doggy image from server
guard let message = try? await MessageFetcher.fetchLastMessage() else {
return
}
let entry = MessageEntry(message: message.message, date: Date())
// Next fetch happens 15 minutes later
let nextUpdate = Calendar.current.date(
byAdding: DateComponents(minute: 15),
to: Date()
)!
let timeline = Timeline(
entries: [entry],
policy: .after(nextUpdate)
)
completion(timeline)
}
}
}
struct love_letter_widgetEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.message)
}
}
struct love_letter_widget: Widget {
let kind: String = "love_letter_widget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
love_letter_widgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct love_letter_widget_Previews: PreviewProvider {
static var previews: some View {
love_letter_widgetEntryView(entry: MessageEntry(message: "Je t'aime", date: Date()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment