├── .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 | 
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 |
--------------------------------------------------------------------------------