Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WIFI_MAC hero banner

WIFI_MAC

A native macOS Wi-Fi analyzer for nearby network inspection, channel ranking, ASUS Merlin heuristics, and exportable reports — shipped as a scriptable CLI and a lightweight SwiftUI app.

Swift Platform SwiftPM License Tests Backends


Table of contents

Why

Dedicated RF hardware and enterprise spectrum tools are overkill when you just want to pick a cleaner 5 GHz channel or sanity-check an ASUS Merlin deployment. WIFI_MAC stays honest about what macOS actually exposes: it ranks channels, surfaces Merlin hints, and tells you — in plain language — when macOS redacted the data instead of inventing it.

Highlights

Area What you get
Dual backends Prefers Apple's private airport, falls back to CoreWLAN, fails closed if neither works
Channel ranking Scored recommendations with confidence grades and stability scoring
Merlin heuristics Detects ASUS Merlin routers, country-mode mismatches, and AiMesh / guest / IoT topology hints
Degraded-mode honesty Missing SSID/BSSID/noise/SNR stay nil; warnings explain why instead of guessing
Export Deterministic JSON and text reports with full provenance, warnings, and snapshot data
Two fronts Scriptable WiFiCLI for terminals and automation; WiFiGUIApp SwiftUI host for live use

Architecture

graph TD
    Domain["WiFiDomain<br/>contracts · provenance · warnings"]

    Capture["WiFiCapture<br/>airport · CoreWLAN · capabilities"]
    Analysis["WiFiAnalysis<br/>ranking · Merlin · China policy"]
    Reports["WiFiReports<br/>JSON · text renderer"]

    CLI["WiFiCLI<br/>executable"]
    GUI["WiFiGUI<br/>SwiftUI root view"]
    GUIApp["WiFiGUIApp<br/>macOS app host"]

    Domain --> Capture
    Domain --> Analysis
    Domain --> Reports
    Analysis --> Reports

    Capture --> CLI
    Reports --> CLI
    Analysis --> CLI

    Reports --> GUI
    Analysis --> GUI
    Capture --> GUIApp
    GUI --> GUIApp
Loading

Each layer depends only on WiFiDomain (and WiFiAnalysis for reporting), so capture churn never leaks into analysis or UI. The boundary is enforced by ArchitectureBoundariesTests.

Installation

Prerequisites

Requirement Minimum version How to check
macOS 13 (Ventura) sw_vers
Swift toolchain 6.0 swift --version
Xcode command-line tools Xcode 16 or Swift 6 toolchain xcode-select -p
Wi-Fi hardware Built-in Apple Silicon or Intel Mac Wi-Fi System Information > Network > Wi-Fi

Step 1 — Clone

git clone https://github.com/leedale30/WIFI_MAC.git
cd WIFI_MAC

Step 2 — Build

swift build

This compiles all seven modules and produces two executables:

  • .build/<arch>-apple-macosx/debug/WiFiCLI — the command-line tool
  • .build/<arch>-apple-macosx/debug/WiFiGUIApp — the SwiftUI app

Sandbox note: If swift build fails with sandbox-exec: Operation not permitted, add --disable-sandbox:

swift build --disable-sandbox

Step 3 — Verify the build

swift test                 # 53 tests across 8 suites
swift run WiFiCLI capabilities

You should see capability output listing airport, CoreWLAN, and system_profiler backends.

Step 4 — (Optional) Build a release binary

swift build -c release

Release binaries land in .build/<arch>-apple-macosx/release/.

Step 5 — (Optional) Create a launchable .app bundle

The WiFiGUIApp binary runs fine from the terminal, but macOS will not foreground its window reliably without a bundle wrapper. To create one:

APP_DIR="$HOME/Applications/WiFiAnalyzer.app/Contents"
mkdir -p "$APP_DIR/MacOS"
cp .build/release/WiFiGUIApp "$APP_DIR/MacOS/WiFiGUIApp"

cat > "$APP_DIR/Info.plist" << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" \
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleName</key>           <string>WiFi Analyzer</string>
  <key>CFBundleDisplayName</key>    <string>WiFi Analyzer</string>
  <key>CFBundleIdentifier</key>     <string>com.leedale30.wifianalyzer</string>
  <key>CFBundleVersion</key>        <string>1.0</string>
  <key>CFBundleExecutable</key>     <string>WiFiGUIApp</string>
  <key>CFBundlePackageType</key>    <string>APPL</string>
  <key>NSHighResolutionCapable</key> <true/>
</dict>
</plist>
PLIST

open "$HOME/Applications/WiFiAnalyzer.app"

Step 6 — (Optional) Add the CLI to your PATH

sudo cp .build/release/WiFiCLI /usr/local/bin/wifi-analyzer
wifi-analyzer scan

Usage

CLI

The CLI is the primary interface for scripting and automation. All commands exit 0 on success and non-zero on failure.

Commands

Command Purpose
scan Run a Wi-Fi scan and print a human-readable report
scan --json Same scan, machine-readable JSON
capabilities Show detected backend capability state
diagnostics Capability state + scope limits in one place (best for support handoff)
export Write a report file to disk
analyze Alias of scan (same report path)
channel-analysis Alias of scan (same report path)

Flags

Flag Applies to Effect
--json scan, diagnostics, export Emit JSON instead of text
--interface <name> scan, diagnostics Force CoreWLAN to target a specific interface (e.g. en0)
--same-lan scan Enable same-LAN fingerprint probing (extension hook — stock builds ship no active probe)
--format <json|text> export Output format for the exported file
--output <path> export Destination file path

Examples

Basic scan (text output):

swift run WiFiCLI scan

Output (abbreviated):

Wi-Fi Analyzer Report
Captured: 2026-08-11T10:02:15Z
Interface: en1
Observed networks: 8
Source backends: coreWLAN
Strongest RSSI: -59 dBm
Stability: 35%
Confidence: 45% (low)

Top Findings:
- Prefer 5GHz channel 161 (80MHz) [score: 71.4, confidence: 45%]
- 4 warning(s) require review before making a final channel change.

Warnings:
- [WARNING] Some CoreWLAN fields were missing or redacted.
- [WARNING] airport integration is unavailable on this host.
- [WARNING] Overlapping 2.4 GHz channels are increasing adjacent-channel contention.

Ranked Recommendations:
1. Prefer 5GHz channel 161 (80MHz) | band: 5GHz | channel: 161 | width: 80MHz | score: 71.4 | confidence: 45%

Observed Networks:
- corewlan-0 | band: 2.4GHz | channel: 6 | RSSI: -61 dBm | SNR: 29 dB | source: coreWLAN
- corewlan-1 | band: 5GHz | channel: 149 | RSSI: -64 dBm | SNR: 26 dB | source: coreWLAN
...

JSON scan (for piping into jq or other tools):

swift run WiFiCLI scan --json | jq '.summary'
{
  "capturedAt" : "2026-08-11T10:02:15Z",
  "interfaceName" : "en1",
  "networkCount" : 8,
  "strongestRSSI" : -59,
  "stabilityScore" : 0.35,
  "confidence" : 0.45,
  "confidenceLevel" : "low",
  "topRecommendation" : {
    "title" : "Prefer 5GHz channel 161 (80MHz)",
    "band" : "5GHz",
    "channel" : 161,
    "channelWidth" : "80MHz",
    "score" : 71.4,
    "confidence" : 0.45
  }
}

Target a specific interface:

swift run WiFiCLI scan --interface en0

Check backend capabilities:

swift run WiFiCLI capabilities
Recommended backend: airport

Backend capabilities:
- airport: available [preferred]
  path: /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport
- CoreWLAN: available
- system_profiler: available
  path: /usr/sbin/system_profiler

Run diagnostics (best for support handoff):

swift run WiFiCLI diagnostics

Diagnostics combines capability state, scan status, and scope limits in a single report. Use this when filing issues or asking for help.

GUI

Launch the SwiftUI app:

swift run WiFiGUIApp

Or if you created the .app bundle:

open ~/Applications/WiFiAnalyzer.app

GUI layout

Panel Content
Left sidebar Navigation: Dashboard, Networks, Channel Analysis, History & Comparison
Center area Summary cards (network count, strongest RSSI, confidence, stability, region, top channel), top findings, warnings, scope limits
Right inspector Selected network details: band, channel, width, security, RSSI, noise, SNR, overlap pressure, composite score, missing fields
Toolbar Run Scan, Export JSON, Export Text, Hide/Show Inspector, Disassociate

Typical GUI session

  1. Launch the app — the dashboard shows an empty state explaining why no data is present yet.
  2. Click Run Scan (or Run First Scan on first launch).
  3. Summary cards populate with live network data.
  4. Click any network in the list to inspect it in the right panel.
  5. Use Export JSON or Export Text to save a report to disk.
  6. Switch to Channel Analysis in the sidebar for the ranked recommendation view.

Exports

Both CLI and GUI produce the same deterministic report format. Reports include:

  • Snapshot — raw scan data (interface, observations, capabilities, warnings)
  • Analysis — ranked recommendations, stability score, confidence, region resolution
  • Summary — captured-at, network count, strongest RSSI, top recommendation
  • Provenance — recommended backend, observed backends, per-source observation counts
  • Warnings — severity, category, summary, detail
  • Region notes — country-code evidence, mismatch signals
  • Scope limitations — what macOS did not expose and why
  • Merlin evidence — confirmed detections and topology hints

Export to a file (CLI):

swift run WiFiCLI export --format json --output ./wifi-report.json
swift run WiFiCLI export --format text --output ./wifi-report.txt

Export to a file (GUI):

Click Export JSON or Export Text in the toolbar and choose a save location.

Common workflows

Pick a cleaner 5 GHz channel:

swift run WiFiCLI scan
# Read the "Ranked Recommendations" section
# Cross-check with the "Observed Networks" list for overlap

Audit an ASUS Merlin deployment:

swift run WiFiCLI scan --json | jq '.merlinEvidence'

Automate periodic scans (cron):

# Save a JSON report every hour
0 * * * * cd /path/to/WIFI_MAC && swift run WiFiCLI scan --json > /tmp/wifi-$(date +\%Y\%m\%d-\%H\%M).json

Pipe into jq for quick channel overview:

swift run WiFiCLI scan --json | jq '.analysis.recommendations[] | {channel, band, score, confidence}'

Backend selection

flowchart TD
    Start([Scan requested]) --> A{airport runnable?}
    A -- yes --> B[Try airport]
    B -- ok --> D[Emit report]
    B -- fail --> C{CoreWLAN available?}
    A -- no --> C
    C -- yes --> E[Use CoreWLAN<br/>+ degraded-mode warning]
    C -- no --> F[Fail closed<br/>no fabricated result]
    E --> D
Loading

WIFI_MAC prefers the private airport backend because it exposes more fields (BSSID, noise, country code, channel width). When airport is unavailable or fails, it falls back to CoreWLAN and attaches a degraded-mode warning. If neither backend works, the scan fails closed — no fabricated results.

Permissions & degraded mode

macOS may redact or omit Wi-Fi metadata even when a scan succeeds. WIFI_MAC never fills gaps with guesses.

  • Missing SSID, BSSID, noise, SNR, country code, or channel width remain nil / empty.
  • Reports and GUI states surface warnings instead of inventing measurements.
  • The GUI first-launch state explains why the dashboard is empty before the first scan.

Typical causes of reduced fidelity: ungranted location/Wi-Fi permissions, a present-but-blocked airport, CoreWLAN exposing fewer fields than airport, or hardware/OS limits.

Project structure

.
├── Examples/Reports/      Example JSON and text outputs for verification
├── Sources/
│   ├── WiFiDomain/         Shared contracts, provenance, warnings, export models
│   ├── WiFiCapture/        Capability detection + airport/CoreWLAN acquisition
│   ├── WiFiAnalysis/       Metrics, channel ranking, Merlin heuristics, China policy
│   ├── WiFiReports/        Text and JSON report rendering
│   ├── WiFiCLI/            Executable CLI entry point
│   ├── WiFiGUI/            Reusable SwiftUI root view and GUI state helpers
│   └── WiFiGUIApp/         Minimal standalone macOS SwiftUI app host
├── Tests/                  Unit, fixture, CLI, report, and GUI state coverage
└── Package.swift           SwiftPM manifest

Verification

swift test                 # 53 tests across 8 suites
swift build
swift build --product WiFiGUIApp
swift run WiFiCLI diagnostics

Troubleshooting

Symptom Cause Fix
sandbox-exec: Operation not permitted SwiftPM sandbox blocks file system access Run with --disable-sandbox
airport backend reported unavailable macOS removed or restricted the private framework Expected on some macOS versions; CoreWLAN fallback covers basic scans
GUI window does not appear Bare SwiftPM binary is not foregrounded by macOS Create an .app bundle wrapper (see Installation Step 5)
Strongest RSSI: n/a in report macOS redacted Wi-Fi metadata (permissions or OS limits) Grant Wi-Fi/location permissions if prompted; otherwise the nil is intentional
--interface en0 has no effect The airport backend cannot target an interface Only CoreWLAN honors --interface; if airport is active, the flag is ignored
swift run WiFiGUIApp exits immediately Missing entitlements or sandbox restrictions on the terminal Run from a properly sandboxed terminal or use the .app bundle

Roadmap & non-goals

Intentionally narrower than a dedicated spectrum analyzer or enterprise RF suite.

Not in scope:

  • Legality determination for China or any other regulatory domain
  • Dedicated spectrum, waterfall, or packet-capture tooling
  • Active throughput, roaming, retry-rate, or airtime testing
  • Stock same-LAN fingerprint probe in the default build
  • Ranked 6 GHz planning (6 GHz observations are surfaced when macOS exposes them)
  • Claiming parity with WiFi Explorer, NetSpot, or hardware-backed analyzers field-for-field

Ready for extension:

  • Channel widths beyond 80 MHz (160 MHz, 320 MHz)
  • Multi-Link Operation (MLO) domain models for Wi-Fi 7
  • BE-series Merlin naming, firmware tokens, and new ASUS topology conventions
  • PSC / wider-channel / regulatory-context 6 GHz planning once capture paths expose them reliably

License

MIT — © 2026 leedale30

About

Native macOS Wi-Fi analyzer (Swift) — CLI + SwiftUI GUI, channel ranking, ASUS Merlin heuristics, exportable reports

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages