Changelog¶
Versions follow SemVer. Each release is published
to Maven Central and tagged on GitHub. Pin to a tag, never to
branch: "main".
Unreleased¶
Public API¶
Reachability.shared— process-lifetime singleton on theReachabilityinterface'scompanion object. Callable from anywhere at any time, with no Context plumbing required. Replaces the pattern of constructingReachability(context)inApplication.onCreateand threading it through your DI graph.close()onReachability.sharedis an intentional no-op — the singleton lives for the process; the OS reaps the platform observer at process exit.- From Swift:
Reachability.sharedvia an in-framework Swift extension (src/appleMain/swift/Reachability+Shared.swift); the rawReachabilityCompanioncall is hidden.
interface Reachability : AutoCloseableexposingval status: StateFlow<ReachabilityStatus>andfun close().- Single-axis shortcuts on
Reachability:val isReachable: Boolean— synchronous online check.val isDataMetered: Boolean— synchronous metered-path check (cellular, hotspot, or Apple Low Data Mode).val reachable: StateFlow<Boolean>— reactive variant ofisReachable, conflated so transport and metering changes don't emit.val dataMetered: StateFlow<Boolean>— reactive variant ofisDataMetered.
data class ReachabilityStatus(isReachable, transport, isDataMetered). Boolean fields areis-prefixed; the unprefixed names are reserved for theStateFlow<Boolean>shortcuts onReachability. Invariant:!isReachable ⇒ !isDataMetered— unreachable paths never report metered, so callers can readisDataMeteredwithout first checking reachability.enum class Transport { Wifi, Cellular, Ethernet, Other, None }.- No separate
Meteringaxis. Apple'snw_path_is_constrained(Low Data Mode) folds intoisDataMeteredalongsidenw_path_is_expensive; Android's metered/unmetered capability bits map to the same boolean. Consumers who want the cellular-vs-Wi-Fi distinction readtransport; consumers who want "should I defer this transfer" readisDataMetered. The Apple-only Low Data Mode signal is not separately exposed in the public API. - Top-level factories:
Reachability()on Apple,Reachability(context)on Android. Still available for explicit-lifecycle use (tests, per-feature observers). For general use, preferReachability.shared.
Implementation¶
androidx.startupintegration (Android):ReachabilityInitializer(anandroidx.startup.Initializer) is registered in the library'sAndroidManifest.xmlunderInitializationProviderwithtools:node="merge". It attachesReachability.sharedto the applicationContextduring the ContentProvider startup pass — beforeApplication.onCreate. Consumers get auto-init for free; no manifest changes required.androidx.startup:startup-runtime:1.2.0is a newimplementationdependency of the library.- Two-phase Android construction:
AndroidReachabilitynow has a zero-arg primary constructor (no OS calls, no Context) and aninternal fun attach(context)that is idempotent (first-call-wins via CAS). The existingReachability(context)factory still callsattach()immediately. The singleton path defersattach()to theReachabilityInitializer. - Apple:
nw_path_monitorviaplatform.Networkcinterop, on a per-instance serial dispatch queue. - Android:
ConnectivityManager.NetworkCallbackagainstNET_CAPABILITY_INTERNET + NET_CAPABILITY_VALIDATED.status.valueis seeded synchronously fromconnectivityManager.activeNetworkso it's meaningful immediately after construction. - Shared base class owns a
SupervisorJob-rooted scope and an atomic close latch.close()is idempotent.
Distribution¶
- Published to Maven Central as
com.happycodelucky.reachable:reachablevia vanniktech maven-publish. Includes the Android AAR plus thekotlinMultiplatformmetadata and theiosArm64,iosSimulatorArm64,macosArm64klibs, all signed. - Companion artifact
com.happycodelucky.reachable:reachable-testingpublished alongside the main artifact. ProvidesFakeReachabilityandwithFakeReachability { }for installing a scriptable fake asReachability.sharedfor the duration of a test. Add astestImplementation/commonTest— see Installation → Testing support. - A native Swift Package Manager artifact is not published in v0.1; see Installation.
Known limitations¶
- Wired Ethernet on macOS surfaces as
Transport.Other. Kotlin/Native'splatform.Networkcinterop doesn't currently exposenw_interface_type_wired_ethernet. See Concepts → Validated vs available. - VPN-over-Wi-Fi resolves to
Transport.Wifi(the underlying physical transport). No separateVPNvalue. - No captive-portal detection callback. Apple and Android both handle
captive-portal flow at the OS level; the library surfaces the resolved
isReachableboolean only. - No
androidHostTestforAndroidReachability's deferred-attach path. Blocked on adding Robolectric or aConnectivityManagerwrapper interface. End-to-end coverage currently provided by the Android sample app.