Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

Platform Setup

Every platform gates Wi-Fi behind something, and most of them fail quietly when it is missing — an empty scan, a placeholder SSID. This page is what to add, and what happens if you do not.

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="33" />
Permission Needed for
ACCESS_WIFI_STATE Reading radio state and scan results
CHANGE_WIFI_STATE Scanning, connecting, the local-only hotspot
ACCESS_FINE_LOCATION Scan results and the SSID of the joined network
NEARBY_WIFI_DEVICES Scanning on API 33+ without asking for location

ACCESS_FINE_LOCATION is not optional even on API 33+. NEARBY_WIFI_DEVICES unlocks scanning without location, but the SSID of the network you are on still requires location. RequestAccess() asks for both where they apply.

API Change
≤ 28 setWifiEnabled works; WifiConfiguration-based joins persist
29 Radio toggle revoked for apps. Joins move to WifiNetworkSpecifier — a system dialog, and the profile is never saved
29 startScan throttled to ~4 calls per 2 minutes in the foreground
30 ScanResultsCallback replaces the scan-results broadcast
31 WifiInfo comes off NetworkCapabilities.TransportInfo instead of WifiManager.ConnectionInfo
33 NEARBY_WIFI_DEVICES introduced

All of these are handled internally; the only one visible to you is the radio toggle, which drops out of Capabilities from API 29.

App ID capabilities (Apple Developer portal, then in your provisioning profile):

  • Hotspot Configuration — required for Connect / Disconnect
  • Access WiFi Information — required to read the current SSID

Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to identify the Wi-Fi network you are connected to.</string>

Since iOS 13, an app without granted location access reads the joined SSID back as a placeholder — "Wi-Fi" or "WLAN" — rather than the real name. Nothing fails. RequestAccess() asks for location for exactly this reason.

There is also no hotspot API on Apple’s platforms — see Hotspot.

NEHotspotConfigurationManager will list and remove the configurations your own app applied, which is what Known Networks maps onto. The networks the user saved themselves are never visible, and a stored configuration cannot be re-joined on demand.

Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to read Wi-Fi network names.</string>

macOS 14 (Sonoma) and later gate both scan results and the joined SSID on location authorization, the same way iOS does.

Sandboxed apps additionally need com.apple.security.network.client.

macOS is backed by CoreWLAN, so unlike iOS it can scan, associate, disassociate and power the interface. It has no hotspot API.

Reading the machine’s preferred-network list is unprivileged, but editing it means committing a whole CWConfiguration, which macOS gates behind an SFAuthorization a normal app cannot raise — so Forget() throws WifiPermissionException unless the process already holds the right. See Known Networks.

Package.appxmanifest:

<Capabilities>
<DeviceCapability Name="wiFiControl" />
<DeviceCapability Name="radios" />
</Capabilities>
Capability Needed for
wiFiControl Scanning and connecting through WiFiAdapter
radios Powering the Wi-Fi radio

Saved profiles are outside WinRT entirely — WiFiAdapter cannot list, delete or join one — so GetKnownNetworks(), Forget() and Connect(id) go through wlanapi.dll directly. That needs no extra manifest capability, but a packaged app running with a restricted token may still be refused WlanDeleteProfile by Windows itself.

WiFiAdapter.RequestAccessAsync() raises a consent prompt the first time; a packaged app missing wiFiControl gets DeniedBySystem back, which surfaces as WifiPermissionException rather than an empty scan.

Tethering additionally needs an active internet connection to share, and can be vetoed independently by group policy, the hardware or the SKU — that comes back as WifiNotSupportedException naming the TetheringCapability value.

Install Shiny.Net.Wifi.Linux rather than the base package. It registers NetworkManager-backed implementations of the same interfaces.

Requires a running NetworkManager. That is the default on Ubuntu, Fedora, Debian desktop and Raspberry Pi OS, but not on a systemd-networkd or netplan-only server. Without it, RequestAccess() returns AccessState.NotSupported.

Reading state and scanning are unprivileged. The mutating calls go through polkit:

Operation polkit action
Connect, disconnect, hotspot org.freedesktop.NetworkManager.network-control
Radio toggle org.freedesktop.NetworkManager.enable-disable-wifi
Forget a saved network org.freedesktop.NetworkManager.settings.modify.system

On a desktop session polkit prompts the user. In a headless session — a daemon, a kiosk, a Pi — add a rule granting those actions to the user the process runs as, or every mutating call fails with WifiPermissionException carrying polkit’s refusal.

Hotspot mode additionally needs an adapter whose driver supports AP mode. Most do; a few cheap USB dongles do not, and those fail at activation rather than up front.

The base package’s net10.0 target is a deliberate stub, not a fallback. CurrentNetwork still reports the IP, DNS, gateway and mask of the wireless interface and Changed still fires off NetworkChange, because that all comes from the managed network stack. Every Wi-Fi-specific call — scan, connect, radio, hotspot — throws WifiNotSupportedException pointing at Shiny.Net.Wifi.Linux where relevant.

Capabilities reports None and RequestAccess() returns AccessState.Restricted, which is exactly what it describes: granted in a limited fashion.