Skip to content

Instantly share code, notes, and snippets.

@Segmentational
Created October 8, 2025 01:38
Show Gist options
  • Save Segmentational/c88d4a74d04181da2acc3e62230f1a9c to your computer and use it in GitHub Desktop.
Save Segmentational/c88d4a74d04181da2acc3e62230f1a9c to your computer and use it in GitHub Desktop.
Example-Observations-Usage.swift
import Combine
import OSLog
import Persistables
import SwiftData
import SwiftUI
extension Tags {
public enum Views {}
}
extension Tags.Views {
@MainActor
@Observable
public final class Watcher {
public private(set) var exception: (any Error)? = nil
public var hovering: Bool = false
public private(set) var seeding: Bool = false
public private(set) var task: Task<Result<Void, Error>, Never>? = nil
public private(set) var response: Models.Tags.API.Response? = nil
@Sendable
public nonisolated func observe() -> Task<
Observations<
(
hovering: Bool,
seeding: Bool,
task: Bool,
response: Models.Tags.API.Response?
), Never
>, Never
> {
return Task { @MainActor in
let observations = Observations {
(
hovering: self.hovering,
seeding: self.seeding,
task: self.task != nil,
response: self.response
)
}
return observations
}
}
private static let logger: Logger = Polyium.Logger(String(reflecting: Watcher.self))
func hydrate(_ context: ModelContext) async {
guard task == nil else { return }
seeding = true
defer { seeding = false }
defer { task = nil }
task = Task {
do {
let tags = Models.Tags.API(modelContainer: context.container)
do {
let ctx = ModelContext(context.container)
let external = try await tags.seed(ctx)
// I worked pretty damn hard on the loading button; because the view
// doesn't become readily available ANYWAYS due to... something with @Query,
// might as well show off the button for a little while longer while persistence
// catches up.
try await Task.sleep(for: .seconds(5))
response = external
} catch {
Self.logger.error("Error while seeding: \(error)")
throw error
}
} catch {
return .failure(error)
}
return .success(())
}
switch await task?.value {
case .none, .success:
Self.logger.debug("Task completed successfully without error.")
case .failure(let error):
self.exception = error
Self.logger.error("Task failure. Exception: \(error).")
}
}
nonisolated func cancel() async {
await Self.logger.debug("Running nonisolated version of cancel.")
// Grab handle and clear state on MainActor in one hop.
let handle = await MainActor.run { () -> Task<Result<Void, Error>, Never>? in
let h = self.task
self.task = nil
self.seeding = false
return h
}
handle?.cancel()
}
func cancel() {
Self.logger.debug("Running main actor isolated version of cancel.")
let handle = task
handle?.cancel()
task = nil
seeding = false
}
}
}
struct Hydration: View {
public var ns: Namespace.ID
@Binding var watcher: Tags.Views.Watcher
@Environment(\.modelContext) private var context
private static let logger: Logger = Polyium.Logger(String(reflecting: Self.self))
var body: some View {
Button {
Task {
await watcher.hydrate(context)
}
} label: {
HStack {
if watcher.seeding {
Icon(ns: ns, watcher: $watcher)
.matchedTransitionSource(id: "icon-transition-source", in: ns)
.matchedGeometryEffect(id: "icon-geometry", in: ns)
} else {
Text("Test")
.matchedTransitionSource(id: "title-transition-source", in: ns)
.matchedGeometryEffect(id: "title-geometry", in: ns)
}
}
}
.buttonStyle(.glass)
.buttonBorderShape(.capsule)
.onHover { hovering in
withAnimation {
self.watcher.hovering = hovering
}
}
.contextMenu {
Button(role: .cancel) {
watcher.cancel()
} label: {
Label("Cancel", systemImage: "xmark")
}
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button(role: .destructive) {
Task {
await watcher.cancel()
}
} label: {
Image(systemName: "xmark")
.help(Text("Cancel"))
}
.help("Cancel")
.labelStyle(.iconOnly)
}
.matchedTransitionSource(
id: "button-transition-source", in: ns,
configuration: { configuration in
configuration.background(.adaptive(Colors.purple, alpha: watcher.hovering ? 0.5 : 0.25))
.clipShape(.rect(cornerRadius: 100, style: .circular))
}
)
.task {
let observations = await watcher.observe().value
for await observation in observations {
Self.logger.debug("\(String(describing: observation))")
}
}
.invalidatableContent(true)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment