cloud-dns-ios/dns/ViewModel.swift

157 lines
3.7 KiB
Swift
Raw Normal View History

//
// ViewModel.swift
// dns
//
// Created by Benjamin Erhart on 14.04.26.
//
import Foundation
import Combine
import NetworkExtension
import OSLog
class ViewModel: NSObject, ObservableObject {
// MARK: Public Properties
@Published
var blocklist: BlocklistOption = .secure {
didSet {
Settings.blocklist = blocklist
if isDnsEnabled {
toggleDns()
}
else {
isProgrammaticChange = false
}
}
}
@Published
var isDnsEnabled = false {
didSet {
toggleDns()
}
}
@Published
var summaryStatus: SummaryStatus = .pending
// MARK: Private Properties
private var isProgrammaticChange = false
private let manager = NEDNSSettingsManager.shared()
private let log = Logger(subsystem: String(describing: ViewModel.self), category: String(describing: ViewModel.self))
override init() {
super.init()
isProgrammaticChange = true
blocklist = Settings.blocklist
Task {
do {
try await manager.loadFromPreferences()
}
catch {
log.error("Error loading preferences: \(error)")
return
}
if manager.isEnabled, let settings = manager.dnsSettings {
for dnsServer in BlocklistOption.allCases {
if settings.servers.contains(dnsServer.ipv4) {
await MainActor.run {
isProgrammaticChange = true
blocklist = dnsServer
isProgrammaticChange = true
isDnsEnabled = true
}
break
}
}
}
}
Task {
await fetchServerStatus()
}
}
// MARK: Public Methods
func toggleDns() {
guard !isProgrammaticChange else {
// Reset, so next one is recognized as coming from the user again.
isProgrammaticChange = false
return
}
Task {
if isDnsEnabled {
manager.dnsSettings = blocklist.settings
manager.localizedDescription = blocklist.description
do {
try await manager.saveToPreferences()
}
catch {
log.error("Error storing preferences: \(error)")
delayedToggle(false)
}
if !manager.isEnabled {
delayedToggle(false)
}
}
else {
do {
try await manager.removeFromPreferences()
}
catch {
log.error("Error removing preferences: \(error)")
delayedToggle(true)
}
}
}
}
func fetchServerStatus() async {
do {
let (data, _) = try await URLSession.shared.data(for: .init(url: .init(string: "https://status.sr2.uk/index.json")!))
let status = try JSONDecoder().decode(Status.self, from: data)
summaryStatus = status.summaryStatus
}
catch {
log.error("Error while checking status: \(error)")
}
}
// MARK: Private Methods
private func delayedToggle(_ enabled: Bool) {
Task {
try? await Task.sleep(nanoseconds: 500_000_000)
await MainActor.run {
isProgrammaticChange = true
isDnsEnabled = enabled
}
}
}
}