Full Code of glinford/dns-easy-switcher for AI

main 899f42346182 cached
86 files
136.5 KB
43.7k tokens
1 requests
Download .txt
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).

![Screenshot of DNS Easy Switcher](screenshot.png)

![downloads](https://img.shields.io/github/downloads/glinford/dns-easy-switcher/total)

## 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)

![settings](settings.png)

## 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>
Download .txt
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.

Copied to clipboard!