A tiny macOS menu bar app that does two things:
- Internet latency — pings
google.comevery 5 seconds and shows the round-trip time in the status bar (the headline number). - LAN device scanner — discovers active devices on your Wi-Fi/Ethernet network and lists them in the menu, with as much identifying info as it can gather per device.
No Dock icon, no window — just a menu bar item. No dependencies beyond the Xcode Command Line Tools.
Open the menu bar item to see the device list. Each device has a submenu with:
- IP address
- MAC address (flagged
(randomized)when it's a private/locally-administered address — phones rotate these) - Vendor — looked up from the MAC's OUI prefix (curated offline table: Apple, Ubiquiti, Raspberry Pi, Samsung, Google, Amazon, Espressif, …)
- Hostname — reverse DNS (PTR)
- Bonjour name — the device's advertised mDNS name (e.g. "living-room-speaker", "office-printer")
- Services — what it advertises over Bonjour (AirPlay, HomeKit, Chromecast, Spotify, Printer, SSH, SMB, …)
- Ping — round-trip time to that device
- Role —
default gateway(your router) orthis Mac
The header also shows the chosen interface + IP and a best-effort Wi-Fi line (signal/channel/rate; SSID requires Location permission).
swiftc main.swift -o MacPing.app/Contents/MacOS/MacPing -framework CoreWLANRequires the Xcode Command Line Tools (xcode-select --install). The -framework CoreWLAN flag links the Wi-Fi info API.
open MacPing.appLaunch via open (not by invoking the binary directly). open hands the process to launchd, which spawns it outside any parent shell sandbox — ICMP sockets need this.
First launch: macOS will prompt to allow MacPing to access devices on your local network. Approve it, or the scanner can't see anything (this is required for both the ping-sweep and Bonjour discovery on modern macOS).
The same binary can print a scan to stdout and exit — handy for scripting:
# Human-readable report
./MacPing.app/Contents/MacOS/MacPing --scan-once
# JSON (pipe to jq, etc.)
./MacPing.app/Contents/MacOS/MacPing --json | jq '.devices[] | {ip, vendor, bonjourName}'| What | When |
|---|---|
| Internet ping (the menu bar number) | every 5 s |
| LAN device scan | only when you open the menu (click the menu bar item) |
| LAN device scan (manual) | also via Scan now / ⌘R while the menu is open |
| Bonjour/mDNS discovery | continuous in the background (passive listening, no ping traffic) |
The LAN scan is click-driven — there's no background timer, so it never ping-sweeps on its own. Each time you open the menu it kicks a fresh scan (ping-sweeps the whole subnet, up to a /22 ≈ 1022 hosts, ~10–15 s); the menu shows the previous results plus "Scanning…" and updates when it finishes. The ping interval is the withTimeInterval: 5 constant in AppDelegate.
- Open the menu bar item for the device list; each device has a submenu with Copy this device.
- Copy report copies the whole scan as text; Scan now (
⌘R) forces a rescan. - Quit from the menu, or:
pkill -x MacPing - Rebuild + relaunch:
swiftc main.swift -o MacPing.app/Contents/MacOS/MacPing -framework CoreWLAN \ && pkill -x MacPing; open MacPing.app
mac-ping/
├── main.swift # source (ping + scanner + menu + CLI)
├── MacPing.app/
│ └── Contents/
│ ├── Info.plist # LSUIElement, NSLocalNetworkUsageDescription, NSBonjourServices
│ └── MacOS/MacPing # compiled binary
└── README.md
NSApp.setActivationPolicy(.accessory)plusLSUIElementinInfo.plistmakes it a menu-bar-only app.- Internet ping: every 5 s,
Processruns/sbin/ping -c 1 -W 2000 google.com; the RTT is regex-extracted and shown as the title. - LAN scan: picks the LAN interface via
getifaddrs(an up, broadcast-capable, non-loopback, non-point-to-pointen*with a private IP — this skips VPNutunNtunnels), then:- Ping-sweeps the subnet with bounded concurrency. Discovery relies on ARP, not ping success: sending the packet makes the kernel resolve the host's MAC even if it ignores ICMP echo.
- Reads the ARP table (
arp -a -n) for IP→MAC, restricted to the chosen interface and filtered to in-subnet unicast hosts (drops multicast/broadcast). - Reverse-DNS each host via
getnameinfo. - Looks up the vendor from the MAC OUI.
- Merges names/services from a continuous Bonjour/mDNS browse (
NetServiceBrowser). - Marks the gateway (per-interface router, so it's correct even behind a VPN) and this Mac.
- On internet-ping error (DNS, timeout, unreachable), the title shows
—.
System Settings → General → Login Items → add MacPing.app.