Repository: glinford/dns-easy-switcher
Branch: main
Commit: 899f42346182
Files: 86
Total size: 136.5 KB
Directory structure:
gitextract_u_joqz4e/
├── .gitignore
├── DNS Easy Switcher/
│ ├── AboutView.swift
│ ├── AddCustomDNSView.swift
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── CustomDNSManagerView.swift
│ ├── CustomSheet.swift
│ ├── DNSManager.swift
│ ├── DNSSettings.swift
│ ├── DNSSpeedTester.swift
│ ├── DNS_Easy_Switcher.entitlements
│ ├── DNS_Easy_SwitcherApp.swift
│ ├── EditCustomDNSView.swift
│ ├── MenuBarController.swift
│ ├── MenuBarView.swift
│ └── Preview Content/
│ └── Preview Assets.xcassets/
│ └── Contents.json
├── DNS Easy Switcher.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata/
│ └── xcschemes/
│ └── DNS Easy Switcher.xcscheme
├── LICENSE
├── README.md
└── Releases/
├── DNS Easy Switcher v1.0.0/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.1/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.2/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.3/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ ├── DNS Easy Switcher.dmg
│ ├── DistributionSummary.plist
│ ├── ExportOptions.plist
│ └── Packaging.log
├── DNS Easy Switcher v1.0.4/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── CodeResources
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.5/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── CodeResources
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.6/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── CodeResources
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
└── DNS Easy Switcher v1.0.7/
├── DNS Easy Switcher.app/
│ └── Contents/
│ ├── CodeResources
│ ├── Info.plist
│ ├── MacOS/
│ │ └── DNS Easy Switcher
│ ├── PkgInfo
│ ├── Resources/
│ │ ├── AppIcon.icns
│ │ └── Assets.car
│ └── _CodeSignature/
│ └── CodeResources
└── DNS Easy Switcher.dmg
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
.DS_Store
================================================
FILE: DNS Easy Switcher/AboutView.swift
================================================
//
// AboutView.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 27/02/2025.
//
import SwiftUI
struct AboutView: View {
var onClose: () -> Void
private var versionText: String {
let shortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
return buildNumber.isEmpty ? "Version \(shortVersion)" : "Version \(shortVersion) (\(buildNumber))"
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("DNS Easy Switcher")
.font(.headline)
Text(versionText)
.foregroundColor(.secondary)
Link("GitHub — glinford/dns-easy-switcher", destination: URL(string: "https://github.com/glinford/dns-easy-switcher")!)
HStack {
Spacer()
Button("Close") {
onClose()
}
.keyboardShortcut(.escape)
}
.padding(.top, 8)
}
.padding()
.frame(width: 320)
}
}
================================================
FILE: DNS Easy Switcher/AddCustomDNSView.swift
================================================
//
// AddCustomDNSView.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 23/02/2025.
//
import SwiftUI
import SwiftData
struct AddCustomDNSView: View {
@State private var name: String = ""
@State private var primaryDNS: String = ""
@State private var secondaryDNS: String = ""
@State private var tertiaryDNS: String = ""
@State private var quaternaryDNS: String = ""
var onComplete: (CustomDNSServer?) -> Void
var body: some View {
VStack(alignment: .leading, spacing: 12) {
TextField("Name (e.g. Work DNS)", text: $name)
.textFieldStyle(.roundedBorder)
TextField("Primary DNS (e.g. 8.8.8.8 or 127.0.0.1:5353)", text: $primaryDNS)
.textFieldStyle(.roundedBorder)
.help("Use comma to add multiple addresses. For custom ports on IPv4, add colon and port number (e.g., 127.0.0.1:5353)")
TextField("Secondary DNS (optional)", text: $secondaryDNS)
.textFieldStyle(.roundedBorder)
.help("Use comma to add multiple addresses. For custom ports on IPv4, add colon and port number (e.g., 127.0.0.1:5353)")
TextField("Third DNS (IPv6 or IPv4, optional)", text: $tertiaryDNS)
.textFieldStyle(.roundedBorder)
.help("Tip: bracket IPv6 if adding a port, e.g., [2001:4860:4860::8888]:5353")
TextField("Fourth DNS (IPv6 or IPv4, optional)", text: $quaternaryDNS)
.textFieldStyle(.roundedBorder)
.help("Use comma to add multiple IPv6 entries if needed")
HStack {
Button("Cancel") {
onComplete(nil)
}
.keyboardShortcut(.escape)
Spacer()
Button("Add") {
guard !name.isEmpty && !primaryDNS.isEmpty else { return }
let server = CustomDNSServer(
name: name,
primaryDNS: primaryDNS,
secondaryDNS: secondaryDNS,
tertiaryDNS: tertiaryDNS,
quaternaryDNS: quaternaryDNS
)
onComplete(server)
}
.keyboardShortcut(.return)
.disabled(name.isEmpty || primaryDNS.isEmpty)
}
}
.padding()
.frame(width: 360)
}
}
================================================
FILE: DNS Easy Switcher/Assets.xcassets/AccentColor.colorset/Contents.json
================================================
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: DNS Easy Switcher/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{"images":[{"size":"1024x1024","filename":"1024-mac.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"128x128","expected-size":"128","filename":"128-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024-mac.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]}
================================================
FILE: DNS Easy Switcher/Assets.xcassets/Contents.json
================================================
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: DNS Easy Switcher/CustomDNSManagerView.swift
================================================
//
// CustomDNSManagerView.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 25/02/2025.
//
import SwiftUI
enum CustomDNSAction {
case use, edit, delete
}
struct CustomDNSManagerView: View {
let customServers: [CustomDNSServer]
let onAction: (CustomDNSAction, CustomDNSServer) -> Void
let onClose: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text("Manage Custom DNS")
.font(.headline)
.padding(.bottom, 4)
if customServers.isEmpty {
Text("No custom DNS servers added")
.foregroundColor(.secondary)
.padding(.vertical, 12)
} else {
List {
ForEach(customServers) { server in
HStack {
Text(server.name)
.lineLimit(1)
Spacer()
Button(action: {
onAction(.edit, server)
}) {
Image(systemName: "pencil")
.frame(width: 16, height: 16)
}
.buttonStyle(.plain)
.help("Edit this DNS")
.padding(.trailing, 8)
Button(action: {
onAction(.delete, server)
}) {
Image(systemName: "trash")
.frame(width: 16, height: 16)
}
.buttonStyle(.plain)
.foregroundColor(.red)
.help("Delete this DNS")
}
.padding(.vertical, 4)
}
}
.frame(minHeight: 100, maxHeight: 200)
.listStyle(.plain)
}
HStack {
Spacer()
Button("Close") {
onClose()
}
.keyboardShortcut(.escape)
}
.padding(.top, 8)
}
.padding()
.frame(width: 300)
}
}
================================================
FILE: DNS Easy Switcher/CustomSheet.swift
================================================
//
// CustomSheet.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 23/02/2025.
//
import SwiftUI
import AppKit
class CustomSheetWindowController: NSWindowController {
convenience init(view: some View, title: String) {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 300, height: 200),
styleMask: [.titled, .closable],
backing: .buffered,
defer: false
)
window.title = title
window.center()
window.contentView = NSHostingView(rootView: view)
window.isReleasedWhenClosed = false
self.init(window: window)
}
}
================================================
FILE: DNS Easy Switcher/DNSManager.swift
================================================
//
// DNSManager.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 23/02/2025.
//
import Foundation
import AppKit
import LocalAuthentication
class DNSManager {
static let shared = DNSManager()
let cloudflareServers = [
"1.1.1.1", // IPv4 Primary
"1.0.0.1", // IPv4 Secondary
"2606:4700:4700::1111", // IPv6 Primary
"2606:4700:4700::1001" // IPv6 Secondary
]
let quad9Servers = [
"9.9.9.9", // IPv4 Primary
"149.112.112.112", // IPv4 Secondary
"2620:fe::fe", // IPv6 Primary
"2620:fe::9" // IPv6 Secondary
]
let adguardServers = [
"94.140.14.14", // IPv4 Primary
"94.140.15.15", // IPv4 Secondary
"2a10:50c0::ad1:ff", // IPv6 Primary
"2a10:50c0::ad2:ff" // IPv6 Secondary
]
let getflixServers: [String: String] = [
"Australia — Melbourne": "118.127.62.178",
"Australia — Perth": "45.248.78.99",
"Australia — Sydney 1": "54.252.183.4",
"Australia — Sydney 2": "54.252.183.5",
"Brazil — São Paulo": "54.94.175.250",
"Canada — Toronto": "169.53.182.124",
"Denmark — Copenhagen": "82.103.129.240",
"Germany — Frankfurt": "54.93.169.181",
"Great Britain — London": "212.71.249.225",
"Hong Kong": "119.9.73.44",
"India — Mumbai": "103.13.112.251",
"Ireland — Dublin": "54.72.70.84",
"Italy — Milan": "95.141.39.238",
"Japan — Tokyo": "172.104.90.123",
"Netherlands — Amsterdam": "46.166.189.67",
"New Zealand — Auckland 1": "120.138.27.84",
"New Zealand — Auckland 2": "120.138.22.174",
"Singapore": "54.251.190.247",
"South Africa — Johannesburg": "102.130.116.140",
"Spain — Madrid": "185.93.3.168",
"Sweden — Stockholm": "46.246.29.68",
"Turkey — Istanbul": "212.68.53.190",
"United States — Dallas (Central)": "169.55.51.86",
"United States — Oregon (West)": "54.187.61.200",
"United States — Virginia (East)": "54.164.176.2"
]
private func getNetworkServices() -> [String] {
let task = Process()
task.launchPath = "/usr/sbin/networksetup"
task.arguments = ["-listallnetworkservices"]
let pipe = Pipe()
task.standardOutput = pipe
do {
try task.run()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let services = String(data: data, encoding: .utf8) {
return services.components(separatedBy: .newlines)
.dropFirst() // Drop the header line
.filter { !$0.isEmpty && !$0.hasPrefix("*") } // Remove empty lines and disabled services
}
} catch {
print("Error getting network services: \(error)")
}
return []
}
private func findActiveServices() -> [String] {
let services = getNetworkServices()
let activeServices = services.filter {
$0.lowercased().contains("wi-fi") || $0.lowercased().contains("ethernet")
}
return activeServices.isEmpty ? [services.first].compactMap { $0 } : activeServices
}
private func executeWithAuthentication(command: String, completion: @escaping (Bool) -> Void) {
let context = LAContext()
context.localizedReason = "DNS Easy Switcher needs to modify network settings"
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "DNS Easy Switcher needs to modify network settings") { success, error in
if success {
DispatchQueue.global(qos: .userInitiated).async {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
do {
try task.run()
task.waitUntilExit()
let success = task.terminationStatus == 0
DispatchQueue.main.async { completion(success) }
} catch {
print("Failed to execute command: \(error)")
DispatchQueue.main.async { completion(false) }
}
}
} else {
print("Authentication failed: \(error?.localizedDescription ?? "Unknown error")")
DispatchQueue.main.async { completion(false) }
}
}
} else {
// Fall back to AppleScript for admin privileges
print("Local Authentication not available: \(error?.localizedDescription ?? "Unknown error")")
DispatchQueue.global(qos: .userInitiated).async {
let script = """
do shell script "\(command)" with administrator privileges
"""
var scriptError: NSDictionary?
if let scriptObject = NSAppleScript(source: script) {
if scriptObject.executeAndReturnError(&scriptError) != nil {
DispatchQueue.main.async { completion(true) }
} else {
print("AppleScript error: \(scriptError ?? ["error": "Unknown error"] as NSDictionary)")
DispatchQueue.main.async { completion(false) }
}
} else {
DispatchQueue.main.async { completion(false) }
}
}
}
}
func setPredefinedDNS(dnsServers: [String], completion: @escaping (Bool) -> Void) {
let services = findActiveServices()
guard !services.isEmpty else {
completion(false)
return
}
let dispatchGroup = DispatchGroup()
var allSucceeded = true
for service in services {
dispatchGroup.enter()
let dnsArgs = dnsServers.joined(separator: " ")
let dnsCommand = "/usr/sbin/networksetup -setdnsservers '\(service)' \(dnsArgs)"
let ipv6Command = "/usr/sbin/networksetup -setv6off '\(service)'; /usr/sbin/networksetup -setv6automatic '\(service)'"
let fullCommand = "\(dnsCommand); \(ipv6Command)"
executeWithAuthentication(command: fullCommand) { success in
if !success {
allSucceeded = false
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
completion(allSucceeded)
}
}
func setCustomDNS(servers rawServers: [String], completion: @escaping (Bool) -> Void) {
let services = findActiveServices()
// Allow comma-separated entries in any slot
let flattenedServers = rawServers
.flatMap { entry in
entry
.split(separator: ",")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
}
.filter { !$0.isEmpty }
let parsedServers = flattenedServers.compactMap(parseDNSServer)
guard !services.isEmpty, !parsedServers.isEmpty else {
completion(false)
return
}
let hasCustomPorts = parsedServers.contains { $0.port != nil }
// If no custom ports are specified, use the standard network setup method
if !hasCustomPorts {
let servers = parsedServers.map { $0.address }
setStandardDNS(services: services, servers: servers, completion: completion)
return
}
// For DNS servers with custom ports, we need to modify the resolver configuration
let resolverContent = createResolverContent(parsedServers)
// We'll use the existing executeWithAuthentication method which properly handles
// authentication with Touch ID or admin password
let createDirCmd = "sudo mkdir -p /etc/resolver"
executeWithAuthentication(command: createDirCmd) { dirSuccess in
if !dirSuccess {
print("Failed to create resolver directory")
completion(false)
return
}
// Now write the resolver content
let writeFileCmd = "echo '\(resolverContent)' | sudo tee /etc/resolver/custom > /dev/null"
self.executeWithAuthentication(command: writeFileCmd) { fileSuccess in
if !fileSuccess {
print("Failed to write resolver configuration")
completion(false)
return
}
// Set permissions
let permCmd = "sudo chmod 644 /etc/resolver/custom"
self.executeWithAuthentication(command: permCmd) { permSuccess in
if !permSuccess {
print("Failed to set resolver file permissions")
completion(false)
return
}
// Also set standard DNS servers to ensure proper resolution
let standardServers = parsedServers.map { $0.address }
self.setStandardDNS(services: services, servers: standardServers, completion: completion)
}
}
}
}
private func createResolverContent(_ servers: [(address: String, port: Int?)]) -> String {
var resolverContent = "# Custom DNS configuration with port\n"
for server in servers {
resolverContent += "nameserver \(server.address)\n"
if let port = server.port {
resolverContent += "port \(port)\n"
}
}
return resolverContent
}
private func parseDNSServer(_ input: String) -> (address: String, port: Int?)? {
let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return nil }
// Support IPv6 with explicit port using bracket notation: [addr]:port
if trimmed.hasPrefix("["), let closingBracket = trimmed.firstIndex(of: "]") {
let address = String(trimmed[trimmed.index(after: trimmed.startIndex)..<closingBracket])
let remainder = trimmed[trimmed.index(after: closingBracket)..<trimmed.endIndex]
if remainder.hasPrefix(":") {
let portString = remainder.dropFirst()
if let port = Int(portString) {
return (address, port)
}
}
return (address, nil)
}
// IPv4 with port (single colon, numeric suffix)
let parts = trimmed.split(separator: ":", omittingEmptySubsequences: false)
if parts.count == 2,
let port = Int(parts[1]),
!parts[0].contains(":") {
return (String(parts[0]), port)
}
// IPv6 or plain address with no port
return (trimmed, nil)
}
func disableDNS(completion: @escaping (Bool) -> Void) {
let services = findActiveServices()
guard !services.isEmpty else {
completion(false)
return
}
// Remove any custom resolver configuration
let removeResolverCmd = "sudo rm -f /etc/resolver/custom"
executeWithAuthentication(command: removeResolverCmd) { _ in
// Continue with normal DNS reset regardless of resolver removal success
let dispatchGroup = DispatchGroup()
var allSucceeded = true
for service in services {
dispatchGroup.enter()
let command = "/usr/sbin/networksetup -setdnsservers '\(service)' empty"
self.executeWithAuthentication(command: command) { success in
if !success {
allSucceeded = false
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
completion(allSucceeded)
}
}
}
// Helper method to set standard DNS settings
private func setStandardDNS(services: [String], servers: [String], completion: @escaping (Bool) -> Void) {
let dispatchGroup = DispatchGroup()
var allSucceeded = true
for service in services {
dispatchGroup.enter()
let dnsArgs = servers.joined(separator: " ")
let dnsCommand = "/usr/sbin/networksetup -setdnsservers '\(service)' \(dnsArgs)"
let ipv6Command = "/usr/sbin/networksetup -setv6off '\(service)'; /usr/sbin/networksetup -setv6automatic '\(service)'"
let fullCommand = "\(dnsCommand); \(ipv6Command)"
executeWithAuthentication(command: fullCommand) { success in
if !success {
allSucceeded = false
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
completion(allSucceeded)
}
}
private func executePrivilegedCommand(arguments: [String]) -> Bool {
let services = findActiveServices()
guard !services.isEmpty else { return false }
var success = true
for service in services {
// Properly escape the arguments for AppleScript
let escapedArgs = arguments.map { arg in
return "\\\"" + arg.replacingOccurrences(of: "\\", with: "\\\\")
.replacingOccurrences(of: "\"", with: "\\\"") + "\\\""
}.joined(separator: " ")
// Combine IPv4 and IPv6 commands in a single script if we're setting DNS
let isSettingDNS = arguments[0] == "-setdnsservers"
let commandScript: String
if isSettingDNS {
// Combine DNS and IPv6 commands with semicolons in a single admin privilege request
let ipv6Script = "/usr/sbin/networksetup -setv6off '\(service)'; /usr/sbin/networksetup -setv6automatic '\(service)'"
commandScript = """
do shell script "/usr/sbin/networksetup \(escapedArgs); \(ipv6Script)" with administrator privileges with prompt "DNS Easy Switcher needs to modify network settings"
"""
} else {
// For other commands, keep as is
commandScript = """
do shell script "/usr/sbin/networksetup \(escapedArgs)" with administrator privileges with prompt "DNS Easy Switcher needs to modify network settings"
"""
}
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: commandScript) {
if scriptObject.executeAndReturnError(&error) == nil {
if let error = error {
print("Error executing privileged command: \(error)")
success = false
}
}
} else {
success = false
}
}
return success
}
func clearDNSCache(completion: @escaping (Bool) -> Void) {
let flushCommand = "dscacheutil -flushcache"
executeWithAuthentication(command: flushCommand) { success in
if success {
let restartCommand = "killall -HUP mDNSResponder 2>/dev/null || killall -HUP mdnsresponder 2>/dev/null || true"
self.executeWithAuthentication(command: restartCommand) { _ in
completion(success)
}
} else {
completion(false)
}
}
}
}
================================================
FILE: DNS Easy Switcher/DNSSettings.swift
================================================
//
// DNSSettings.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 23/02/2025.
//
import Foundation
import SwiftData
@Model
final class CustomDNSServer: Identifiable {
var id: String
var name: String
var primaryDNS: String
var secondaryDNS: String
var tertiaryDNS: String?
var quaternaryDNS: String?
var timestamp: Date
init(id: String = UUID().uuidString,
name: String,
primaryDNS: String,
secondaryDNS: String,
tertiaryDNS: String? = nil,
quaternaryDNS: String? = nil,
timestamp: Date = Date()) {
self.id = id
self.name = name
self.primaryDNS = primaryDNS
self.secondaryDNS = secondaryDNS
self.tertiaryDNS = tertiaryDNS
self.quaternaryDNS = quaternaryDNS
self.timestamp = timestamp
}
}
@Model
final class DNSSettings {
@Attribute(.unique) var id: String
var isCloudflareEnabled: Bool
var isQuad9Enabled: Bool
var activeCustomDNSID: String?
var timestamp: Date
var activeGetFlixLocation: String?
var isAdGuardEnabled: Bool?
init(id: String = UUID().uuidString,
isCloudflareEnabled: Bool = false,
isQuad9Enabled: Bool = false,
activeCustomDNSID: String? = nil,
timestamp: Date = Date(),
isAdGuardEnabled: Bool? = false,
activeGetFlixLocation: String? = nil) {
self.id = id
self.isCloudflareEnabled = isCloudflareEnabled
self.isQuad9Enabled = isQuad9Enabled
self.activeCustomDNSID = activeCustomDNSID
self.timestamp = timestamp
self.isAdGuardEnabled = isAdGuardEnabled
}
}
extension CustomDNSServer {
/// Returns all user-entered DNS entries, supporting comma-separated values per field.
var dnsEntries: [String] {
[primaryDNS, secondaryDNS, tertiaryDNS ?? "", quaternaryDNS ?? ""]
.flatMap { entry in
entry
.split(separator: ",")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
}
.filter { !$0.isEmpty }
}
}
================================================
FILE: DNS Easy Switcher/DNSSpeedTester.swift
================================================
//
// DNSSpeedTester.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 25/02/2025.
//
import Foundation
import SwiftData
class DNSSpeedTester {
static let shared = DNSSpeedTester()
// Result struct to store ping results
struct PingResult: Identifiable {
let id = UUID()
let dnsName: String
let server: String
let responseTime: Double // in milliseconds
let isSuccess: Bool
let isCustom: Bool
let customID: String?
init(dnsName: String, server: String, responseTime: Double, isSuccess: Bool, isCustom: Bool = false, customID: String? = nil) {
self.dnsName = dnsName
self.server = server
self.responseTime = responseTime
self.isSuccess = isSuccess
self.isCustom = isCustom
self.customID = customID
}
}
// Track running tasks to ensure proper cleanup
private var runningTasks: [Process] = []
private var isCurrentlyTesting = false
// Perform ping test for all DNS servers including custom ones
func testAllDNS(customServers: [CustomDNSServer], completion: @escaping ([PingResult]) -> Void) {
// Safety check to prevent multiple simultaneous tests
guard !isCurrentlyTesting else {
completion([])
return
}
isCurrentlyTesting = true
runningTasks = []
let dnsManager = DNSManager.shared
var allDNSToTest: [(String, String, Bool, String?)] = [
("Cloudflare", dnsManager.cloudflareServers[0], false, nil),
("Quad9", dnsManager.quad9Servers[0], false, nil),
("AdGuard", dnsManager.adguardServers[0], false, nil)
]
// Add all Getflix servers
let getflixServers = dnsManager.getflixServers.sorted(by: { $0.key < $1.key })
allDNSToTest.append(contentsOf: getflixServers.map { ("Getflix: \($0.key)", $0.value, false, nil) })
// Add custom DNS servers (first entry only to keep test time reasonable)
for server in customServers {
if let firstEntry = server.dnsEntries.first {
allDNSToTest.append((server.name, firstEntry, true, server.id))
}
}
// Use serial queue to avoid overwhelming the system
let queue = DispatchQueue(label: "com.glinford.DNSSpeedTest", qos: .userInitiated)
let resultsQueue = DispatchQueue(label: "com.glinford.DNSSpeedTestResults", attributes: .concurrent)
let resultsLock = NSLock()
var results: [PingResult] = []
let group = DispatchGroup()
// Create a semaphore to limit concurrent operations
let semaphore = DispatchSemaphore(value: 5) // Allow 5 concurrent pings
for (index, (name, server, isCustom, customID)) in allDNSToTest.enumerated() {
group.enter()
// Add a small delay between tests to avoid overwhelming the system
queue.asyncAfter(deadline: .now() + Double(index) * 0.05) { [weak self] in
guard let self = self else {
semaphore.signal()
group.leave()
return
}
semaphore.wait() // Wait for a slot to become available
self.pingServer(server: server) { responseTime, isSuccess in
resultsQueue.async {
resultsLock.lock()
let result = PingResult(
dnsName: name,
server: server,
responseTime: responseTime,
isSuccess: isSuccess,
isCustom: isCustom,
customID: customID
)
results.append(result)
resultsLock.unlock()
semaphore.signal() // Release the slot
group.leave()
}
}
}
}
group.notify(queue: .main) { [weak self] in
guard let self = self else { return }
// Clean up any remaining processes
for task in self.runningTasks {
if task.isRunning {
task.terminate()
}
}
self.runningTasks = []
self.isCurrentlyTesting = false
// Sort results by response time
let sortedResults = results.sorted { $0.responseTime < $1.responseTime }
completion(sortedResults)
}
}
// Cancel any ongoing tests
func cancelTests() {
for task in runningTasks {
if task.isRunning {
task.terminate()
}
}
runningTasks = []
isCurrentlyTesting = false
}
// Clean up when app is terminating
func cleanup() {
cancelTests()
}
// Measure ping time to a DNS server with safer implementation
private func pingServer(server: String, completion: @escaping (Double, Bool) -> Void) {
let task = Process()
task.launchPath = "/sbin/ping"
task.arguments = ["-c", "2", "-t", "1", server] // 2 pings with 1-second timeout (reduced for speed)
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
// Keep track of task for cleanup
runningTasks.append(task)
// Set up termination handler before running
task.terminationHandler = { [weak self] process in
guard let self = self else { return }
// Remove this task from our tracking list
if let index = self.runningTasks.firstIndex(where: { $0 === process }) {
self.runningTasks.remove(at: index)
}
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? ""
// Parse ping results
if process.terminationStatus == 0 && output.contains("min/avg/max") {
// More robust parsing approach
let lines = output.components(separatedBy: .newlines)
for line in lines {
if line.contains("min/avg/max") {
let parts = line.components(separatedBy: "=")
if parts.count >= 2 {
let stats = parts[1].trimmingCharacters(in: .whitespaces)
let values = stats.components(separatedBy: "/")
if values.count >= 2 {
if let avgTime = Double(values[1].trimmingCharacters(in: .whitespaces)) {
completion(avgTime, true)
return
}
}
}
}
}
// If we get here, parsing failed
completion(999, false)
} else {
completion(999, false) // Ping failed
}
}
do {
try task.run()
} catch {
// Remove this task from our tracking list if it failed to start
if let index = runningTasks.firstIndex(where: { $0 === task }) {
runningTasks.remove(at: index)
}
completion(999, false) // Process failed to start
}
}
// Deinitializer to clean up resources
deinit {
cleanup()
}
}
================================================
FILE: DNS Easy Switcher/DNS_Easy_Switcher.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
================================================
FILE: DNS Easy Switcher/DNS_Easy_SwitcherApp.swift
================================================
//
// DNS_Easy_SwitcherApp.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 23/02/2025.
//
import SwiftUI
import SwiftData
import AppKit
@main
struct DNS_Easy_SwitcherApp: App {
@StateObject private var menuBarController = MenuBarController()
let modelContainer: ModelContainer
init() {
do {
let schema = Schema([
DNSSettings.self,
CustomDNSServer.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
self.modelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
// If the persistent store cannot be created (e.g., corrupted store or permission issue),
// fall back to an in-memory container so the app can still launch.
let schema = Schema([
DNSSettings.self,
CustomDNSServer.self
])
self.modelContainer = try! ModelContainer(
for: schema,
configurations: [ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)]
)
print("Warning: Using in-memory store due to ModelContainer error: \(error)")
}
}
var body: some Scene {
WindowGroup(id: "hidden") {
Color.clear
.frame(width: 0, height: 0)
.hidden()
}
.windowStyle(.hiddenTitleBar)
.defaultSize(width: 0, height: 0)
.modelContainer(modelContainer)
MenuBarExtra("DNS Switcher", systemImage: "network") {
MenuBarView()
.environment(\.modelContext, modelContainer.mainContext)
.frame(width: 300)
}
}
}
================================================
FILE: DNS Easy Switcher/EditCustomDNSView.swift
================================================
//
// EditCustomDNSView.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 25/02/2025.
//
import SwiftUI
struct EditCustomDNSView: View {
let server: CustomDNSServer
var onComplete: (CustomDNSServer?) -> Void
@State private var name: String
@State private var primaryDNS: String
@State private var secondaryDNS: String
@State private var tertiaryDNS: String
@State private var quaternaryDNS: String
init(server: CustomDNSServer, onComplete: @escaping (CustomDNSServer?) -> Void) {
self.server = server
self.onComplete = onComplete
_name = State(initialValue: server.name)
_primaryDNS = State(initialValue: server.primaryDNS)
_secondaryDNS = State(initialValue: server.secondaryDNS)
_tertiaryDNS = State(initialValue: server.tertiaryDNS ?? "")
_quaternaryDNS = State(initialValue: server.quaternaryDNS ?? "")
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
TextField("Name (e.g. Work DNS)", text: $name)
.textFieldStyle(.roundedBorder)
TextField("Primary DNS (e.g. 8.8.8.8 or 127.0.0.1:5353)", text: $primaryDNS)
.textFieldStyle(.roundedBorder)
.help("Use comma to add multiple addresses. For custom ports on IPv4, add colon and port number (e.g., 127.0.0.1:5353)")
TextField("Secondary DNS (optional)", text: $secondaryDNS)
.textFieldStyle(.roundedBorder)
.help("Use comma to add multiple addresses. For custom ports on IPv4, add colon and port number (e.g., 127.0.0.1:5353)")
TextField("Third DNS (IPv6 or IPv4, optional)", text: $tertiaryDNS)
.textFieldStyle(.roundedBorder)
.help("Tip: bracket IPv6 if adding a port, e.g., [2001:4860:4860::8888]:5353")
TextField("Fourth DNS (IPv6 or IPv4, optional)", text: $quaternaryDNS)
.textFieldStyle(.roundedBorder)
.help("Use comma to add multiple IPv6 entries if needed")
HStack {
Button("Cancel") {
onComplete(nil)
}
.keyboardShortcut(.escape)
Spacer()
Button("Save") {
guard !name.isEmpty && !primaryDNS.isEmpty else { return }
let updatedServer = CustomDNSServer(
id: server.id,
name: name,
primaryDNS: primaryDNS,
secondaryDNS: secondaryDNS,
tertiaryDNS: tertiaryDNS,
quaternaryDNS: quaternaryDNS,
timestamp: server.timestamp
)
onComplete(updatedServer)
}
.keyboardShortcut(.return)
.disabled(name.isEmpty || primaryDNS.isEmpty)
}
}
.padding()
.frame(width: 360)
}
}
================================================
FILE: DNS Easy Switcher/MenuBarController.swift
================================================
//
// MenuBarController.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 23/02/2025.
//
import Foundation
import AppKit
class MenuBarController: ObservableObject {
init() {
// Hide the dock icon
NSApplication.shared.setActivationPolicy(.accessory)
}
}
================================================
FILE: DNS Easy Switcher/MenuBarView.swift
================================================
//
// MenuBarView.swift
// DNS Easy Switcher
//
// Created by Gregory LINFORD on 23/02/2025.
//
import SwiftUI
import SwiftData
struct MenuBarView: View {
@Environment(\.modelContext) private var modelContext
@Query(sort: \DNSSettings.timestamp) private var dnsSettings: [DNSSettings]
@Query(sort: \CustomDNSServer.name) private var customServers: [CustomDNSServer]
@State private var isUpdating = false
@State private var isSpeedTesting = false
@State private var pingResults: [DNSSpeedTester.PingResult] = []
@State private var showingAddDNS = false
@State private var showingManageDNS = false
@State private var aboutWindowController: CustomSheetWindowController?
@State private var selectedServer: CustomDNSServer?
@State private var windowController: CustomSheetWindowController?
var body: some View {
Group {
VStack {
// Cloudflare DNS
Toggle(getLabelWithPing("Cloudflare DNS", dnsType: .cloudflare), isOn: Binding(
get: { dnsSettings.first?.isCloudflareEnabled ?? false },
set: { newValue in
if newValue && !isUpdating {
activateDNS(type: .cloudflare)
}
}
))
.padding(.horizontal)
.disabled(isUpdating || isSpeedTesting)
.overlay(alignment: .trailing) {
if isSpeedTesting {
ProgressView()
.scaleEffect(0.6)
.frame(width: 12, height: 12)
.padding(.trailing, 8)
}
}
// Quad9 DNS
Toggle(getLabelWithPing("Quad9 DNS", dnsType: .quad9), isOn: Binding(
get: { dnsSettings.first?.isQuad9Enabled ?? false },
set: { newValue in
if newValue && !isUpdating {
activateDNS(type: .quad9)
}
}
))
.padding(.horizontal)
.disabled(isUpdating || isSpeedTesting)
.overlay(alignment: .trailing) {
if isSpeedTesting {
ProgressView()
.scaleEffect(0.6)
.frame(width: 12, height: 12)
.padding(.trailing, 8)
}
}
// AdGuard DNS
Toggle(getLabelWithPing("AdGuard DNS", dnsType: .adguard), isOn: Binding(
get: { dnsSettings.first?.isAdGuardEnabled ?? false },
set: { newValue in
if newValue && !isUpdating {
activateDNS(type: .adguard)
}
}
))
.padding(.horizontal)
.disabled(isUpdating || isSpeedTesting)
.overlay(alignment: .trailing) {
if isSpeedTesting {
ProgressView()
.scaleEffect(0.6)
.frame(width: 12, height: 12)
.padding(.trailing, 8)
}
}
// GetFlix DNS Menu
Menu {
ForEach(Array(DNSManager.shared.getflixServers.keys.sorted()), id: \.self) { location in
Button(action: {
activateDNS(type: .getflix(location))
}) {
HStack {
Text(getGetflixLabelWithPing(location))
Spacer()
if dnsSettings.first?.activeGetFlixLocation == location {
Image(systemName: "checkmark")
}
}
}
}
} label: {
HStack {
Text("GetFlix DNS")
Spacer()
if let activeLocation = dnsSettings.first?.activeGetFlixLocation {
Circle()
.fill(Color.green)
.frame(width: 8, height: 8)
}
if isSpeedTesting {
ProgressView()
.scaleEffect(0.6)
.frame(width: 12, height: 12)
.padding(.trailing, 4)
}
}
}
.padding(.horizontal)
.disabled(isUpdating || isSpeedTesting)
Divider()
// Custom DNS section
if !customServers.isEmpty {
Menu {
ForEach(customServers) { server in
Button(action: {
activateDNS(type: .custom(server))
}) {
HStack {
Text(getCustomDNSLabelWithPing(server))
Spacer()
if dnsSettings.first?.activeCustomDNSID == server.id {
Image(systemName: "checkmark")
}
}
}
}
} label: {
HStack {
Text("Custom DNS")
Spacer()
if dnsSettings.first?.activeCustomDNSID != nil {
Circle()
.fill(Color.green)
.frame(width: 8, height: 8)
}
if isSpeedTesting {
ProgressView()
.scaleEffect(0.6)
.frame(width: 12, height: 12)
.padding(.trailing, 4)
}
}
}
.padding(.horizontal)
.disabled(isUpdating || isSpeedTesting)
Button(action: {
showManageCustomDNSSheet()
}) {
Text("Manage Custom DNS")
.frame(maxWidth: .infinity)
}
.buttonStyle(.plain)
.padding(.horizontal)
.padding(.vertical, 5)
.disabled(isSpeedTesting)
}
Button(action: {
showAddCustomDNSSheet()
}) {
Text("Add Custom DNS")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.padding(.horizontal)
.padding(.vertical, 5)
.disabled(isSpeedTesting)
Divider()
Button("Disable DNS Override") {
if !isUpdating && !isSpeedTesting {
isUpdating = true
DNSManager.shared.disableDNS { success in
if success {
Task { @MainActor in
updateSettings(type: .none)
}
}
isUpdating = false
}
}
}
.padding(.vertical, 5)
.disabled(isUpdating || isSpeedTesting)
// Speed Test Button
Button(action: {
runSpeedTest()
}) {
HStack {
Text("Run Speed Test")
if isSpeedTesting {
Spacer()
ProgressView()
.scaleEffect(0.8)
.frame(width: 16, height: 16)
}
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.padding(.horizontal)
.padding(.vertical, 5)
.disabled(isUpdating || isSpeedTesting)
Button(action: {
clearDNSCache()
}) {
HStack {
Text("Clear DNS Cache")
if isUpdating {
Spacer()
ProgressView()
.scaleEffect(0.8)
.frame(width: 16, height: 16)
}
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.padding(.horizontal)
.padding(.vertical, 5)
.disabled(isUpdating || isSpeedTesting)
Divider()
Button("About") {
showAboutSheet()
}
.padding(.vertical, 5)
Button("Quit") {
NSApplication.shared.terminate(nil)
}
.padding(.vertical, 5)
}
.padding(.vertical, 5)
}
.onAppear {
ensureSettingsExist()
}
}
// Helper methods for getting ping results
private func getLabelWithPing(_ baseLabel: String, dnsType: DNSType) -> String {
guard !pingResults.isEmpty else { return baseLabel }
switch dnsType {
case .cloudflare:
if let result = pingResults.first(where: { $0.dnsName == "Cloudflare" }) {
return "\(baseLabel) (\(Int(result.responseTime))ms)"
}
case .quad9:
if let result = pingResults.first(where: { $0.dnsName == "Quad9" }) {
return "\(baseLabel) (\(Int(result.responseTime))ms)"
}
case .adguard:
if let result = pingResults.first(where: { $0.dnsName == "AdGuard" }) {
return "\(baseLabel) (\(Int(result.responseTime))ms)"
}
default:
break
}
return baseLabel
}
private func getGetflixLabelWithPing(_ location: String) -> String {
guard !pingResults.isEmpty else { return location }
if let result = pingResults.first(where: { $0.dnsName == "Getflix: \(location)" }) {
return "\(location) (\(Int(result.responseTime))ms)"
}
return location
}
private func getCustomDNSLabelWithPing(_ server: CustomDNSServer) -> String {
guard !pingResults.isEmpty else { return server.name }
if let result = pingResults.first(where: { $0.isCustom && $0.customID == server.id }) {
return "\(server.name) (\(Int(result.responseTime))ms)"
}
return server.name
}
// Run DNS speed test
private func runSpeedTest() {
guard !isSpeedTesting else { return }
isSpeedTesting = true
pingResults = []
DNSSpeedTester.shared.testAllDNS(customServers: customServers) { results in
self.pingResults = results
self.isSpeedTesting = false
}
}
private func showAddCustomDNSSheet() {
let addView = AddCustomDNSView { newServer in
if let newServer = newServer {
modelContext.insert(newServer)
try? modelContext.save()
// Automatically activate the new DNS
activateDNS(type: .custom(newServer))
}
windowController?.close()
windowController = nil
}
windowController = CustomSheetWindowController(view: addView, title: "Add Custom DNS")
windowController?.window?.level = .floating
windowController?.showWindow(nil)
// Position the window relative to the menu bar
if let window = windowController?.window,
let screenFrame = NSScreen.main?.frame {
let windowFrame = window.frame
let newOrigin = NSPoint(
x: screenFrame.width - windowFrame.width - 20,
y: screenFrame.height - 40 - windowFrame.height
)
window.setFrameTopLeftPoint(newOrigin)
}
}
private func showManageCustomDNSSheet() {
let manageView = CustomDNSManagerView(customServers: customServers, onAction: { action, server in
switch action {
case .edit:
editCustomDNS(server)
case .delete:
modelContext.delete(server)
try? modelContext.save()
// If this was the active server, disable DNS
if dnsSettings.first?.activeCustomDNSID == server.id {
isUpdating = true
DNSManager.shared.disableDNS { success in
if success {
Task { @MainActor in
updateSettings(type: .none)
}
}
isUpdating = false
}
}
case .use:
activateDNS(type: .custom(server))
}
// Don't close the window for .use or .edit actions
if action == .delete {
windowController?.close()
windowController = nil
}
}, onClose: {
windowController?.close()
windowController = nil
})
windowController = CustomSheetWindowController(view: manageView, title: "Manage Custom DNS")
windowController?.window?.level = .floating
windowController?.showWindow(nil)
// Position the window relative to the menu bar
if let window = windowController?.window,
let screenFrame = NSScreen.main?.frame {
let windowFrame = window.frame
let newOrigin = NSPoint(
x: screenFrame.width - windowFrame.width - 20,
y: screenFrame.height - 40 - windowFrame.height
)
window.setFrameTopLeftPoint(newOrigin)
}
}
private func showAboutSheet() {
let aboutView = AboutView {
aboutWindowController?.close()
aboutWindowController = nil
}
aboutWindowController?.close()
aboutWindowController = CustomSheetWindowController(view: aboutView, title: "About")
aboutWindowController?.window?.level = .floating
aboutWindowController?.showWindow(nil)
aboutWindowController?.window?.center()
}
private func editCustomDNS(_ server: CustomDNSServer) {
let editView = EditCustomDNSView(server: server) { updatedServer in
if let updatedServer = updatedServer {
// Update existing server properties
server.name = updatedServer.name
server.primaryDNS = updatedServer.primaryDNS
server.secondaryDNS = updatedServer.secondaryDNS
server.tertiaryDNS = updatedServer.tertiaryDNS
server.quaternaryDNS = updatedServer.quaternaryDNS
try? modelContext.save()
// If this was the active server, update DNS settings
if dnsSettings.first?.activeCustomDNSID == server.id {
activateDNS(type: .custom(server))
}
}
windowController?.close()
windowController = nil
}
windowController?.close()
windowController = CustomSheetWindowController(view: editView, title: "Edit Custom DNS")
windowController?.window?.level = .floating
windowController?.showWindow(nil)
// Position the window relative to the menu bar
if let window = windowController?.window,
let screenFrame = NSScreen.main?.frame {
let windowFrame = window.frame
let newOrigin = NSPoint(
x: screenFrame.width - windowFrame.width - 20,
y: screenFrame.height - 40 - windowFrame.height
)
window.setFrameTopLeftPoint(newOrigin)
}
}
enum DNSType: Equatable {
case none
case cloudflare
case quad9
case adguard
case custom(CustomDNSServer)
case getflix(String)
static func == (lhs: DNSType, rhs: DNSType) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case (.cloudflare, .cloudflare):
return true
case (.quad9, .quad9):
return true
case (.adguard, .adguard):
return true
case (.custom(let lServer), .custom(let rServer)):
return lServer.id == rServer.id
case (.getflix(let lLocation), .getflix(let rLocation)):
return lLocation == rLocation
default:
return false
}
}
}
private func activateDNS(type: DNSType) {
isUpdating = true
switch type {
case .cloudflare:
DNSManager.shared.setPredefinedDNS(dnsServers: DNSManager.shared.cloudflareServers) { success in
if success {
Task { @MainActor in
updateSettings(type: type)
}
}
isUpdating = false
}
case .quad9:
DNSManager.shared.setPredefinedDNS(dnsServers: DNSManager.shared.quad9Servers) { success in
if success {
Task { @MainActor in
updateSettings(type: type)
}
}
isUpdating = false
}
case .adguard:
DNSManager.shared.setPredefinedDNS(dnsServers: DNSManager.shared.adguardServers) { success in
if success {
Task { @MainActor in
updateSettings(type: type)
}
}
isUpdating = false
}
case .custom(let server):
DNSManager.shared.setCustomDNS(servers: server.dnsEntries) { success in
if success {
Task { @MainActor in
updateSettings(type: type)
}
}
isUpdating = false
}
case .getflix(let location):
if let dnsServer = DNSManager.shared.getflixServers[location] {
DNSManager.shared.setCustomDNS(servers: [dnsServer]) { success in
if success {
Task { @MainActor in
updateSettings(type: type)
}
}
isUpdating = false
}
}
case .none:
updateSettings(type: type)
isUpdating = false
}
}
private func updateSettings(type: DNSType) {
if let settings = dnsSettings.first {
settings.isCloudflareEnabled = (type == .cloudflare)
settings.isQuad9Enabled = (type == .quad9)
settings.isAdGuardEnabled = type == .adguard ? true : nil
if case .getflix(let location) = type {
settings.activeGetFlixLocation = location
} else {
settings.activeGetFlixLocation = nil
}
if case .custom(let server) = type {
settings.activeCustomDNSID = server.id
} else {
settings.activeCustomDNSID = nil
}
settings.timestamp = Date()
}
}
private func ensureSettingsExist() {
if dnsSettings.isEmpty {
modelContext.insert(DNSSettings())
try? modelContext.save()
}
}
private func clearDNSCache() {
if !isUpdating && !isSpeedTesting {
isUpdating = true
DNSManager.shared.clearDNSCache { success in
DispatchQueue.main.async {
self.isUpdating = false
}
}
}
}
}
================================================
FILE: DNS Easy Switcher/Preview Content/Preview Assets.xcassets/Contents.json
================================================
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: DNS Easy Switcher.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
9165B6682D6B616D00DD9643 /* DNS_Easy_SwitcherApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9165B6672D6B616D00DD9643 /* DNS_Easy_SwitcherApp.swift */; };
9165B66A2D6B616D00DD9643 /* MenuBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9165B6692D6B616D00DD9643 /* MenuBarView.swift */; };
9165B66C2D6B616D00DD9643 /* DNSSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9165B66B2D6B616D00DD9643 /* DNSSettings.swift */; };
9165B66E2D6B616D00DD9643 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9165B66D2D6B616D00DD9643 /* Assets.xcassets */; };
9165B6712D6B616D00DD9643 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9165B6702D6B616D00DD9643 /* Preview Assets.xcassets */; };
9165B6792D6B61C500DD9643 /* MenuBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9165B6782D6B61C500DD9643 /* MenuBarController.swift */; };
9165B67B2D6B62CC00DD9643 /* DNSManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9165B67A2D6B62CC00DD9643 /* DNSManager.swift */; };
9165B67D2D6B699A00DD9643 /* AddCustomDNSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9165B67C2D6B699A00DD9643 /* AddCustomDNSView.swift */; };
9165B67F2D6B6D1800DD9643 /* CustomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9165B67E2D6B6D1800DD9643 /* CustomSheet.swift */; };
91FFB82B2D6E6619008C0471 /* CustomDNSManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FFB82A2D6E6619008C0471 /* CustomDNSManagerView.swift */; };
91FFB82D2D6E6639008C0471 /* EditCustomDNSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FFB82C2D6E6639008C0471 /* EditCustomDNSView.swift */; };
91FFB82F2D6E6868008C0471 /* DNSSpeedTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FFB82E2D6E6868008C0471 /* DNSSpeedTester.swift */; };
91FFB9002D70E000008C0471 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FFB8FF2D70E000008C0471 /* AboutView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
9165B6642D6B616D00DD9643 /* DNS Easy Switcher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DNS Easy Switcher.app"; sourceTree = BUILT_PRODUCTS_DIR; };
9165B6672D6B616D00DD9643 /* DNS_Easy_SwitcherApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNS_Easy_SwitcherApp.swift; sourceTree = "<group>"; };
9165B6692D6B616D00DD9643 /* MenuBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarView.swift; sourceTree = "<group>"; };
9165B66B2D6B616D00DD9643 /* DNSSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSSettings.swift; sourceTree = "<group>"; };
9165B66D2D6B616D00DD9643 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
9165B6702D6B616D00DD9643 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
9165B6722D6B616D00DD9643 /* DNS_Easy_Switcher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DNS_Easy_Switcher.entitlements; sourceTree = "<group>"; };
9165B6782D6B61C500DD9643 /* MenuBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarController.swift; sourceTree = "<group>"; };
9165B67A2D6B62CC00DD9643 /* DNSManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSManager.swift; sourceTree = "<group>"; };
9165B67C2D6B699A00DD9643 /* AddCustomDNSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCustomDNSView.swift; sourceTree = "<group>"; };
9165B67E2D6B6D1800DD9643 /* CustomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSheet.swift; sourceTree = "<group>"; };
91FFB82A2D6E6619008C0471 /* CustomDNSManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDNSManagerView.swift; sourceTree = "<group>"; };
91FFB82C2D6E6639008C0471 /* EditCustomDNSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCustomDNSView.swift; sourceTree = "<group>"; };
91FFB82E2D6E6868008C0471 /* DNSSpeedTester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSSpeedTester.swift; sourceTree = "<group>"; };
91FFB8FF2D70E000008C0471 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
9165B6612D6B616D00DD9643 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9165B65B2D6B616D00DD9643 = {
isa = PBXGroup;
children = (
9165B6662D6B616D00DD9643 /* DNS Easy Switcher */,
9165B6652D6B616D00DD9643 /* Products */,
);
sourceTree = "<group>";
};
9165B6652D6B616D00DD9643 /* Products */ = {
isa = PBXGroup;
children = (
9165B6642D6B616D00DD9643 /* DNS Easy Switcher.app */,
);
name = Products;
sourceTree = "<group>";
};
9165B6662D6B616D00DD9643 /* DNS Easy Switcher */ = {
isa = PBXGroup;
children = (
9165B6672D6B616D00DD9643 /* DNS_Easy_SwitcherApp.swift */,
9165B6692D6B616D00DD9643 /* MenuBarView.swift */,
9165B66B2D6B616D00DD9643 /* DNSSettings.swift */,
9165B66D2D6B616D00DD9643 /* Assets.xcassets */,
9165B6722D6B616D00DD9643 /* DNS_Easy_Switcher.entitlements */,
9165B66F2D6B616D00DD9643 /* Preview Content */,
9165B6782D6B61C500DD9643 /* MenuBarController.swift */,
9165B67A2D6B62CC00DD9643 /* DNSManager.swift */,
9165B67C2D6B699A00DD9643 /* AddCustomDNSView.swift */,
9165B67E2D6B6D1800DD9643 /* CustomSheet.swift */,
91FFB82A2D6E6619008C0471 /* CustomDNSManagerView.swift */,
91FFB82C2D6E6639008C0471 /* EditCustomDNSView.swift */,
91FFB82E2D6E6868008C0471 /* DNSSpeedTester.swift */,
91FFB8FF2D70E000008C0471 /* AboutView.swift */,
);
path = "DNS Easy Switcher";
sourceTree = "<group>";
};
9165B66F2D6B616D00DD9643 /* Preview Content */ = {
isa = PBXGroup;
children = (
9165B6702D6B616D00DD9643 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
9165B6632D6B616D00DD9643 /* DNS Easy Switcher */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9165B6752D6B616D00DD9643 /* Build configuration list for PBXNativeTarget "DNS Easy Switcher" */;
buildPhases = (
9165B6602D6B616D00DD9643 /* Sources */,
9165B6612D6B616D00DD9643 /* Frameworks */,
9165B6622D6B616D00DD9643 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "DNS Easy Switcher";
productName = "DNS Easy Switcher";
productReference = 9165B6642D6B616D00DD9643 /* DNS Easy Switcher.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
9165B65C2D6B616D00DD9643 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1500;
LastUpgradeCheck = 1500;
TargetAttributes = {
9165B6632D6B616D00DD9643 = {
CreatedOnToolsVersion = 15.0.1;
};
};
};
buildConfigurationList = 9165B65F2D6B616D00DD9643 /* Build configuration list for PBXProject "DNS Easy Switcher" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 9165B65B2D6B616D00DD9643;
productRefGroup = 9165B6652D6B616D00DD9643 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
9165B6632D6B616D00DD9643 /* DNS Easy Switcher */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
9165B6622D6B616D00DD9643 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9165B6712D6B616D00DD9643 /* Preview Assets.xcassets in Resources */,
9165B66E2D6B616D00DD9643 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
9165B6602D6B616D00DD9643 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9165B6792D6B61C500DD9643 /* MenuBarController.swift in Sources */,
9165B67F2D6B6D1800DD9643 /* CustomSheet.swift in Sources */,
9165B67D2D6B699A00DD9643 /* AddCustomDNSView.swift in Sources */,
9165B66A2D6B616D00DD9643 /* MenuBarView.swift in Sources */,
91FFB82B2D6E6619008C0471 /* CustomDNSManagerView.swift in Sources */,
9165B66C2D6B616D00DD9643 /* DNSSettings.swift in Sources */,
91FFB82F2D6E6868008C0471 /* DNSSpeedTester.swift in Sources */,
9165B67B2D6B62CC00DD9643 /* DNSManager.swift in Sources */,
91FFB82D2D6E6639008C0471 /* EditCustomDNSView.swift in Sources */,
91FFB9002D70E000008C0471 /* AboutView.swift in Sources */,
9165B6682D6B616D00DD9643 /* DNS_Easy_SwitcherApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
9165B6732D6B616D00DD9643 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
9165B6742D6B616D00DD9643 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
};
name = Release;
};
9165B6762D6B616D00DD9643 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = "DNS Easy Switcher/DNS_Easy_Switcher.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"DNS Easy Switcher/Preview Content\"";
DEVELOPMENT_TEAM = 6645MJMX63;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "DNS Easy Switcher";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0.7;
PRODUCT_BUNDLE_IDENTIFIER = com.linfordsoftware.dnseasyswitcher;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
9165B6772D6B616D00DD9643 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = "DNS Easy Switcher/DNS_Easy_Switcher.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"DNS Easy Switcher/Preview Content\"";
DEVELOPMENT_TEAM = 6645MJMX63;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "DNS Easy Switcher";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0.7;
PRODUCT_BUNDLE_IDENTIFIER = com.linfordsoftware.dnseasyswitcher;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
9165B65F2D6B616D00DD9643 /* Build configuration list for PBXProject "DNS Easy Switcher" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9165B6732D6B616D00DD9643 /* Debug */,
9165B6742D6B616D00DD9643 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9165B6752D6B616D00DD9643 /* Build configuration list for PBXNativeTarget "DNS Easy Switcher" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9165B6762D6B616D00DD9643 /* Debug */,
9165B6772D6B616D00DD9643 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 9165B65C2D6B616D00DD9643 /* Project object */;
}
================================================
FILE: DNS Easy Switcher.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
================================================
FILE: DNS Easy Switcher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: DNS Easy Switcher.xcodeproj/xcshareddata/xcschemes/DNS Easy Switcher.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9165B6632D6B616D00DD9643"
BuildableName = "DNS Easy Switcher.app"
BlueprintName = "DNS Easy Switcher"
ReferencedContainer = "container:DNS Easy Switcher.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9165B6632D6B616D00DD9643"
BuildableName = "DNS Easy Switcher.app"
BlueprintName = "DNS Easy Switcher"
ReferencedContainer = "container:DNS Easy Switcher.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9165B6632D6B616D00DD9643"
BuildableName = "DNS Easy Switcher.app"
BlueprintName = "DNS Easy Switcher"
ReferencedContainer = "container:DNS Easy Switcher.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 Greg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# DNS Easy Switcher
A simple macOS menu bar app that allows you to quickly switch between different DNS providers (or add custom ones).


## Features
- Easy switching between popular DNS providers:
- Cloudflare DNS (1.1.1.1)
- Quad9 DNS (9.9.9.9)
- AdGuard DNS (94.140.14.14)
- GetFlix DNS (with list of all locations)
- Disable DNS Overrides (DHCP-Provided DNS)
- Add and manage your own custom DNS servers
- Test DNS speed to find the fastest provider
- Flush DNS Cache
- Touch ID authentication for DNS changes
- Native macOS menu bar integration
- Persists your settings between app launches
- IPv4 and IPv6 support
## Installation
### Using Homebrew (Recommended)
Install DNS Easy Switcher with Homebrew using these commands:
```bash
brew tap glinford/tap
brew install --cask dns-easy-switcher
```
To update to the latest version when available:
```bash
brew upgrade --cask dns-easy-switcher
```
### Manual Installation
1. Download the latest release from the [Releases](../../releases) page
2. Mount the DMG file
3. Drag DNS Easy Switcher to your Applications folder
4. Launch DNS Easy Switcher from Applications
## First Launch
Since DNS Easy Switcher is distributed outside the Mac App Store, macOS may show a security warning when you first launch it.
To allow the app to run:
1. Right-click (or Control-click) on DNS Easy Switcher in your Applications folder
2. Select "Open" from the context menu
3. Click "Open" in the dialog that appears
4. Allow system extensions when prompted (required for DNS changes)

## Important Note
Due to macOS security requirements, administrator privileges are required each time you switch DNS settings. If you have a Touch ID-enabled Mac, you can now use Touch ID instead of password entry for authentication.
## Requirements
- macOS 14.0 (Sonoma) or later
- Administrator privileges (required for changing DNS settings)
- Touch ID compatible Mac (for Touch ID authentication feature)
## Tested Configurations
| macOS Version | Status |
|--------------|--------|
| Sonoma 14.5 | ✅ |
## Building from Source
1. Clone the repository:
```bash
git clone https://github.com/glinford/dns-easy-switcher.git
```
2. Open the project in Xcode 15 or later
3. Build and run the project
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## Acknowledgments
- [Cloudflare DNS](https://1.1.1.1) for their public DNS service
- [Quad9](https://quad9.net) for their secure DNS service
- [AdGuard DNS](https://adguard-dns.io/en/welcome.html) for their privacy-focused DNS service with ad blocking capabilities
- [GetFlix](https://www.getflix.com.au/setup/dns-servers/)
## Privacy
DNS Easy Switcher does not collect any data. All settings are stored locally on your device.
================================================
FILE: Releases/DNS Easy Switcher v1.0.0/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.0</string>
<key>DTSDKBuild</key>
<string>23A334</string>
<key>DTSDKName</key>
<string>macosx14.0</string>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.0/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.0/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
IhhuUihlv8TNfeE5CB9OZcOX21w=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
/sNzrOXugwAIyPdIJZkIv1275OZELTMDgWfU3DNwkwQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.1/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.1</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>2</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.0</string>
<key>DTSDKBuild</key>
<string>23A334</string>
<key>DTSDKName</key>
<string>macosx14.0</string>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.1/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.1/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
IhhuUihlv8TNfeE5CB9OZcOX21w=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
/sNzrOXugwAIyPdIJZkIv1275OZELTMDgWfU3DNwkwQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.2/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.2</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>2</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.0</string>
<key>DTSDKBuild</key>
<string>23A334</string>
<key>DTSDKName</key>
<string>macosx14.0</string>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.2/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.2/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
IhhuUihlv8TNfeE5CB9OZcOX21w=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
/sNzrOXugwAIyPdIJZkIv1275OZELTMDgWfU3DNwkwQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.3/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.3</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>4</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.0</string>
<key>DTSDKBuild</key>
<string>23A334</string>
<key>DTSDKName</key>
<string>macosx14.0</string>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.3/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.3/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
IhhuUihlv8TNfeE5CB9OZcOX21w=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
/sNzrOXugwAIyPdIJZkIv1275OZELTMDgWfU3DNwkwQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.3/DistributionSummary.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DNS Easy Switcher</key>
<array>
<dict>
<key>architectures</key>
<array>
<string>x86_64</string>
<string>arm64</string>
</array>
<key>buildNumber</key>
<string>4</string>
<key>certificate</key>
<dict>
<key>SHA1</key>
<string>8FB842802AF0A81CD08194DB5E6CA6B5FB771823</string>
<key>dateExpires</key>
<string>26/02/2030</string>
<key>type</key>
<string>Developer ID Application</string>
</dict>
<key>name</key>
<string>DNS Easy Switcher.app</string>
<key>team</key>
<dict>
<key>id</key>
<string>6645MJMX63</string>
<key>name</key>
<string>Gregory Linford</string>
</dict>
<key>versionNumber</key>
<string>1.0.3</string>
</dict>
</array>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.3/ExportOptions.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>developer-id</string>
<key>signingStyle</key>
<string>automatic</string>
<key>teamID</key>
<string>6645MJMX63</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.3/Packaging.log
================================================
2025-02-25 22:00:11 +0000 Initial pipeline context: <IDEDistributionProcessingPipelineContext: 0x12ff20ce0; archive(resolved)="<IDEArchive: 0x600008e05380>", distributionTask(resolved)="2", distributionDestination(resolved)="1", distributionMethod(resolved)="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team(resolved)="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
Chain (13, self inclusive):
<IDEDistributionProcessingPipelineContext: 0x12ff20ce0; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionProcessingPipelineContext: 0x12ff20a40; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x1691170b0; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x313e57e20; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x10ba89ed0; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x12ff143a0; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x12ff13ff0; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x12ff14900; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x10bafd670; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="<IDEProvisioningDisambiguatableBasicTeam: 0x6000071c8400; teamID='6645MJMX63', teamName='Gregory Linford', teamType='Individual', username='greglin19@gmail.com', isFreeProvisioningTeam='0'>">
<IDEDistributionContext: 0x3231a3910; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="(null)">
<IDEDistributionContext: 0x362b199e0; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="<IDEDistributionMethodDeveloperID: 0x60000a9d6ab0>", team="(null)">
<IDEDistributionContext: 0x323321b70; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="(null)", team="(null)">
<IDEDistributionContext: 0x33f8d9800; archive = "<IDEArchive: 0x600008e05380>", distributionMethod="(null)", team="(null)">
</IDEDistributionProcessingPipelineContext: 0x12ff20ce0>
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionCreateDestRootStep
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionCopyItemStep
2025-02-25 22:00:11 +0000 Running /usr/bin/ditto '-V' '/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app' '/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app'
2025-02-25 22:00:11 +0000 >>> Copying /Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app
2025-02-25 22:00:11 +0000 copying file ./Contents/_CodeSignature/CodeResources ...
2025-02-25 22:00:11 +0000 2672 bytes for ./Contents/_CodeSignature/CodeResources
2025-02-25 22:00:11 +0000 copying file ./Contents/MacOS/DNS Easy Switcher ...
2025-02-25 22:00:11 +0000 664176 bytes for ./Contents/MacOS/DNS Easy Switcher
2025-02-25 22:00:11 +0000 copying file ./Contents/Resources/AppIcon.icns ...
2025-02-25 22:00:11 +0000 67535 bytes for ./Contents/Resources/AppIcon.icns
2025-02-25 22:00:11 +0000 copying file ./Contents/Resources/Assets.car ...
2025-02-25 22:00:11 +0000 745832 bytes for ./Contents/Resources/Assets.car
2025-02-25 22:00:11 +0000 copying file ./Contents/Info.plist ...
2025-02-25 22:00:11 +0000 1572 bytes for ./Contents/Info.plist
2025-02-25 22:00:11 +0000 copying file ./Contents/PkgInfo ...
2025-02-25 22:00:11 +0000 8 bytes for ./Contents/PkgInfo
2025-02-25 22:00:11 +0000 /usr/bin/ditto exited with 0
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionEmbedProfileStep
2025-02-25 22:00:11 +0000 Skipping profile for item: <IDEDistributionItem: 0x6000041be5e0; bundleID='com.linfordsoftware.dnseasyswitcher', path='<DVTFilePath:0x60000400ba30:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x60000ca58eb0; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x6000011c8000; name='Apple Development: Gregory Linford (XGV4H982AX)', hash='75EA596AB87130E42C8EC49DCDCA8469458315F6', serialNumber='148CC429E2C0F0690B09BCCED6AB40F3', certificateKinds='(
"1.2.840.113635.100.6.1.12",
"1.2.840.113635.100.6.1.2"
), issueDate='2025-02-23 14:00:58 +0000''>', entitlements='{
}', teamID='6645MJMX63', identifier='com.linfordsoftware.dnseasyswitcher', executablePath='<DVTFilePath:0x600004009180:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app/Contents/MacOS/DNS Easy Switcher'>', hardenedRuntime='1'>'>
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionInfoPlistStep
2025-02-25 22:00:11 +0000 Skipping step: IDEDistributionInfoPlistStep because it said so
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionAppThinningPlistStep
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionCompileBitcodeStep
2025-02-25 22:00:11 +0000 Skipping step: IDEDistributionCompileBitcodeStep because it said so
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionCodeSlimmingStep
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionCopyBCSymbolMapsStep
2025-02-25 22:00:11 +0000 Skipping step: IDEDistributionCopyBCSymbolMapsStep because it said so
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionSymbolsStep
2025-02-25 22:00:11 +0000 Skipping step: IDEDistributionSymbolsStep because it said so
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionAppThinningStep
2025-02-25 22:00:11 +0000 Skipping step: IDEDistributionAppThinningStep because it said so
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionArchThinningStep
2025-02-25 22:00:11 +0000 Running /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo '/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app/Contents/MacOS/DNS Easy Switcher' '-verify_arch' 'arm64e'
2025-02-25 22:00:11 +0000 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo exited with 1
2025-02-25 22:00:11 +0000 Skipping architecture thinning for item "DNS Easy Switcher" because arch "arm64e" wasn't found
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionODRStep
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionStripXattrsStep
2025-02-25 22:00:11 +0000 Skipping stripping extended attributes because the codesign step will strip them.
2025-02-25 22:00:11 +0000 Processing step: IDEDistributionCodesignStep
2025-02-25 22:00:11 +0000 Entitlements for <IDEDistributionItem: 0x6000041be5e0; bundleID='com.linfordsoftware.dnseasyswitcher', path='<DVTFilePath:0x60000400ba30:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x60000ca58eb0; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x600001e7cf00; name='Apple Development: Gregory Linford (XGV4H982AX)', hash='75EA596AB87130E42C8EC49DCDCA8469458315F6', serialNumber='148CC429E2C0F0690B09BCCED6AB40F3', certificateKinds='(
"1.2.840.113635.100.6.1.12",
"1.2.840.113635.100.6.1.2"
), issueDate='2025-02-23 14:00:58 +0000''>', entitlements='{
}', teamID='6645MJMX63', identifier='com.linfordsoftware.dnseasyswitcher', executablePath='<DVTFilePath:0x600004009180:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app/Contents/MacOS/DNS Easy Switcher'>', hardenedRuntime='1'>'>: {
}
2025-02-25 22:00:11 +0000 Associated App Clip Identifiers Filter: Skipping because "com.apple.developer.associated-appclip-app-identifiers" is not present
2025-02-25 22:00:11 +0000 Entitlements for <IDEDistributionItem: 0x6000041be5e0; bundleID='com.linfordsoftware.dnseasyswitcher', path='<DVTFilePath:0x60000400ba30:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x60000ca58eb0; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x600001e7d980; name='Apple Development: Gregory Linford (XGV4H982AX)', hash='75EA596AB87130E42C8EC49DCDCA8469458315F6', serialNumber='148CC429E2C0F0690B09BCCED6AB40F3', certificateKinds='(
"1.2.840.113635.100.6.1.12",
"1.2.840.113635.100.6.1.2"
), issueDate='2025-02-23 14:00:58 +0000''>', entitlements='{
}', teamID='6645MJMX63', identifier='com.linfordsoftware.dnseasyswitcher', executablePath='<DVTFilePath:0x600004009180:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app/Contents/MacOS/DNS Easy Switcher'>', hardenedRuntime='1'>'> are: {
}
2025-02-25 22:00:11 +0000 Writing entitlements for <IDEDistributionItem: 0x6000041be5e0; bundleID='com.linfordsoftware.dnseasyswitcher', path='<DVTFilePath:0x60000400ba30:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app'>', codeSigningInfo='<_DVTCodeSigningInformation_Path: 0x60000ca58eb0; isSigned='1', isAdHocSigned='0', signingCertificate='<DVTSigningCertificate: 0x600001e7d380; name='Apple Development: Gregory Linford (XGV4H982AX)', hash='75EA596AB87130E42C8EC49DCDCA8469458315F6', serialNumber='148CC429E2C0F0690B09BCCED6AB40F3', certificateKinds='(
"1.2.840.113635.100.6.1.12",
"1.2.840.113635.100.6.1.2"
), issueDate='2025-02-23 14:00:58 +0000''>', entitlements='{
}', teamID='6645MJMX63', identifier='com.linfordsoftware.dnseasyswitcher', executablePath='<DVTFilePath:0x600004009180:'/Users/glinf/Library/Developer/Xcode/Archives/2025-02-25/DNS Easy Switcher 25-02-2025, 22.59.xcarchive/Products/Applications/DNS Easy Switcher.app/Contents/MacOS/DNS Easy Switcher'>', hardenedRuntime='1'>'> to: /var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/entitlements~~~Eyl0xd
2025-02-25 22:00:11 +0000 Running /usr/bin/codesign '-vvv' '--force' '--sign' '8FB842802AF0A81CD08194DB5E6CA6B5FB771823' '--entitlements' '/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/entitlements~~~Eyl0xd' '--generate-entitlement-der' '--preserve-metadata=identifier,flags,runtime' '--requirements' '=designated => anchor apple generic and identifier "$self.identifier" and ((cert leaf[field.1.2.840.113635.100.6.1.9] exists) or ( certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists and certificate leaf[subject.OU] = "6645MJMX63" ))' '/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app'
2025-02-25 22:00:11 +0000 /var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app: replacing existing signature
2025-02-25 22:00:23 +0000 /var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app: signed app bundle with Mach-O universal (x86_64 arm64) [com.linfordsoftware.dnseasyswitcher]
2025-02-25 22:00:23 +0000 /usr/bin/codesign exited with 0
2025-02-25 22:00:23 +0000 Processing step: IDEDistributionZipODRItemStep
2025-02-25 22:00:23 +0000 Skipping step: IDEDistributionZipODRItemStep because it said so
2025-02-25 22:00:23 +0000 Processing step: IDEDistributionSkipPackagingStep
2025-02-25 22:00:23 +0000 Processing step: IDEDistributionAppStoreInformationStep
2025-02-25 22:00:23 +0000 Skipping step: IDEDistributionAppStoreInformationStep because it said so
2025-02-25 22:00:23 +0000 Processing step: IDEDistributionGenerateProcessedDistributionItems
2025-02-25 22:00:23 +0000 IDEDistributionItem init <DVTFilePath:0x60000438c460:'/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app'>
2025-02-25 22:00:23 +0000 [OPTIONAL] Didn't find embedded provisioning profile for <DVTFilePath:0x60000438c460:'/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app'>: Error Domain=NSCocoaErrorDomain Code=4 "No file at <DVTFilePath:0x6000047c8700:'/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app/Contents/embedded.provisionprofile'>" UserInfo={NSLocalizedDescription=No file at <DVTFilePath:0x6000047c8700:'/var/folders/33/r3mtzdn102x40vpjzrzm9kyh0000gn/T/XcodeDistPipeline.~~~8zKmE7/Root/Applications/DNS Easy Switcher.app/Contents/embedded.provisionprofile'>}
2025-02-25 22:00:23 +0000 Processing step: IDEDistributionCreateManifestStep
2025-02-25 22:00:23 +0000 Skipping step: IDEDistributionCreateManifestStep because it said so
================================================
FILE: Releases/DNS Easy Switcher v1.0.4/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.4</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>4</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.0</string>
<key>DTSDKBuild</key>
<string>23A334</string>
<key>DTSDKName</key>
<string>macosx14.0</string>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.4/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.4/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
IhhuUihlv8TNfeE5CB9OZcOX21w=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
/sNzrOXugwAIyPdIJZkIv1275OZELTMDgWfU3DNwkwQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.5/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.5</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>5</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.0</string>
<key>DTSDKBuild</key>
<string>23A334</string>
<key>DTSDKName</key>
<string>macosx14.0</string>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.5/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.5/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
IhhuUihlv8TNfeE5CB9OZcOX21w=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
/sNzrOXugwAIyPdIJZkIv1275OZELTMDgWfU3DNwkwQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.6/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.5</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>7</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string></string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>14.0</string>
<key>DTSDKBuild</key>
<string>23A334</string>
<key>DTSDKName</key>
<string>macosx14.0</string>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.6/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.6/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
IhhuUihlv8TNfeE5CB9OZcOX21w=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
/sNzrOXugwAIyPdIJZkIv1275OZELTMDgWfU3DNwkwQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.7/DNS Easy Switcher.app/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>24F74</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>DNS Easy Switcher</string>
<key>CFBundleExecutable</key>
<string>DNS Easy Switcher</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.linfordsoftware.dnseasyswitcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>DNS Easy Switcher</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.7</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>24F74</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>15.5</string>
<key>DTSDKBuild</key>
<string>24F74</string>
<key>DTSDKName</key>
<string>macosx15.5</string>
<key>DTXcode</key>
<string>1640</string>
<key>DTXcodeBuild</key>
<string>16F6</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
</dict>
</plist>
================================================
FILE: Releases/DNS Easy Switcher v1.0.7/DNS Easy Switcher.app/Contents/PkgInfo
================================================
APPL????
================================================
FILE: Releases/DNS Easy Switcher v1.0.7/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
F6YGlmox4KixBMRrSRhTusxdld8=
</data>
<key>Resources/Assets.car</key>
<data>
3jqFr5HGKA9n2WOV5/Xl44NZRrQ=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
I0wlcCvBzQjHkKQ1llwB0poCp2bmfglbZW45inQqQV0=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
YYZb1v3omIkmYQEJ3ZIxWpYeBvtIZn01EGvVrO8Dau4=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>
gitextract_u_joqz4e/
├── .gitignore
├── DNS Easy Switcher/
│ ├── AboutView.swift
│ ├── AddCustomDNSView.swift
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── CustomDNSManagerView.swift
│ ├── CustomSheet.swift
│ ├── DNSManager.swift
│ ├── DNSSettings.swift
│ ├── DNSSpeedTester.swift
│ ├── DNS_Easy_Switcher.entitlements
│ ├── DNS_Easy_SwitcherApp.swift
│ ├── EditCustomDNSView.swift
│ ├── MenuBarController.swift
│ ├── MenuBarView.swift
│ └── Preview Content/
│ └── Preview Assets.xcassets/
│ └── Contents.json
├── DNS Easy Switcher.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata/
│ └── xcschemes/
│ └── DNS Easy Switcher.xcscheme
├── LICENSE
├── README.md
└── Releases/
├── DNS Easy Switcher v1.0.0/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.1/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.2/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.3/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ ├── DNS Easy Switcher.dmg
│ ├── DistributionSummary.plist
│ ├── ExportOptions.plist
│ └── Packaging.log
├── DNS Easy Switcher v1.0.4/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── CodeResources
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.5/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── CodeResources
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
├── DNS Easy Switcher v1.0.6/
│ ├── DNS Easy Switcher.app/
│ │ └── Contents/
│ │ ├── CodeResources
│ │ ├── Info.plist
│ │ ├── MacOS/
│ │ │ └── DNS Easy Switcher
│ │ ├── PkgInfo
│ │ ├── Resources/
│ │ │ ├── AppIcon.icns
│ │ │ └── Assets.car
│ │ └── _CodeSignature/
│ │ └── CodeResources
│ └── DNS Easy Switcher.dmg
└── DNS Easy Switcher v1.0.7/
├── DNS Easy Switcher.app/
│ └── Contents/
│ ├── CodeResources
│ ├── Info.plist
│ ├── MacOS/
│ │ └── DNS Easy Switcher
│ ├── PkgInfo
│ ├── Resources/
│ │ ├── AppIcon.icns
│ │ └── Assets.car
│ └── _CodeSignature/
│ └── CodeResources
└── DNS Easy Switcher.dmg
Condensed preview — 86 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (154K chars).
[
{
"path": ".gitignore",
"chars": 1603,
"preview": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n"
},
{
"path": "DNS Easy Switcher/AboutView.swift",
"chars": 1201,
"preview": "//\n// AboutView.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 27/02/2025.\n//\n\nimport SwiftUI\n\nstruct"
},
{
"path": "DNS Easy Switcher/AddCustomDNSView.swift",
"chars": 2526,
"preview": "//\n// AddCustomDNSView.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 23/02/2025.\n//\n\nimport SwiftUI\n"
},
{
"path": "DNS Easy Switcher/Assets.xcassets/AccentColor.colorset/Contents.json",
"chars": 123,
"preview": "{\n \"colors\" : [\n {\n \"idiom\" : \"universal\"\n }\n ],\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }"
},
{
"path": "DNS Easy Switcher/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1552,
"preview": "{\"images\":[{\"size\":\"1024x1024\",\"filename\":\"1024-mac.png\",\"expected-size\":\"1024\",\"idiom\":\"ios-marketing\",\"folder\":\"Assets"
},
{
"path": "DNS Easy Switcher/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "DNS Easy Switcher/CustomDNSManagerView.swift",
"chars": 2472,
"preview": "//\n// CustomDNSManagerView.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 25/02/2025.\n//\n\nimport Swif"
},
{
"path": "DNS Easy Switcher/CustomSheet.swift",
"chars": 652,
"preview": "//\n// CustomSheet.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 23/02/2025.\n//\n\nimport SwiftUI\nimpor"
},
{
"path": "DNS Easy Switcher/DNSManager.swift",
"chars": 16715,
"preview": "//\n// DNSManager.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 23/02/2025.\n//\n\nimport Foundation\nimp"
},
{
"path": "DNS Easy Switcher/DNSSettings.swift",
"chars": 2147,
"preview": "//\n// DNSSettings.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 23/02/2025.\n//\n\nimport Foundation\nim"
},
{
"path": "DNS Easy Switcher/DNSSpeedTester.swift",
"chars": 7885,
"preview": "//\n// DNSSpeedTester.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 25/02/2025.\n//\n\nimport Foundation"
},
{
"path": "DNS Easy Switcher/DNS_Easy_Switcher.entitlements",
"chars": 181,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "DNS Easy Switcher/DNS_Easy_SwitcherApp.swift",
"chars": 1799,
"preview": "//\n// DNS_Easy_SwitcherApp.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 23/02/2025.\n//\n\nimport Swif"
},
{
"path": "DNS Easy Switcher/EditCustomDNSView.swift",
"chars": 3110,
"preview": "//\n// EditCustomDNSView.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 25/02/2025.\n//\n\nimport SwiftUI"
},
{
"path": "DNS Easy Switcher/MenuBarController.swift",
"chars": 295,
"preview": "//\n// MenuBarController.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 23/02/2025.\n//\n\nimport Foundat"
},
{
"path": "DNS Easy Switcher/MenuBarView.swift",
"chars": 21354,
"preview": "//\n// MenuBarView.swift\n// DNS Easy Switcher\n//\n// Created by Gregory LINFORD on 23/02/2025.\n//\nimport SwiftUI\nimport"
},
{
"path": "DNS Easy Switcher/Preview Content/Preview Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "DNS Easy Switcher.xcodeproj/project.pbxproj",
"chars": 17792,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 56;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "DNS Easy Switcher.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "DNS Easy Switcher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "DNS Easy Switcher.xcodeproj/xcshareddata/xcschemes/DNS Easy Switcher.xcscheme",
"chars": 2934,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1500\"\n version = \"1.7\">\n <BuildAction\n "
},
{
"path": "LICENSE",
"chars": 1061,
"preview": "MIT License\n\nCopyright (c) 2025 Greg\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof th"
},
{
"path": "README.md",
"chars": 3041,
"preview": "# DNS Easy Switcher\n\nA simple macOS menu bar app that allows you to quickly switch between different DNS providers (or a"
},
{
"path": "Releases/DNS Easy Switcher v1.0.0/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1484,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.0/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.0/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.1/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.1/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.1/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.2/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.2/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.2/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.3/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.3/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.3/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.3/DistributionSummary.plist",
"chars": 926,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.3/ExportOptions.plist",
"chars": 388,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.3/Packaging.log",
"chars": 15541,
"preview": "2025-02-25 22:00:11 +0000 Initial pipeline context: <IDEDistributionProcessingPipelineContext: 0x12ff20ce0; archive(res"
},
{
"path": "Releases/DNS Easy Switcher v1.0.4/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.4/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.4/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.5/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.5/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.5/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.6/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.6/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.6/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.7/DNS Easy Switcher.app/Contents/Info.plist",
"chars": 1574,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Releases/DNS Easy Switcher v1.0.7/DNS Easy Switcher.app/Contents/PkgInfo",
"chars": 8,
"preview": "APPL????"
},
{
"path": "Releases/DNS Easy Switcher v1.0.7/DNS Easy Switcher.app/Contents/_CodeSignature/CodeResources",
"chars": 2672,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
}
]
// ... and 36 more files (download for full content)
About this extraction
This page contains the full source code of the glinford/dns-easy-switcher GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 86 files (136.5 KB), approximately 43.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.