Compare commits

..

5 commits

6 changed files with 121 additions and 28 deletions

View file

@ -269,7 +269,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -305,7 +305,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",

View file

@ -6,6 +6,7 @@
// //
import Foundation import Foundation
import NetworkExtension
enum BlocklistOption: String, CaseIterable, Identifiable { enum BlocklistOption: String, CaseIterable, Identifiable {
case secure = "Secure" case secure = "Secure"
@ -66,4 +67,12 @@ enum BlocklistOption: String, CaseIterable, Identifiable {
return "2001:db8::1" return "2001:db8::1"
} }
} }
var settings: NEDNSOverHTTPSSettings {
let settings = NEDNSOverHTTPSSettings(servers: [ipv4, ipv6])
settings.serverURL = URL(string: "https://\(server)/dns-query")
settings.matchDomains = [""]
return settings
}
} }

View file

@ -48,7 +48,7 @@ struct HomeView: View {
private let privacyPolicyURL = URL(string: "https://www.sr2.uk/privacy")! private let privacyPolicyURL = URL(string: "https://www.sr2.uk/privacy")!
var body: some View { var body: some View {
NavigationStack { NavigationCompat {
List { List {
// Main toggle section // Main toggle section

View file

@ -0,0 +1,32 @@
//
// NavigationCompat.swift
// dns
//
// Created by Benjamin Erhart on 15.04.26.
//
import SwiftUI
struct NavigationCompat<Content: View>: View {
let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
if #available(iOS 16.0, macOS 13.0, *) {
NavigationStack {
content()
}
}
else {
NavigationView {
content()
}
#if !os(macOS)
.navigationViewStyle(StackNavigationViewStyle())
#endif
}
}
}

24
dns/Settings.swift Normal file
View file

@ -0,0 +1,24 @@
//
// Settings.swift
// dns
//
// Created by Benjamin Erhart on 15.04.26.
//
import Foundation
class Settings {
private static let blocklistKey = "blocklist"
private static let defaults = UserDefaults.standard
class var blocklist: BlocklistOption {
get {
BlocklistOption(rawValue: defaults.string(forKey: blocklistKey) ?? BlocklistOption.secure.rawValue) ?? .secure
}
set {
defaults.set(newValue.rawValue, forKey: blocklistKey)
}
}
}

View file

@ -12,24 +12,27 @@ import OSLog
class ViewModel: NSObject, ObservableObject { class ViewModel: NSObject, ObservableObject {
// TODO: Store this in UserDefaults
@Published @Published
var blocklist: BlocklistOption = .secure var blocklist: BlocklistOption = .secure {
// TODO: Store this in UserDefaults
@Published
var isDnsEnabled = false {
didSet { didSet {
if !isProgrammaticChange { Settings.blocklist = blocklist
if isDnsEnabled {
toggleDns() toggleDns()
} }
else { else {
// Reset, so next one is recognized as coming from the user again.
isProgrammaticChange = false isProgrammaticChange = false
} }
} }
} }
@Published
var isDnsEnabled = false {
didSet {
toggleDns()
}
}
private var isProgrammaticChange = false private var isProgrammaticChange = false
@ -38,26 +41,51 @@ class ViewModel: NSObject, ObservableObject {
private let log = Logger(subsystem: String(describing: ViewModel.self), category: String(describing: ViewModel.self)) 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 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
}
}
}
}
}
func toggleDns() { func toggleDns() {
guard !isProgrammaticChange else {
// Reset, so next one is recognized as coming from the user again.
isProgrammaticChange = false
return
}
Task { Task {
if isDnsEnabled { if isDnsEnabled {
do { manager.dnsSettings = blocklist.settings
try await manager.loadFromPreferences()
}
catch {
log.error("Error loading preferences: \(error)")
delayedToggle(false)
return
}
let settings = NEDNSOverHTTPSSettings(servers: [blocklist.ipv4, blocklist.ipv6])
settings.serverURL = URL(string: "https://\(blocklist.server)")
settings.matchDomains = [""]
manager.dnsSettings = settings
manager.localizedDescription = blocklist.description manager.localizedDescription = blocklist.description
do { do {