Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/concepts/api-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ metered = many cases) or stuff axes inside one case's payload, defeating
exhaustiveness.

Enum-per-axis still gives Swift consumers exhaustive `switch` over transport
individually via SKIE: `switch status.transport { case .wifi: …; case
individually: `switch status.transport { case .wifi: …; case
.cellular: …; … }`. `data class` brings `equals`, `hashCode`, `copy()`, and
destructuring for free.

Expand Down Expand Up @@ -121,7 +121,7 @@ val reachability: Reachability = Reachability.shared
```

From Swift: `Reachability.shared` (via a Swift extension compiled into the
framework by SKIE; no companion prefix needed).
framework; no companion prefix needed).

**Why we use a singleton, not a mandatory factory.** On Android, the
`Context` requirement means that anything consuming reachability via
Expand Down Expand Up @@ -192,7 +192,7 @@ own a platform observer (`nw_path_monitor` or `NetworkCallback`) and a
path on both Apple and Android, so cleanup is explicit.

`AutoCloseable.close()` is the universal idiom across Kotlin, Java, and
Swift; SKIE renders it as `close()` without any name mangling. The
Swift; it surfaces in Swift as `close()` without any name mangling. The
implementation is idempotent and synchronous.

`Reachability.shared` is an exception: `close()` is an intentional no-op
Expand Down
8 changes: 3 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,9 @@ Apple's `nw_path_is_expensive` and `nw_path_is_constrained` (cellular,
hotspot, and Low Data Mode) both fold into `isDataMetered`. Android uses
`NET_CAPABILITY_NOT_METERED` for the same signal.

The Swift surface is generated by [SKIE]: enums become exhaustive Swift
enums, `StateFlow<T>` becomes `AsyncSequence<T>`, `suspend fun` becomes
`async throws`.

[SKIE]: https://skie.touchlab.co/
The Swift surface is idiomatic: Kotlin enums arrive as exhaustive Swift
enums, `StateFlow<T>` is consumed as an `AsyncSequence<T>`, and
`suspend fun` becomes `async throws`.

## Install

Expand Down
18 changes: 9 additions & 9 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ Reachable ships through two channels:
| Channel | For | Artifacts |
|---|---|---|
| **Maven Central** | Gradle — Android, JVM, Kotlin Multiplatform | Android AAR, `kotlinMultiplatform` metadata, per-target klibs (`iosArm64`, `iosSimulatorArm64`, `macosArm64`) |
| **Swift Package Manager** | Pure-Swift iOS / macOS apps, no Kotlin toolchain | Prebuilt [SKIE](https://skie.touchlab.co/)-enhanced `Reachable.xcframework`, hosted as a GitHub Release asset |
| **Swift Package Manager** | Pure-Swift iOS / macOS apps, no Kotlin toolchain | Prebuilt `Reachable.xcframework`, hosted as a GitHub Release asset |

Kotlin Multiplatform projects should use the Maven artifact from
`commonMain` — KMP resolves the right per-target slice automatically, and
SKIE bridging happens at *your* project's framework build. The Swift
package is for apps with no Kotlin in them at all — see
the Swift surface is produced at *your* project's framework build. The
Swift package is for apps with no Kotlin in them at all — see
[Swift Package Manager](#swift-package-manager) below.

## Platform floors
Expand All @@ -20,7 +20,7 @@ package is for apps with no Kotlin in them at all — see
| iOS / iPadOS | iOS 18 |
| macOS | macOS 15 |
| Android | API 30 (Android 11), `arm64-v8a` only |
| Kotlin | 2.3.x (K2). The upper bound tracks SKIE — see [SKIE releases](https://github.com/touchlab/SKIE/releases). |
| Kotlin | 2.3.x (K2) |

## Gradle (Android, JVM, KMP)

Expand Down Expand Up @@ -113,8 +113,8 @@ when the block throws.

## Swift Package Manager

Pure-Swift apps consume Reachable as a binary Swift package: a prebuilt,
SKIE-enhanced `Reachable.xcframework` with `iosArm64`, `iosSimulatorArm64`,
Pure-Swift apps consume Reachable as a binary Swift package: a prebuilt
`Reachable.xcframework` with `iosArm64`, `iosSimulatorArm64`,
and `macosArm64` slices. No Kotlin toolchain, no Gradle, no authentication —
the package manifest lives at the root of this repository and the binary is
a public GitHub Release asset, pinned by sha256 checksum in the manifest.
Expand Down Expand Up @@ -142,7 +142,7 @@ a public GitHub Release asset, pinned by sha256 checksum in the manifest.
]
```

Then `import Reachable`. The SKIE bridge is baked into the framework, so
Then `import Reachable`. The Swift bridge is baked into the framework, so
`StateFlow` arrives as a Swift `AsyncSequence`, sealed types `switch`
exhaustively via `onEnum(of:)`, and `suspend` functions are `async throws`.

Expand All @@ -153,8 +153,8 @@ resolve` downloads a prebuilt framework instead of compiling Kotlin.
If you're working from a KMP project, don't add the Swift package — the
iOS / macOS targets are consumed transparently via the
`kotlinMultiplatform` metadata published alongside the Android AAR, and
SKIE bridging happens at your project's framework build time, not the
library's.
the Swift surface is produced at your project's framework build time, not
the library's.

## Local development override

Expand Down
14 changes: 6 additions & 8 deletions docs/platforms/ios.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ let reachability: any Reachability = Reachability.shared
it constructs an `nw_path_monitor`-backed observer and starts it eagerly.
Subsequent accesses return the same instance.

The Swift `Reachability.shared` property is provided by an in-framework
Swift extension (`src/appleMain/swift/Reachability+Shared.swift`), which
SKIE auto-discovers and compiles into the `Reachable` module. Consumers
don't need any additional configuration — `Reachability.shared` just
works.
The Swift `Reachability.shared` property is provided by a Swift
extension compiled into the `Reachable` module. Consumers don't need any
additional configuration — `Reachability.shared` just works.

Calling `close()` on `Reachability.shared` is an intentional no-op — the
singleton's lifetime is the process.
Expand All @@ -38,9 +36,9 @@ import Reachable
let reachability: any Reachability = Reachability()
```

`Reachability()` is a top-level Swift function that SKIE generates from the
top-level Kotlin factory `fun Reachability(): Reachability` in
`appleMain/Reachability.apple.kt`. Construction:
`Reachability()` is a top-level Swift function bridged from the
top-level Kotlin factory `fun Reachability(): Reachability`.
Construction:

1. Creates a per-instance serial dispatch queue
(`dispatch_queue_create("dev.reachable.monitor", null)`).
Expand Down
4 changes: 2 additions & 2 deletions docs/platforms/macos.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ let reachability: any Reachability = Reachability.shared
```

`Reachability.shared` is a process-lifetime singleton provided by the
same Swift extension as on iOS (`src/appleMain/swift/Reachability+Shared.swift`,
compiled into the `Reachable` module by SKIE). On first access, constructs
same Swift extension as on iOS, compiled into the `Reachable` module.
On first access, constructs
an `nw_path_monitor`-backed observer and starts it eagerly. Calling
`close()` on this instance is a no-op.

Expand Down
10 changes: 5 additions & 5 deletions docs/recipes/swiftui-binding.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SwiftUI binding

SKIE bridges `StateFlow<T>` as `AsyncSequence<T>`, so the integration is a
single `for await` loop. Wrap that in an `@MainActor`
`StateFlow<T>` is bridged to Swift as an `AsyncSequence<T>`, so the
integration is a single `for await` loop. Wrap that in an `@MainActor`
`ObservableObject` view-model and bind from a SwiftUI view.

## With `Reachability.shared` (recommended)
Expand Down Expand Up @@ -143,9 +143,9 @@ if now.isReachable {
}
```

`StateFlow.value` is non-optional in Kotlin, but SKIE renders it as
Swift `Any?`. The `!` unwraps the bridged value to `ReachabilityStatus`.
SKIE may relax this in a future version.
`StateFlow.value` is non-optional in Kotlin, but the bridged Swift
property is typed `Any?`. The `!` unwraps the bridged value to
`ReachabilityStatus`. This may tighten in a future release.

For a one-shot suspending read in an `async` function:

Expand Down
4 changes: 2 additions & 2 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Reachable docs site (CLAUDE.md §10).
# Reachable docs site.
#
# Source of truth for docs structure. Auto-discovery is deliberately disabled —
# explicit `nav:` keeps page ordering deterministic.
Expand Down Expand Up @@ -67,7 +67,7 @@ markdown_extensions:
- toc:
permalink: true
toc_depth: 3
# PyMdown — same set as Backgrounder.
# PyMdown extensions.
- pymdownx.details
- pymdownx.highlight:
anchor_linenums: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import kotlin.native.ObjCName
* shared singleton via `Reachability.installForTesting`, runs your test
* block, then uninstalls and closes the fake in `finally`.
*
* Renames cleanly across the SKIE bridge: in Swift the class reads as
* Renames cleanly across the Swift bridge: in Swift the class reads as
* `FakeReachability`, with `emit(status:)`, `setReachable(_:)`,
* `setTransport(_:)`, `setDataMetered(_:)`, `reset()`, `closeCallCount`,
* `wasClosed`.
Expand Down