72 lines
2.7 KiB
Swift
72 lines
2.7 KiB
Swift
|
|
//
|
||
|
|
// CandidatesViewModel.swift
|
||
|
|
// iris
|
||
|
|
//
|
||
|
|
// Created by Codex.
|
||
|
|
//
|
||
|
|
|
||
|
|
import CoreLocation
|
||
|
|
import Foundation
|
||
|
|
import os
|
||
|
|
|
||
|
|
@MainActor
|
||
|
|
final class CandidatesViewModel: ObservableObject {
|
||
|
|
@Published private(set) var candidates: [Candidate] = []
|
||
|
|
@Published private(set) var lastUpdatedAt: Date? = nil
|
||
|
|
@Published private(set) var isLoading = false
|
||
|
|
@Published private(set) var lastError: String? = nil
|
||
|
|
@Published private(set) var diagnostics: [String: String] = [:]
|
||
|
|
|
||
|
|
var demoLatitude: Double = 51.5074
|
||
|
|
var demoLongitude: Double = -0.1278
|
||
|
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "iris", category: "CandidatesViewModel")
|
||
|
|
|
||
|
|
func refresh() {
|
||
|
|
guard !isLoading else { return }
|
||
|
|
isLoading = true
|
||
|
|
lastError = nil
|
||
|
|
diagnostics = [:]
|
||
|
|
|
||
|
|
let location = CLLocation(latitude: demoLatitude, longitude: demoLongitude)
|
||
|
|
let now = Int(Date().timeIntervalSince1970)
|
||
|
|
logger.info("Refresh start lat=\(self.demoLatitude, format: .fixed(precision: 4)) lon=\(self.demoLongitude, format: .fixed(precision: 4)) now=\(now)")
|
||
|
|
|
||
|
|
Task {
|
||
|
|
defer {
|
||
|
|
Task { @MainActor in
|
||
|
|
self.isLoading = false
|
||
|
|
self.lastUpdatedAt = Date()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if #available(iOS 16.0, *) {
|
||
|
|
let ds = WeatherDataSource()
|
||
|
|
let result = await ds.candidatesWithDiagnostics(for: location, now: now)
|
||
|
|
await MainActor.run {
|
||
|
|
self.candidates = result.candidates.sorted { $0.confidence > $1.confidence }
|
||
|
|
self.diagnostics = result.diagnostics
|
||
|
|
if let error = result.weatherKitError {
|
||
|
|
self.lastError = "WeatherKit error: \(error)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if let error = result.weatherKitError {
|
||
|
|
self.logger.error("WeatherKit error: \(error)")
|
||
|
|
}
|
||
|
|
self.logger.info("Produced candidates count=\(result.candidates.count)")
|
||
|
|
for c in result.candidates {
|
||
|
|
self.logger.info("Candidate id=\(c.id, privacy: .public) type=\(c.type.rawValue, privacy: .public) conf=\(c.confidence, format: .fixed(precision: 2)) ttl=\(c.ttlSec) title=\(c.title, privacy: .public)")
|
||
|
|
}
|
||
|
|
if result.candidates.isEmpty {
|
||
|
|
self.logger.info("Diagnostics: \(String(describing: result.diagnostics), privacy: .public)")
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
await MainActor.run {
|
||
|
|
self.candidates = []
|
||
|
|
self.lastError = "WeatherKit requires iOS 16+."
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|