-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Configurable Widget, OpenURL inside app
- Loading branch information
Showing
7 changed files
with
228 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// Router.swift | ||
// SuperCounter | ||
// | ||
// Created by Nonprawich I. on 26/12/2024. | ||
// | ||
|
||
import Foundation | ||
|
||
@Observable | ||
class Router { | ||
var tallyName: String? | ||
init(tallyName: String? = nil) { | ||
self.tallyName = tallyName | ||
} | ||
} |
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
44 changes: 44 additions & 0 deletions
44
SuperCounterWidget/ConfigurableWidget/ConfigurableUpdateIntent.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,44 @@ | ||
// | ||
// ConfigurableUpdateIntent.swift | ||
// SuperCounterWidgetExtension | ||
// | ||
// Created by Nonprawich I. on 26/12/2024. | ||
// | ||
|
||
import AppIntents | ||
import SwiftData | ||
import WidgetKit | ||
|
||
struct ConfigurableUpdateIntent: AppIntent { | ||
static var title: LocalizedStringResource = LocalizedStringResource("Update first tally") | ||
static var description: IntentDescription? = IntentDescription("Tap the tally once to increment") | ||
|
||
@Parameter(title: "Tally") | ||
var name: String | ||
|
||
init(name: String) { | ||
self.name = name | ||
} | ||
|
||
init() { } | ||
|
||
func perform() async throws -> some IntentResult { | ||
let update = await updateTally(name: name) | ||
return .result(value: update) | ||
} | ||
|
||
@MainActor func updateTally(name: String) -> Int { | ||
let container = try! ModelContainer(for: Tally.self) | ||
let predicate = #Predicate<Tally> { $0.name == name } | ||
let descriptor = FetchDescriptor<Tally>(predicate: predicate) | ||
let foundTallies = try? container.mainContext.fetch(descriptor) | ||
|
||
if let tally = foundTallies?.first { | ||
tally.increase() | ||
try? container.mainContext.save() | ||
WidgetCenter.shared.reloadAllTimelines() | ||
return tally.value | ||
} | ||
return 0 | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
SuperCounterWidget/ConfigurableWidget/ConfigurableWidget.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,100 @@ | ||
// | ||
// Provider.swift | ||
// SuperCounter | ||
// | ||
// Created by Nonprawich I. on 26/12/2024. | ||
// | ||
|
||
|
||
import WidgetKit | ||
import SwiftUI | ||
import SwiftData | ||
|
||
struct ConfigWidgetProvider: AppIntentTimelineProvider { | ||
|
||
var container: ModelContainer = { | ||
try! ModelContainer(for: Tally.self) | ||
}() | ||
|
||
func placeholder(in context: Context) -> ConfigurableEntry { | ||
ConfigurableEntry(date: Date(), configuration: ConfigurationAppIntent(), selectedTally: nil) | ||
} | ||
|
||
func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> ConfigurableEntry { | ||
let selectedTally = try? await getTally(name: configuration.selectedTally?.id) | ||
return ConfigurableEntry(date: Date(), configuration: configuration, selectedTally: selectedTally) | ||
} | ||
|
||
func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline<ConfigurableEntry> { | ||
let currentDate = Date.now | ||
let selectedTally = try? await getTally(name: configuration.selectedTally?.id) | ||
let entry = ConfigurableEntry(date: currentDate, configuration: configuration, selectedTally: selectedTally) | ||
return Timeline(entries: [entry], policy: .atEnd) | ||
} | ||
|
||
@MainActor func getTally(name: String?) throws -> Tally? { | ||
guard let name else { return nil } | ||
let predicate = #Predicate<Tally> { $0.name == name } | ||
let descriptor = FetchDescriptor<Tally>(predicate: predicate) | ||
let foundTallies = try? container.mainContext.fetch(descriptor) | ||
return foundTallies?.first | ||
} | ||
|
||
} | ||
|
||
struct ConfigurableEntry: TimelineEntry { | ||
let date: Date | ||
let configuration: ConfigurationAppIntent | ||
let selectedTally: Tally? | ||
} | ||
|
||
struct ConfigurableWidgetEntryView : View { | ||
var entry: ConfigWidgetProvider.Entry | ||
|
||
var body: some View { | ||
if entry.selectedTally == nil { | ||
ContentUnavailableView("No Tallies yet", systemImage: "plus.circle.fill") | ||
} else { | ||
Link(destination: URL(string: "mtls://tally/\(entry.selectedTally!.name)")!) { | ||
ZStack { | ||
Color.clear | ||
VStack { | ||
Button(intent: ConfigurableUpdateIntent(name: entry.selectedTally!.name)) { | ||
SingleTallyView(size: 50, tally: entry.selectedTally!) | ||
} | ||
|
||
Text(entry.selectedTally!.name) | ||
.font(.caption) | ||
.fontDesign(.rounded) | ||
.bold() | ||
} | ||
.buttonStyle(.plain) | ||
|
||
|
||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct ConfigurableWidget: Widget { | ||
let kind: String = "ConfigurableWidget" | ||
|
||
var body: some WidgetConfiguration { | ||
AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: ConfigWidgetProvider()) { entry in | ||
ConfigurableWidgetEntryView(entry: entry) | ||
.containerBackground(.fill.tertiary, for: .widget) | ||
} | ||
.configurationDisplayName("Tally") | ||
.description("Update your Tally") | ||
.supportedFamilies([.systemSmall]) | ||
.contentMarginsDisabled() | ||
} | ||
} | ||
|
||
|
||
#Preview(as: .systemSmall) { | ||
ConfigurableWidget() | ||
} timeline: { | ||
ConfigurableEntry(date: .now, configuration: ConfigurationAppIntent(), selectedTally: nil) | ||
} |
50 changes: 50 additions & 0 deletions
50
SuperCounterWidget/ConfigurableWidget/ConfigurationAppIntent.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,50 @@ | ||
// | ||
// ConfigurationAppIntent.swift | ||
// SuperCounter | ||
// | ||
// Created by Nonprawich I. on 26/12/2024. | ||
// | ||
|
||
|
||
import WidgetKit | ||
import AppIntents | ||
import SwiftData | ||
|
||
struct ConfigurationAppIntent: WidgetConfigurationIntent { | ||
static var title: LocalizedStringResource { "Selected Tally" } | ||
static var description: IntentDescription { "Choose your tally from the list." } | ||
|
||
// An example configurable parameter. | ||
@Parameter(title: "Select Tally", default: nil) | ||
var selectedTally: TallyEntity? | ||
} | ||
|
||
struct TallyEntity: AppEntity { | ||
var id: String | ||
static var typeDisplayRepresentation: TypeDisplayRepresentation = TypeDisplayRepresentation(name: "Selected Tally") | ||
var displayRepresentation: DisplayRepresentation { | ||
DisplayRepresentation(title: "\(id)") | ||
} | ||
static var defaultQuery = TallyQuery() | ||
} | ||
|
||
struct TallyQuery: EntityQuery { | ||
func entities(for identifiers: [String]) async throws -> [TallyEntity] { | ||
try await suggestedEntities().filter({identifiers.contains($0.id)}) | ||
} | ||
|
||
@MainActor func suggestedEntities() async throws -> [TallyEntity] { | ||
let container = try! ModelContainer(for: Tally.self) | ||
let sort = [SortDescriptor(\Tally.name)] | ||
let descriptor = FetchDescriptor<Tally>(sortBy: sort) | ||
let allTallies = try? container.mainContext.fetch(descriptor) | ||
let allEntities = allTallies?.map({ tally in | ||
TallyEntity(id: tally.name) | ||
}) | ||
return allEntities ?? [] | ||
} | ||
|
||
func defaultResult() async -> TallyEntity? { | ||
try? await suggestedEntities().first | ||
} | ||
} |
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