├── .DS_Store ├── Easy Ethernet Icon ├── .DS_Store ├── Easy Ethernet Icon │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── .DS_Store │ │ ├── AppIcon.appiconset │ │ │ ├── 16.png │ │ │ ├── 32.png │ │ │ ├── 64.png │ │ │ ├── 128.png │ │ │ ├── 256.png │ │ │ ├── 32 1.png │ │ │ └── Contents.json │ │ ├── macOS_EthernetDisconnected.imageset │ │ │ ├── .DS_Store │ │ │ ├── Default_EthernetConnected Kopie.png │ │ │ └── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── Default_EthernetDisconnected.imageset │ │ │ ├── .DS_Store │ │ │ ├── icons8-kein-netzwerk-96.png │ │ │ └── Contents.json │ │ ├── macOS_EthernetConnected.imageset │ │ │ ├── Default_EthernetConnected.png │ │ │ └── Contents.json │ │ └── Default_EthernetConnected.imageset │ │ │ ├── icons8-wired-network-connection-96(1).png │ │ │ └── Contents.json │ ├── .DS_Store │ ├── Preview Content │ │ ├── Preview Assets.xcassets │ │ │ └── Contents.json │ │ └── .DS_Store │ └── Sources │ │ ├── Main.swift │ │ ├── NetworkMonitor.swift │ │ ├── AppDelegate.swift │ │ ├── ApplicationMenu.swift │ │ └── SettingsView.swift └── Easy Ethernet Icon.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── felix.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── swiftpm │ │ └── Package.resolved │ ├── xcuserdata │ └── felix.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ └── project.pbxproj └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/.DS_Store -------------------------------------------------------------------------------- /Easy Ethernet Icon/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/.DS_Store -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/.DS_Store -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Preview Content/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Preview Content/.DS_Store -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/32 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/32 1.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetDisconnected.imageset/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetDisconnected.imageset/.DS_Store -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetDisconnected.imageset/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetDisconnected.imageset/.DS_Store -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetConnected.imageset/Default_EthernetConnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetConnected.imageset/Default_EthernetConnected.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetDisconnected.imageset/icons8-kein-netzwerk-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetDisconnected.imageset/icons8-kein-netzwerk-96.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetDisconnected.imageset/Default_EthernetConnected Kopie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetDisconnected.imageset/Default_EthernetConnected Kopie.png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon.xcodeproj/project.xcworkspace/xcuserdata/felix.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon.xcodeproj/project.xcworkspace/xcuserdata/felix.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetConnected.imageset/icons8-wired-network-connection-96(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixblome/easy-ethernet-icon/HEAD/Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetConnected.imageset/icons8-wired-network-connection-96(1).png -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetDisconnected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icons8-kein-netzwerk-96.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetConnected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Default_EthernetConnected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/macOS_EthernetDisconnected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Default_EthernetConnected Kopie.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/Default_EthernetConnected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icons8-wired-network-connection-96(1).png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "aae61040c30e63b9cf0f5455365680cfeabef0aac829c0941d4d6f6180010fc9", 3 | "pins" : [ 4 | { 5 | "identity" : "launchatlogin-modern", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/sindresorhus/LaunchAtLogin-Modern", 8 | "state" : { 9 | "branch" : "main", 10 | "revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc" 11 | } 12 | } 13 | ], 14 | "version" : 3 15 | } 16 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon.xcodeproj/xcuserdata/felix.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Easy Ethernet Icon.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | Ethernet Menu Icon.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Sources/Main.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Cocoa 3 | import Network 4 | 5 | /// Custom notification for icon style changes 6 | extension Notification.Name { 7 | static let iconStyleChanged = Notification.Name("iconStyleChanged") 8 | } 9 | 10 | /// Main application structure 11 | @main 12 | struct Ethernet_Menu_IconApp: App { 13 | // Links the AppDelegate to the SwiftUI application lifecycle 14 | @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 15 | 16 | var body: some Scene { 17 | // Creates the settings scene for the application 18 | Settings { 19 | SettingsView() 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "32 1.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "64.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "256.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "idiom" : "mac", 41 | "scale" : "1x", 42 | "size" : "256x256" 43 | }, 44 | { 45 | "idiom" : "mac", 46 | "scale" : "2x", 47 | "size" : "256x256" 48 | }, 49 | { 50 | "idiom" : "mac", 51 | "scale" : "1x", 52 | "size" : "512x512" 53 | }, 54 | { 55 | "idiom" : "mac", 56 | "scale" : "2x", 57 | "size" : "512x512" 58 | } 59 | ], 60 | "info" : { 61 | "author" : "xcode", 62 | "version" : 1 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easy Ethernet Icon for macOS 2 | 3 | ⚠️ THE PROJECT IS NO LONGER MAINTAINED! ⚠️ 4 | 5 | A simple and lightweight macOS menu bar application that shows your Ethernet connection status at a glance. 6 | 7 | ![Different icon styles](https://i.ibb.co/kD2KbCs/win.png) 8 | 9 | ## Features 10 | - 🔌 Live ethernet connection status monitoring 11 | - 📊 Live connection speed monitoring 12 | - 🎨 Choice between macOS and Windows style icons 13 | - 🚀 Launch at Login support 14 | - 🏃‍♂️ Lightweight and efficient 15 | 16 | ## System Requirements 17 | - macOS 13.5 or newer 18 | 19 | ## Installation 20 | 1. Download the latest release from the [Releases page](../../releases) 21 | 2. Unzip the downloaded file 22 | 3. Drag the app to your Applications folder 23 | 4. Double click to start the app 24 | 5. If the app cannot be opened due to security warnings: 25 | - Go to System Preferences > Security & Privacy > Scroll down to "Security" 26 | - Click Open Anyway next to the blocked app 27 | 7. (Optional) Click the menu bar icon and select Settings to customize 28 | 29 | ## Usage 30 | - The icon in the menu bar shows your current Ethernet connection status and (if enabled) the connection speed 31 | - Click the icon to: 32 | - See connection status 33 | - See connection speed 34 | - Access Network Settings 35 | - Configure app settings 36 | - Quit the application 37 | 38 | ## Build from Source 39 | If you want to build the app yourself: 40 | 1. Clone this repository 41 | 2. Open the project in Xcode 42 | 3. Ensure you have Xcode 14 or newer 43 | 4. Build the project (⌘B) 44 | 45 | ## Privacy 46 | This app: 47 | - Only monitors the ethernet connection status 48 | 49 | --- 50 | Made by Felix Blome 51 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Sources/NetworkMonitor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import QuartzCore 3 | 4 | class NetworkMonitor { 5 | private var monitorTimer: DispatchSourceTimer? 6 | private var lastDataReceived: UInt64 = 0 7 | private var lastDataSent: UInt64 = 0 8 | private var lastUpdateTime: TimeInterval = 0 9 | 10 | var onSpeedUpdate: ((Double, Double) -> Void)? 11 | 12 | func startMonitoring() { 13 | let refreshInterval = UserDefaults.standard.double(forKey: "refreshInterval") 14 | 15 | monitorTimer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .background)) 16 | monitorTimer?.schedule(deadline: .now(), repeating: refreshInterval) 17 | monitorTimer?.setEventHandler { [weak self] in 18 | self?.updateNetworkUsage() 19 | } 20 | monitorTimer?.resume() 21 | } 22 | 23 | func stopMonitoring() { 24 | monitorTimer?.cancel() 25 | monitorTimer = nil 26 | } 27 | 28 | private func updateNetworkUsage() { 29 | guard let counters = getNetworkCounters() else { return } 30 | 31 | let currentTime = CACurrentMediaTime() 32 | let timeInterval = currentTime - lastUpdateTime 33 | 34 | let useKilobytes = UserDefaults.standard.string(forKey: "speedUnit") == "KB/s" 35 | let divisor = useKilobytes ? 1024.0 : 1_048_576.0 36 | 37 | var downloadSpeed: Double 38 | var uploadSpeed: Double 39 | 40 | if counters.received >= lastDataReceived { 41 | let bytesReceived = Double(counters.received - lastDataReceived) 42 | downloadSpeed = (bytesReceived / timeInterval) / divisor 43 | } else { 44 | let bytesReceived = Double(UInt64.max - lastDataReceived + counters.received) 45 | downloadSpeed = (bytesReceived / timeInterval) / divisor 46 | } 47 | 48 | if counters.sent >= lastDataSent { 49 | let bytesSent = Double(counters.sent - lastDataSent) 50 | uploadSpeed = (bytesSent / timeInterval) / divisor 51 | } else { 52 | let bytesSent = Double(UInt64.max - lastDataSent + counters.sent) 53 | uploadSpeed = (bytesSent / timeInterval) / divisor 54 | } 55 | 56 | lastDataReceived = counters.received 57 | lastDataSent = counters.sent 58 | lastUpdateTime = currentTime 59 | 60 | DispatchQueue.main.async { 61 | self.onSpeedUpdate?(downloadSpeed, uploadSpeed) 62 | } 63 | } 64 | 65 | private func getNetworkCounters() -> (received: UInt64, sent: UInt64)? { 66 | var ifaddrs: UnsafeMutablePointer? = nil 67 | guard getifaddrs(&ifaddrs) == 0, let firstAddr = ifaddrs else { return nil } 68 | defer { freeifaddrs(ifaddrs) } 69 | 70 | var dataReceived: UInt64 = 0 71 | var dataSent: UInt64 = 0 72 | 73 | for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) { 74 | let flags = Int32(ifptr.pointee.ifa_flags) 75 | let isUp = (flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING) 76 | let isLoopback = (flags & IFF_LOOPBACK) != 0 77 | 78 | if !isUp || isLoopback { continue } 79 | 80 | if let data = ifptr.pointee.ifa_data?.assumingMemoryBound(to: if_data.self) { 81 | dataReceived += UInt64(data.pointee.ifi_ibytes) 82 | dataSent += UInt64(data.pointee.ifi_obytes) 83 | } 84 | } 85 | 86 | return (dataReceived, dataSent) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Sources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Cocoa 3 | 4 | /// The AppDelegate class handles the core functionality of the menu bar application 5 | /// It manages the status bar item (icon in the menu bar) and handles connection status updates 6 | class AppDelegate: NSObject, NSApplicationDelegate { 7 | // Singleton instance of AppDelegate that can be accessed throughout the app 8 | static private(set) var instance: AppDelegate! 9 | 10 | // View that handles application settings 11 | private var settingsView = SettingsView() 12 | 13 | // Fixed size for the menu bar icon (20x20 pixels) 14 | private let fixedImageSize = 20 15 | 16 | // The actual menu bar item that shows in the system status bar 17 | lazy var statusBarItem = NSStatusBar.system.statusItem(withLength: 20) 18 | 19 | // Instance of ApplicationMenu that handles menu creation and ethernet monitoring 20 | let menu = ApplicationMenu() 21 | 22 | /// Called when the application finishes launching 23 | func applicationDidFinishLaunching(_ notification: Notification) { 24 | // Set up singleton instance 25 | AppDelegate.instance = self 26 | 27 | // Listen for changes in icon style from settings 28 | NotificationCenter.default.addObserver( 29 | self, 30 | selector: #selector(updateIcon), 31 | name: .iconStyleChanged, 32 | object: nil 33 | ) 34 | 35 | // Start monitoring ethernet connection and update the status icon 36 | menu.startMonitoringEthernetStatus { status in 37 | DispatchQueue.main.async { 38 | self.updateStatusBarIcon(status: status) 39 | } 40 | } 41 | 42 | // Configure the status bar item 43 | setupStatusBarItem() 44 | } 45 | 46 | /// Sets up the initial status bar item configuration 47 | private func setupStatusBarItem() { 48 | statusBarItem.button?.imagePosition = .imageLeading 49 | statusBarItem.menu = menu.createMenu() 50 | } 51 | 52 | /// Updates the status bar icon when settings change 53 | @objc func updateIcon() { 54 | self.statusBarItem.button?.image = self.getConnectionImage( 55 | status: .Disconnected, 56 | setting: self.settingsView 57 | ) 58 | } 59 | 60 | /// Updates the status bar icon with current connection status 61 | private func updateStatusBarIcon(status: ApplicationMenu.ConnectionStatus) { 62 | self.statusBarItem.button?.imagePosition = .imageOnly 63 | self.statusBarItem.button?.imageScaling = .scaleNone 64 | self.statusBarItem.button?.image = self.getConnectionImage( 65 | status: status, 66 | setting: self.settingsView 67 | ) 68 | self.statusBarItem.button?.image?.size = NSSize( 69 | width: self.fixedImageSize, 70 | height: self.fixedImageSize 71 | ) 72 | self.statusBarItem.button?.image?.isTemplate = true 73 | } 74 | 75 | /// Returns the appropriate icon image based on connection status and user settings 76 | func getConnectionImage( 77 | status: ApplicationMenu.ConnectionStatus, 78 | setting: SettingsView 79 | ) -> NSImage { 80 | // Choose icon based on selected style (macOS or default/Windows style) 81 | let imageName = setting.selectedOption == "macOS" 82 | ? "macOS_Ethernet\(status == .Connected ? "Connected" : "Disconnected")" 83 | : "Default_Ethernet\(status == .Connected ? "Connected" : "Disconnected")" 84 | 85 | let image = NSImage(named: imageName) ?? NSImage() 86 | image.size = NSSize(width: self.fixedImageSize, height: self.fixedImageSize) 87 | return image 88 | } 89 | 90 | /// Triggers a refresh of the status icon 91 | func updateStatusIcon() { 92 | menu.startMonitoringEthernetStatus { status in 93 | DispatchQueue.main.async { 94 | self.updateStatusBarIcon(status: status) 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Sources/ApplicationMenu.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import SwiftUI 3 | import Network 4 | 5 | /// Manages the application's menu and monitors ethernet connection status 6 | class ApplicationMenu: NSObject, NSWindowDelegate { 7 | // Main menu instance 8 | let menu = NSMenu() 9 | 10 | /// Represents the possible states of ethernet connection 11 | enum ConnectionStatus { 12 | case Connected 13 | case Disconnected 14 | } 15 | 16 | // Menu items 17 | let ethernetStatusItem = NSMenuItem( 18 | title: "Checking Ethernet Status...", 19 | action: nil, 20 | keyEquivalent: "" 21 | ) 22 | let speedStatusItem = NSMenuItem( 23 | title: "Speed: -", 24 | action: nil, 25 | keyEquivalent: "" 26 | ) 27 | let quitApplicationItem = NSMenuItem( 28 | title: "Quit Application", 29 | action: #selector(quitApplication), 30 | keyEquivalent: "q" 31 | ) 32 | let networkSettingsItem = NSMenuItem( 33 | title: "Open Network Settings", 34 | action: #selector(openNetworkSettings), 35 | keyEquivalent: "n" 36 | ) 37 | let settingsItem = NSMenuItem( 38 | title: "Settings", 39 | action: #selector(openSettings), 40 | keyEquivalent: "s" 41 | ) 42 | 43 | // Settings window reference 44 | var settingsPanel: NSPanel? 45 | 46 | // Reference to NetworkMonitor 47 | private let networkMonitor = NetworkMonitor() 48 | 49 | override init() { 50 | super.init() 51 | setupMenuItems() 52 | setupSpeedMonitoring() 53 | 54 | // Observe changes to showConnectionSpeed setting 55 | NotificationCenter.default.addObserver( 56 | self, 57 | selector: #selector(handleSpeedSettingChange), 58 | name: UserDefaults.didChangeNotification, 59 | object: nil 60 | ) 61 | } 62 | 63 | private func setupSpeedMonitoring() { 64 | // Setup speed monitoring callback 65 | networkMonitor.onSpeedUpdate = { [weak self] download, upload in 66 | guard let self = self else { return } 67 | DispatchQueue.main.async { 68 | let unit = UserDefaults.standard.string(forKey: "speedUnit") ?? "MB/s" 69 | let speedText = String(format: "Speed: %.1f %@ ↓ | %.1f %@ ↑", download, unit, upload, unit) 70 | self.speedStatusItem.title = speedText 71 | } 72 | } 73 | 74 | // Start monitoring only if enabled in settings 75 | updateSpeedMonitoring() 76 | } 77 | 78 | @objc private func handleSpeedSettingChange() { 79 | updateSpeedMonitoring() 80 | } 81 | 82 | private func updateSpeedMonitoring() { 83 | let showSpeed = UserDefaults.standard.bool(forKey: "showConnectionSpeed") 84 | 85 | if showSpeed { 86 | networkMonitor.startMonitoring() 87 | } else { 88 | networkMonitor.stopMonitoring() 89 | DispatchQueue.main.async { 90 | self.speedStatusItem.title = "Speed: -" 91 | } 92 | } 93 | } 94 | 95 | /// Sets up menu item targets 96 | private func setupMenuItems() { 97 | quitApplicationItem.target = self 98 | networkSettingsItem.target = self 99 | settingsItem.target = self 100 | } 101 | 102 | /// Creates and returns the configured menu 103 | func createMenu() -> NSMenu { 104 | menu.removeAllItems() // Clean up before adding items 105 | 106 | menu.addItem(ethernetStatusItem) 107 | menu.addItem(speedStatusItem) 108 | menu.addItem(NSMenuItem.separator()) 109 | menu.addItem(networkSettingsItem) 110 | menu.addItem(settingsItem) 111 | menu.addItem(NSMenuItem.separator()) 112 | menu.addItem(quitApplicationItem) 113 | 114 | return menu 115 | } 116 | 117 | /// Starts monitoring ethernet connection status 118 | func startMonitoringEthernetStatus(statusUpdate: @escaping (ConnectionStatus) -> Void) { 119 | let monitor = NWPathMonitor(requiredInterfaceType: .wiredEthernet) 120 | 121 | monitor.pathUpdateHandler = { path in 122 | let status: ConnectionStatus = path.status == .satisfied 123 | ? .Connected 124 | : .Disconnected 125 | 126 | statusUpdate(status) 127 | 128 | DispatchQueue.main.async { 129 | self.updateStatusMenuItem(status: status) 130 | } 131 | } 132 | 133 | monitor.start(queue: DispatchQueue.global(qos: .background)) 134 | } 135 | 136 | private func updateStatusMenuItem(status: ConnectionStatus) { 137 | self.ethernetStatusItem.title = "Ethernet: \(status == .Connected ? "Connected" : "Disconnected")" 138 | } 139 | 140 | func stopMonitoring() { 141 | networkMonitor.stopMonitoring() 142 | } 143 | 144 | /// Quits the application 145 | @objc func quitApplication() { 146 | stopMonitoring() 147 | NSApplication.shared.terminate(self) 148 | } 149 | 150 | /// Opens the settings panel 151 | @objc func openSettings() { 152 | NSApplication.shared.activate(ignoringOtherApps: true) 153 | 154 | if settingsPanel == nil { 155 | createSettingsPanel() 156 | } 157 | 158 | settingsPanel?.makeKeyAndOrderFront(nil) 159 | settingsPanel?.orderFrontRegardless() 160 | } 161 | 162 | /// Creates the settings panel 163 | private func createSettingsPanel() { 164 | let panel = NSPanel( 165 | contentRect: NSRect(x: 0, y: 0, width: 300, height: 200), 166 | styleMask: [.titled, .closable], 167 | backing: .buffered, 168 | defer: false 169 | ) 170 | 171 | panel.center() 172 | panel.setFrameAutosaveName("Settings") 173 | panel.contentView = NSHostingView(rootView: SettingsView()) 174 | panel.delegate = self 175 | panel.isFloatingPanel = true 176 | panel.level = .floating 177 | 178 | self.settingsPanel = panel 179 | } 180 | 181 | /// Opens system network settings 182 | @objc func openNetworkSettings() { 183 | if let url = URL(string: "x-apple.systempreferences:com.apple.preference.network") { 184 | NSWorkspace.shared.open(url) 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon/Sources/SettingsView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import LaunchAtLogin 3 | 4 | /// Enum for tab selection with three options 5 | enum SettingsTab: String, CaseIterable { 6 | case general = "General" 7 | case network = "Network" 8 | case about = "About" 9 | } 10 | 11 | /// Main settings view with tabs 12 | struct SettingsView: View { 13 | @State private var selectedTab: SettingsTab = .general 14 | @AppStorage("selectedOption") var selectedOption: String = "macOS" 15 | 16 | var body: some View { 17 | VStack(spacing: 0) { 18 | // Tab bar at the top 19 | HStack { 20 | ForEach(SettingsTab.allCases, id: \.self) { tab in 21 | Button(action: { selectedTab = tab }) { 22 | VStack(spacing: 4) { 23 | Image(systemName: icon(for: tab)) 24 | .font(.system(size: 18, weight: .medium)) 25 | .foregroundColor(selectedTab == tab ? .accentColor : .gray) 26 | Text(tab.rawValue) 27 | .font(.system(size: 12, weight: .medium)) 28 | .foregroundColor(selectedTab == tab ? .accentColor : .gray) 29 | } 30 | .padding(.horizontal, 16) 31 | .padding(.vertical, 10) 32 | } 33 | .buttonStyle(PlainButtonStyle()) 34 | } 35 | } 36 | .navigationTitle("Settings") 37 | .background(Color(NSColor.windowBackgroundColor).opacity(0.9)) 38 | .cornerRadius(8) 39 | .padding(.horizontal, 20) 40 | .padding(.top, 10) 41 | 42 | Divider() 43 | .padding(.vertical, 8) 44 | 45 | // Content area for selected tab 46 | ScrollView { 47 | VStack(alignment: .leading, spacing: 16) { 48 | switch selectedTab { 49 | case .general: 50 | GeneralSettingsView() 51 | case .network: 52 | NetworkSettingsView() 53 | case .about: 54 | AboutSettingsView() 55 | } 56 | } 57 | .padding(20) 58 | } 59 | } 60 | .frame(width: 420, height: 240) 61 | .background(Color(NSColor.windowBackgroundColor)) 62 | .cornerRadius(12) 63 | .shadow(radius: 8) 64 | .padding() 65 | } 66 | 67 | /// Returns the icon name for each tab 68 | private func icon(for tab: SettingsTab) -> String { 69 | switch tab { 70 | case .general: return "gearshape" 71 | case .network: return "network" 72 | case .about: return "info.circle" 73 | } 74 | } 75 | } 76 | 77 | /// General settings view content 78 | struct GeneralSettingsView: View { 79 | @AppStorage("selectedOption") var selectedOption: String = "macOS" 80 | 81 | var body: some View { 82 | VStack(alignment: .leading, spacing: 16) { 83 | // Launch at login toggle with aligned label and switch 84 | HStack(alignment: .center) { 85 | Text("Launch at login") 86 | .font(.system(size: 14)) 87 | .frame(width: 120, alignment: .leading) // Fixed width for alignment 88 | LaunchAtLogin.Toggle("") 89 | .toggleStyle(SwitchToggleStyle(tint: .red)) // Custom color for switch 90 | } 91 | .padding(.horizontal, 16) 92 | 93 | // Icon Style selector with aligned label and dropdown 94 | HStack(alignment: .center) { 95 | Text("Icon Style:") 96 | .font(.system(size: 14)) 97 | .frame(width: 120, alignment: .leading) // Fixed width for alignment 98 | 99 | // Dropdown menu for icon style selection 100 | Menu { 101 | Button(action: { updateIcon(selectedOption: "Windows") }) { 102 | Text("Windows") 103 | } 104 | Button(action: { updateIcon(selectedOption: "macOS") }) { 105 | Text("macOS") 106 | } 107 | } label: { 108 | HStack { 109 | Text(selectedOption) 110 | .foregroundColor(.primary) 111 | Spacer() 112 | Image(systemName: "chevron.down") 113 | .foregroundColor(.gray) 114 | } 115 | .padding(.horizontal, 10) 116 | .padding(.vertical, 6) 117 | .background(Color(NSColor.controlBackgroundColor)) 118 | .cornerRadius(6) 119 | .overlay( 120 | RoundedRectangle(cornerRadius: 6) 121 | .stroke(Color.gray.opacity(0.3), lineWidth: 1) 122 | ) 123 | } 124 | .frame(width: 120) // Ensure consistent width for dropdown 125 | } 126 | .padding(.horizontal, 16) 127 | } 128 | } 129 | 130 | /// Updates the icon style and triggers a refresh of the status bar icon 131 | func updateIcon(selectedOption: String) { 132 | self.selectedOption = selectedOption 133 | AppDelegate.instance.updateStatusIcon() 134 | } 135 | } 136 | 137 | struct NetworkSettingsView: View { 138 | @AppStorage("showConnectionSpeed") var showConnectionSpeed: Bool = false 139 | @AppStorage("speedUnit") var speedUnit: String = "MB/s" 140 | @AppStorage("refreshInterval") var refreshInterval: Double = 1.0 141 | 142 | var body: some View { 143 | VStack(alignment: .leading, spacing: 16) { 144 | // Einheitliches Layout für Labels 145 | let labelWidth: CGFloat = 160 146 | 147 | // Show speed toggle 148 | HStack(alignment: .center) { 149 | Text("Show connection speed") 150 | .font(.system(size: 14)) 151 | .frame(width: labelWidth, alignment: .leading) 152 | 153 | Toggle("", isOn: $showConnectionSpeed) 154 | .toggleStyle(SwitchToggleStyle(tint: .red)) 155 | } 156 | 157 | // Speed unit dropdown 158 | HStack(alignment: .center) { 159 | Text("Speed unit") 160 | .font(.system(size: 14)) 161 | .frame(width: labelWidth, alignment: .leading) 162 | 163 | Menu { 164 | Button(action: { speedUnit = "KB/s" }) { 165 | Text("KB/s") 166 | } 167 | Button(action: { speedUnit = "MB/s" }) { 168 | Text("MB/s") 169 | } 170 | } label: { 171 | dropdownLabel(title: speedUnit) 172 | } 173 | .frame(width: 120) 174 | .disabled(!showConnectionSpeed) 175 | } 176 | 177 | // Refresh interval dropdown 178 | HStack(alignment: .center) { 179 | Text("Refresh interval") 180 | .font(.system(size: 14)) 181 | .frame(width: labelWidth, alignment: .leading) 182 | 183 | Menu { 184 | Button(action: { refreshInterval = 1.0 }) { 185 | Text("1 second") 186 | } 187 | Button(action: { refreshInterval = 3.0 }) { 188 | Text("3 seconds") 189 | } 190 | Button(action: { refreshInterval = 5.0 }) { 191 | Text("5 seconds") 192 | } 193 | Button(action: { refreshInterval = 10.0 }) { 194 | Text("10 seconds") 195 | } 196 | Button(action: { refreshInterval = 30.0 }) { 197 | Text("30 seconds") 198 | } 199 | Button(action: { refreshInterval = 60.0 }) { 200 | Text("60 seconds") 201 | } 202 | } label: { 203 | dropdownLabel(title: "\(Int(refreshInterval)) seconds") 204 | } 205 | .frame(width: 120) 206 | .disabled(!showConnectionSpeed) 207 | } 208 | } 209 | .padding(.horizontal, 16) 210 | } 211 | 212 | /// Dropdown-Label-Styling als Wiederverwendbare Funktion 213 | private func dropdownLabel(title: String) -> some View { 214 | HStack { 215 | Text(title) 216 | .foregroundColor(.primary) 217 | Spacer() 218 | Image(systemName: "chevron.down") 219 | .foregroundColor(.gray) 220 | } 221 | .padding(.horizontal, 10) 222 | .padding(.vertical, 6) 223 | .background(Color(NSColor.controlBackgroundColor)) 224 | .cornerRadius(6) 225 | .overlay( 226 | RoundedRectangle(cornerRadius: 6) 227 | .stroke(Color.gray.opacity(0.3), lineWidth: 1) 228 | ) 229 | } 230 | } 231 | 232 | 233 | /// About settings view content 234 | struct AboutSettingsView: View { 235 | var body: some View { 236 | VStack(alignment: .leading, spacing: 6) { 237 | Text("Easy Ethernet Icon") 238 | .font(.system(size: 16, weight: .bold)) 239 | 240 | Link("More information", destination: URL(string: "https://github.com/felixblome/easy-ethernet-icon")!) 241 | .font(.system(size: 14, weight: .medium)) 242 | .foregroundColor(.accentColor) 243 | .padding(.top, 5) 244 | 245 | Text("Made by Felix Blome | v1.2") 246 | .font(.system(size: 12, weight: .regular)) 247 | .foregroundColor(.secondary) 248 | .padding(.top, 4) 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /Easy Ethernet Icon/Easy Ethernet Icon.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 77; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DC50737D2CDB697A00B012ED /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = DC50737C2CDB697A00B012ED /* LaunchAtLogin */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXContainerItemProxy section */ 14 | DC9195212CDA9D49001BD4BB /* PBXContainerItemProxy */ = { 15 | isa = PBXContainerItemProxy; 16 | containerPortal = DC9195072CDA9D48001BD4BB /* Project object */; 17 | proxyType = 1; 18 | remoteGlobalIDString = DC91950E2CDA9D48001BD4BB; 19 | remoteInfo = "Ethernet Menu Icon"; 20 | }; 21 | DC91952B2CDA9D49001BD4BB /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = DC9195072CDA9D48001BD4BB /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = DC91950E2CDA9D48001BD4BB; 26 | remoteInfo = "Ethernet Menu Icon"; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | DC91950F2CDA9D48001BD4BB /* Easy Ethernet Icon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Easy Ethernet Icon.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | DC9195202CDA9D49001BD4BB /* Ethernet Menu IconTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Ethernet Menu IconTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | DC91952A2CDA9D49001BD4BB /* Ethernet Menu IconUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Ethernet Menu IconUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFileSystemSynchronizedRootGroup section */ 37 | DC9195112CDA9D48001BD4BB /* Easy Ethernet Icon */ = { 38 | isa = PBXFileSystemSynchronizedRootGroup; 39 | path = "Easy Ethernet Icon"; 40 | sourceTree = ""; 41 | }; 42 | /* End PBXFileSystemSynchronizedRootGroup section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | DC91950C2CDA9D48001BD4BB /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | DC50737D2CDB697A00B012ED /* LaunchAtLogin in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | DC91951D2CDA9D49001BD4BB /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | DC9195272CDA9D49001BD4BB /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | DC9195062CDA9D48001BD4BB = { 71 | isa = PBXGroup; 72 | children = ( 73 | DC9195112CDA9D48001BD4BB /* Easy Ethernet Icon */, 74 | DC9195102CDA9D48001BD4BB /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | DC9195102CDA9D48001BD4BB /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | DC91950F2CDA9D48001BD4BB /* Easy Ethernet Icon.app */, 82 | DC9195202CDA9D49001BD4BB /* Ethernet Menu IconTests.xctest */, 83 | DC91952A2CDA9D49001BD4BB /* Ethernet Menu IconUITests.xctest */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | DC91950E2CDA9D48001BD4BB /* Easy Ethernet Icon */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = DC9195342CDA9D49001BD4BB /* Build configuration list for PBXNativeTarget "Easy Ethernet Icon" */; 94 | buildPhases = ( 95 | DC91950B2CDA9D48001BD4BB /* Sources */, 96 | DC91950C2CDA9D48001BD4BB /* Frameworks */, 97 | DC91950D2CDA9D48001BD4BB /* Resources */, 98 | ); 99 | buildRules = ( 100 | ); 101 | dependencies = ( 102 | ); 103 | fileSystemSynchronizedGroups = ( 104 | DC9195112CDA9D48001BD4BB /* Easy Ethernet Icon */, 105 | ); 106 | name = "Easy Ethernet Icon"; 107 | packageProductDependencies = ( 108 | DC50737C2CDB697A00B012ED /* LaunchAtLogin */, 109 | ); 110 | productName = "Ethernet Menu Icon"; 111 | productReference = DC91950F2CDA9D48001BD4BB /* Easy Ethernet Icon.app */; 112 | productType = "com.apple.product-type.application"; 113 | }; 114 | DC91951F2CDA9D49001BD4BB /* Ethernet Menu IconTests */ = { 115 | isa = PBXNativeTarget; 116 | buildConfigurationList = DC9195372CDA9D49001BD4BB /* Build configuration list for PBXNativeTarget "Ethernet Menu IconTests" */; 117 | buildPhases = ( 118 | DC91951C2CDA9D49001BD4BB /* Sources */, 119 | DC91951D2CDA9D49001BD4BB /* Frameworks */, 120 | DC91951E2CDA9D49001BD4BB /* Resources */, 121 | ); 122 | buildRules = ( 123 | ); 124 | dependencies = ( 125 | DC9195222CDA9D49001BD4BB /* PBXTargetDependency */, 126 | ); 127 | name = "Ethernet Menu IconTests"; 128 | packageProductDependencies = ( 129 | ); 130 | productName = "Ethernet Menu IconTests"; 131 | productReference = DC9195202CDA9D49001BD4BB /* Ethernet Menu IconTests.xctest */; 132 | productType = "com.apple.product-type.bundle.unit-test"; 133 | }; 134 | DC9195292CDA9D49001BD4BB /* Ethernet Menu IconUITests */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = DC91953A2CDA9D49001BD4BB /* Build configuration list for PBXNativeTarget "Ethernet Menu IconUITests" */; 137 | buildPhases = ( 138 | DC9195262CDA9D49001BD4BB /* Sources */, 139 | DC9195272CDA9D49001BD4BB /* Frameworks */, 140 | DC9195282CDA9D49001BD4BB /* Resources */, 141 | ); 142 | buildRules = ( 143 | ); 144 | dependencies = ( 145 | DC91952C2CDA9D49001BD4BB /* PBXTargetDependency */, 146 | ); 147 | name = "Ethernet Menu IconUITests"; 148 | packageProductDependencies = ( 149 | ); 150 | productName = "Ethernet Menu IconUITests"; 151 | productReference = DC91952A2CDA9D49001BD4BB /* Ethernet Menu IconUITests.xctest */; 152 | productType = "com.apple.product-type.bundle.ui-testing"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | DC9195072CDA9D48001BD4BB /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | BuildIndependentTargetsInParallel = 1; 161 | LastSwiftUpdateCheck = 1610; 162 | LastUpgradeCheck = 1610; 163 | TargetAttributes = { 164 | DC91950E2CDA9D48001BD4BB = { 165 | CreatedOnToolsVersion = 16.1; 166 | }; 167 | DC91951F2CDA9D49001BD4BB = { 168 | CreatedOnToolsVersion = 16.1; 169 | TestTargetID = DC91950E2CDA9D48001BD4BB; 170 | }; 171 | DC9195292CDA9D49001BD4BB = { 172 | CreatedOnToolsVersion = 16.1; 173 | TestTargetID = DC91950E2CDA9D48001BD4BB; 174 | }; 175 | }; 176 | }; 177 | buildConfigurationList = DC91950A2CDA9D48001BD4BB /* Build configuration list for PBXProject "Easy Ethernet Icon" */; 178 | developmentRegion = en; 179 | hasScannedForEncodings = 0; 180 | knownRegions = ( 181 | en, 182 | Base, 183 | ); 184 | mainGroup = DC9195062CDA9D48001BD4BB; 185 | minimizedProjectReferenceProxies = 1; 186 | packageReferences = ( 187 | DC50737A2CDB681D00B012ED /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */, 188 | ); 189 | preferredProjectObjectVersion = 77; 190 | productRefGroup = DC9195102CDA9D48001BD4BB /* Products */; 191 | projectDirPath = ""; 192 | projectRoot = ""; 193 | targets = ( 194 | DC91950E2CDA9D48001BD4BB /* Easy Ethernet Icon */, 195 | DC91951F2CDA9D49001BD4BB /* Ethernet Menu IconTests */, 196 | DC9195292CDA9D49001BD4BB /* Ethernet Menu IconUITests */, 197 | ); 198 | }; 199 | /* End PBXProject section */ 200 | 201 | /* Begin PBXResourcesBuildPhase section */ 202 | DC91950D2CDA9D48001BD4BB /* Resources */ = { 203 | isa = PBXResourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | DC91951E2CDA9D49001BD4BB /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | DC9195282CDA9D49001BD4BB /* Resources */ = { 217 | isa = PBXResourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | /* End PBXResourcesBuildPhase section */ 224 | 225 | /* Begin PBXSourcesBuildPhase section */ 226 | DC91950B2CDA9D48001BD4BB /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | DC91951C2CDA9D49001BD4BB /* Sources */ = { 234 | isa = PBXSourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | DC9195262CDA9D49001BD4BB /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | }; 247 | /* End PBXSourcesBuildPhase section */ 248 | 249 | /* Begin PBXTargetDependency section */ 250 | DC9195222CDA9D49001BD4BB /* PBXTargetDependency */ = { 251 | isa = PBXTargetDependency; 252 | target = DC91950E2CDA9D48001BD4BB /* Easy Ethernet Icon */; 253 | targetProxy = DC9195212CDA9D49001BD4BB /* PBXContainerItemProxy */; 254 | }; 255 | DC91952C2CDA9D49001BD4BB /* PBXTargetDependency */ = { 256 | isa = PBXTargetDependency; 257 | target = DC91950E2CDA9D48001BD4BB /* Easy Ethernet Icon */; 258 | targetProxy = DC91952B2CDA9D49001BD4BB /* PBXContainerItemProxy */; 259 | }; 260 | /* End PBXTargetDependency section */ 261 | 262 | /* Begin XCBuildConfiguration section */ 263 | DC9195322CDA9D49001BD4BB /* Debug */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | ALWAYS_SEARCH_USER_PATHS = NO; 267 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 268 | CLANG_ANALYZER_NONNULL = YES; 269 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 271 | CLANG_ENABLE_MODULES = YES; 272 | CLANG_ENABLE_OBJC_ARC = YES; 273 | CLANG_ENABLE_OBJC_WEAK = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INFINITE_RECURSION = YES; 284 | CLANG_WARN_INT_CONVERSION = YES; 285 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 286 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 287 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 288 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 289 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 290 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 291 | CLANG_WARN_STRICT_PROTOTYPES = YES; 292 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 293 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 294 | CLANG_WARN_UNREACHABLE_CODE = YES; 295 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 296 | COPY_PHASE_STRIP = NO; 297 | DEBUG_INFORMATION_FORMAT = dwarf; 298 | ENABLE_STRICT_OBJC_MSGSEND = YES; 299 | ENABLE_TESTABILITY = YES; 300 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 301 | GCC_C_LANGUAGE_STANDARD = gnu17; 302 | GCC_DYNAMIC_NO_PIC = NO; 303 | GCC_NO_COMMON_BLOCKS = YES; 304 | GCC_OPTIMIZATION_LEVEL = 0; 305 | GCC_PREPROCESSOR_DEFINITIONS = ( 306 | "DEBUG=1", 307 | "$(inherited)", 308 | ); 309 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 310 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 311 | GCC_WARN_UNDECLARED_SELECTOR = YES; 312 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 313 | GCC_WARN_UNUSED_FUNCTION = YES; 314 | GCC_WARN_UNUSED_VARIABLE = YES; 315 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 316 | MACOSX_DEPLOYMENT_TARGET = 15.1; 317 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 318 | MTL_FAST_MATH = YES; 319 | ONLY_ACTIVE_ARCH = YES; 320 | SDKROOT = macosx; 321 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 322 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 323 | }; 324 | name = Debug; 325 | }; 326 | DC9195332CDA9D49001BD4BB /* Release */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | ALWAYS_SEARCH_USER_PATHS = NO; 330 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 331 | CLANG_ANALYZER_NONNULL = YES; 332 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 334 | CLANG_ENABLE_MODULES = YES; 335 | CLANG_ENABLE_OBJC_ARC = YES; 336 | CLANG_ENABLE_OBJC_WEAK = YES; 337 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 338 | CLANG_WARN_BOOL_CONVERSION = YES; 339 | CLANG_WARN_COMMA = YES; 340 | CLANG_WARN_CONSTANT_CONVERSION = YES; 341 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 342 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 343 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 344 | CLANG_WARN_EMPTY_BODY = YES; 345 | CLANG_WARN_ENUM_CONVERSION = YES; 346 | CLANG_WARN_INFINITE_RECURSION = YES; 347 | CLANG_WARN_INT_CONVERSION = YES; 348 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 349 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 350 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 351 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 352 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 353 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 354 | CLANG_WARN_STRICT_PROTOTYPES = YES; 355 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 356 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 357 | CLANG_WARN_UNREACHABLE_CODE = YES; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | COPY_PHASE_STRIP = NO; 360 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 361 | ENABLE_NS_ASSERTIONS = NO; 362 | ENABLE_STRICT_OBJC_MSGSEND = YES; 363 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 364 | GCC_C_LANGUAGE_STANDARD = gnu17; 365 | GCC_NO_COMMON_BLOCKS = YES; 366 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 367 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 368 | GCC_WARN_UNDECLARED_SELECTOR = YES; 369 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 370 | GCC_WARN_UNUSED_FUNCTION = YES; 371 | GCC_WARN_UNUSED_VARIABLE = YES; 372 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 373 | MACOSX_DEPLOYMENT_TARGET = 15.1; 374 | MTL_ENABLE_DEBUG_INFO = NO; 375 | MTL_FAST_MATH = YES; 376 | SDKROOT = macosx; 377 | SWIFT_COMPILATION_MODE = wholemodule; 378 | }; 379 | name = Release; 380 | }; 381 | DC9195352CDA9D49001BD4BB /* Debug */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 385 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 386 | CODE_SIGN_STYLE = Automatic; 387 | COMBINE_HIDPI_IMAGES = YES; 388 | DEVELOPMENT_ASSET_PATHS = "\"Easy Ethernet Icon/Preview Content\""; 389 | DEVELOPMENT_TEAM = GXN2F5G8XG; 390 | ENABLE_HARDENED_RUNTIME = YES; 391 | ENABLE_PREVIEWS = YES; 392 | GENERATE_INFOPLIST_FILE = YES; 393 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; 394 | INFOPLIST_KEY_LSUIElement = YES; 395 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 396 | LD_RUNPATH_SEARCH_PATHS = ( 397 | "$(inherited)", 398 | "@executable_path/../Frameworks", 399 | ); 400 | MACOSX_DEPLOYMENT_TARGET = 13.5; 401 | MARKETING_VERSION = 1.2; 402 | PRODUCT_BUNDLE_IDENTIFIER = "com.felixblome.easy-ethernet-icon"; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | SWIFT_EMIT_LOC_STRINGS = YES; 405 | SWIFT_VERSION = 5.0; 406 | }; 407 | name = Debug; 408 | }; 409 | DC9195362CDA9D49001BD4BB /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 414 | CODE_SIGN_STYLE = Automatic; 415 | COMBINE_HIDPI_IMAGES = YES; 416 | DEVELOPMENT_ASSET_PATHS = "\"Easy Ethernet Icon/Preview Content\""; 417 | DEVELOPMENT_TEAM = GXN2F5G8XG; 418 | ENABLE_HARDENED_RUNTIME = YES; 419 | ENABLE_PREVIEWS = YES; 420 | GENERATE_INFOPLIST_FILE = YES; 421 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; 422 | INFOPLIST_KEY_LSUIElement = YES; 423 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 424 | LD_RUNPATH_SEARCH_PATHS = ( 425 | "$(inherited)", 426 | "@executable_path/../Frameworks", 427 | ); 428 | MACOSX_DEPLOYMENT_TARGET = 13.5; 429 | MARKETING_VERSION = 1.2; 430 | PRODUCT_BUNDLE_IDENTIFIER = "com.felixblome.easy-ethernet-icon"; 431 | PRODUCT_NAME = "$(TARGET_NAME)"; 432 | SWIFT_EMIT_LOC_STRINGS = YES; 433 | SWIFT_VERSION = 5.0; 434 | }; 435 | name = Release; 436 | }; 437 | DC9195382CDA9D49001BD4BB /* Debug */ = { 438 | isa = XCBuildConfiguration; 439 | buildSettings = { 440 | BUNDLE_LOADER = "$(TEST_HOST)"; 441 | CODE_SIGN_STYLE = Automatic; 442 | CURRENT_PROJECT_VERSION = 1; 443 | DEVELOPMENT_TEAM = GXN2F5G8XG; 444 | GENERATE_INFOPLIST_FILE = YES; 445 | MACOSX_DEPLOYMENT_TARGET = 15.1; 446 | MARKETING_VERSION = 1.0; 447 | PRODUCT_BUNDLE_IDENTIFIER = "felixblome.Ethernet-Menu-IconTests"; 448 | PRODUCT_NAME = "$(TARGET_NAME)"; 449 | SWIFT_EMIT_LOC_STRINGS = NO; 450 | SWIFT_VERSION = 5.0; 451 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Ethernet Menu Icon.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Ethernet Menu Icon"; 452 | }; 453 | name = Debug; 454 | }; 455 | DC9195392CDA9D49001BD4BB /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | BUNDLE_LOADER = "$(TEST_HOST)"; 459 | CODE_SIGN_STYLE = Automatic; 460 | CURRENT_PROJECT_VERSION = 1; 461 | DEVELOPMENT_TEAM = GXN2F5G8XG; 462 | GENERATE_INFOPLIST_FILE = YES; 463 | MACOSX_DEPLOYMENT_TARGET = 15.1; 464 | MARKETING_VERSION = 1.0; 465 | PRODUCT_BUNDLE_IDENTIFIER = "felixblome.Ethernet-Menu-IconTests"; 466 | PRODUCT_NAME = "$(TARGET_NAME)"; 467 | SWIFT_EMIT_LOC_STRINGS = NO; 468 | SWIFT_VERSION = 5.0; 469 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Ethernet Menu Icon.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Ethernet Menu Icon"; 470 | }; 471 | name = Release; 472 | }; 473 | DC91953B2CDA9D49001BD4BB /* Debug */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | CODE_SIGN_STYLE = Automatic; 477 | CURRENT_PROJECT_VERSION = 1; 478 | DEVELOPMENT_TEAM = GXN2F5G8XG; 479 | GENERATE_INFOPLIST_FILE = YES; 480 | MARKETING_VERSION = 1.0; 481 | PRODUCT_BUNDLE_IDENTIFIER = "felixblome.Ethernet-Menu-IconUITests"; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | SWIFT_EMIT_LOC_STRINGS = NO; 484 | SWIFT_VERSION = 5.0; 485 | TEST_TARGET_NAME = "Ethernet Menu Icon"; 486 | }; 487 | name = Debug; 488 | }; 489 | DC91953C2CDA9D49001BD4BB /* Release */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | CODE_SIGN_STYLE = Automatic; 493 | CURRENT_PROJECT_VERSION = 1; 494 | DEVELOPMENT_TEAM = GXN2F5G8XG; 495 | GENERATE_INFOPLIST_FILE = YES; 496 | MARKETING_VERSION = 1.0; 497 | PRODUCT_BUNDLE_IDENTIFIER = "felixblome.Ethernet-Menu-IconUITests"; 498 | PRODUCT_NAME = "$(TARGET_NAME)"; 499 | SWIFT_EMIT_LOC_STRINGS = NO; 500 | SWIFT_VERSION = 5.0; 501 | TEST_TARGET_NAME = "Ethernet Menu Icon"; 502 | }; 503 | name = Release; 504 | }; 505 | /* End XCBuildConfiguration section */ 506 | 507 | /* Begin XCConfigurationList section */ 508 | DC91950A2CDA9D48001BD4BB /* Build configuration list for PBXProject "Easy Ethernet Icon" */ = { 509 | isa = XCConfigurationList; 510 | buildConfigurations = ( 511 | DC9195322CDA9D49001BD4BB /* Debug */, 512 | DC9195332CDA9D49001BD4BB /* Release */, 513 | ); 514 | defaultConfigurationIsVisible = 0; 515 | defaultConfigurationName = Release; 516 | }; 517 | DC9195342CDA9D49001BD4BB /* Build configuration list for PBXNativeTarget "Easy Ethernet Icon" */ = { 518 | isa = XCConfigurationList; 519 | buildConfigurations = ( 520 | DC9195352CDA9D49001BD4BB /* Debug */, 521 | DC9195362CDA9D49001BD4BB /* Release */, 522 | ); 523 | defaultConfigurationIsVisible = 0; 524 | defaultConfigurationName = Release; 525 | }; 526 | DC9195372CDA9D49001BD4BB /* Build configuration list for PBXNativeTarget "Ethernet Menu IconTests" */ = { 527 | isa = XCConfigurationList; 528 | buildConfigurations = ( 529 | DC9195382CDA9D49001BD4BB /* Debug */, 530 | DC9195392CDA9D49001BD4BB /* Release */, 531 | ); 532 | defaultConfigurationIsVisible = 0; 533 | defaultConfigurationName = Release; 534 | }; 535 | DC91953A2CDA9D49001BD4BB /* Build configuration list for PBXNativeTarget "Ethernet Menu IconUITests" */ = { 536 | isa = XCConfigurationList; 537 | buildConfigurations = ( 538 | DC91953B2CDA9D49001BD4BB /* Debug */, 539 | DC91953C2CDA9D49001BD4BB /* Release */, 540 | ); 541 | defaultConfigurationIsVisible = 0; 542 | defaultConfigurationName = Release; 543 | }; 544 | /* End XCConfigurationList section */ 545 | 546 | /* Begin XCRemoteSwiftPackageReference section */ 547 | DC50737A2CDB681D00B012ED /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */ = { 548 | isa = XCRemoteSwiftPackageReference; 549 | repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-Modern"; 550 | requirement = { 551 | branch = main; 552 | kind = branch; 553 | }; 554 | }; 555 | /* End XCRemoteSwiftPackageReference section */ 556 | 557 | /* Begin XCSwiftPackageProductDependency section */ 558 | DC50737C2CDB697A00B012ED /* LaunchAtLogin */ = { 559 | isa = XCSwiftPackageProductDependency; 560 | package = DC50737A2CDB681D00B012ED /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */; 561 | productName = LaunchAtLogin; 562 | }; 563 | /* End XCSwiftPackageProductDependency section */ 564 | }; 565 | rootObject = DC9195072CDA9D48001BD4BB /* Project object */; 566 | } 567 | --------------------------------------------------------------------------------