A native client for Reolink cameras, NVRs, and Home Hubs — on Mac, iPad, and iPhone.
Website · TestFlight · Install · What's new · Issues
Reolens is a native app for watching your Reolink cameras. SwiftUI, Swift 6, AVFoundation / VideoToolbox — no Electron, no Java, no QtWebEngine. Cold launches in under a second; feels like every other native Apple app on each platform.
It runs entirely on your devices. No Reolens server, no third-party analytics, no telemetry, no accounts.
The footage in marketing screenshots is procedurally rendered (a small Swift script generates "stock" camera scenes) rather than real captures — privacy-clean, license-clean, and still representative of what the app looks like in use.
Reolens ships as one multiplatform app through TestFlight — Mac, iPad, and iPhone share a single App Store record. Open the invite on each device and TestFlight installs the right build:
https://testflight.apple.com/join/c815tbVE
Requires macOS 26+ / iPadOS / iOS 26+. The Mac app moved to the App Store / TestFlight in 0.8; the old Developer-ID DMG and Homebrew/Sparkle path is retired.
git clone https://github.com/jestatsio/reolens.git
cd reolens
./Scripts/build-app.sh run # macOS — builds + signs + launches
cd AppiOS && xcodegen generate # iOS — then open in Xcode 26Requires Xcode 26 + Swift 6.2.
- Launch Reolens. It asks for Local Network permission (needed to reach your cameras) and Notification permission (for motion alerts).
- Click the + in the sidebar to add a camera. Enter the IP address (or hostname), username, and password — the rest is auto-detected.
- Pick a camera in the sidebar to view it. Use the layout picker in the toolbar to switch between adaptive / spotlight / 2×2 / 3×3 / 4×4 grids.
Drag tiles to rearrange them. Right-click a tile for "Make primary", "Rotate", and per-channel settings.
Reolens is LAN-only by design. It reaches your camera at its LAN IP and does not exchange anything with Reolink's cloud, a DDNS provider, or any third-party relay. To make a LAN-only app reachable when you're away from home, put your phone on the LAN with an overlay network. Tailscale is the recommended path — it's free for personal use, takes about ten minutes to set up, and avoids every footgun of port forwarding (open ports, WAN-IP exposure, CGNAT'd ISPs, untrusted DDNS providers).
See docs/remote-connectivity.md for the full step-by-step. The short version:
- Install Tailscale on something always-on inside your LAN —
an Apple TV (the sleeper-pick: zero extra hardware in an Apple
household), a UniFi / OPNsense / pfSense / OpenWrt router with
the native Tailscale plugin, a Synology / QNAP NAS, or a Raspberry
Pi. Enable subnet routing so it advertises your LAN's CIDR
(e.g.
192.168.1.0/24). - Approve the route in the Tailscale admin console.
- Install Tailscale on your iPhone, iPad, and Mac, sign in with the same account.
- Open Reolens off-LAN — your phone reaches the hub at its LAN IP exactly as it does at home. No setting to change in Reolens.
Why an overlay network instead of DDNS: zero ports forwarded, nothing on the public internet to scan, works on CGNAT'd connections, no trust delegated to a free DDNS provider, family members reach the cameras by inviting them to your tailnet.
- Multi-camera grids with adaptive layout, spotlight, and fixed 2×2 → 5×5 presets. Drag-and-drop reorder. Per-camera rotation.
- Hardware-decoded RTSP (H.264 / H.265) via VideoToolbox. FLV / JPEG fallback when the stream renegotiates.
- Picture-in-Picture, pinch-to-zoom, drag-to-pan on iOS / iPadOS.
- Two-way talkback on supported cameras.
- Full PTZ — all 17 ops (pan / tilt / zoom / focus / presets / patrols) from a dedicated control bar.
- Battery cameras wake on the first tap; the single-camera detail view auto-wakes on appear so you don't tap twice.
- Per-camera Recordings tab with a day-density calendar, a per-day timeline strip with AI-event ticks, and a player that scrubs with a thumbnail rail.
- All Recordings view that fans across every channel — and across hubs — into one chronological feed. Filter by AI tag or by camera.
- Cross-day natural-language search ("packages this week",
"vehicles yesterday at the driveway"). Runs through Apple's on-device
FoundationModelswhen available (no recording data crosses the model boundary — just the prompt); otherwise a deterministic regex parser. - Bookmarks — long-press / right-click / swipe to bookmark without playing. The clip downloads in the background so it's available offline; Wi-Fi by default, cellular toggle in Settings.
- Rich motion / AI notifications with the trigger frame, not just text. Tap to jump straight to the clip.
- CloudKit-relayed background pushes to iOS / iPadOS — events ride through your own iCloud private database, no Reolens server.
- Per-camera mute (default on, iCloud-synced) and per-AI-tag filters. Notification diagnostics screen + a 1,000-record rolling notification log so silent-push issues are self- diagnosing. Overnight digest — local notification at a user- configurable hour summarizing the previous day's events.
- Recording schedule editor — visual 7×24 weekly grid of when each
camera writes to storage. Reads / writes via Reolink's
RecCGI command; degrades to read-only on firmware that doesn't expose it. - Motion-detection schedule with per-AI-tag overrides — set
the channel-level "when can alarms fire" window, then override per
tag (e.g. quiet
vehicleovernight while still alerting onpeople). - Motion privacy zones — draw rectangles on the live frame;
written back via
SetMaskwith graceful local-only fallback.
- Stage Manager / multi-window on iPad + macOS. "Open in New Window" on any camera row.
- Home Screen / Lock Screen / Control Center widgets on iOS / iPadOS; desktop widgets on macOS.
- Live Activities + Dynamic Island on iOS for in-flight motion events. Hub-grouped — multiple events on the same hub merge into one Live Activity rather than stacking.
- HomeKit bridge scaffolding on iOS / iPadOS (added in 0.6.0). The per-camera "Expose to HomeKit" toggle syncs through iCloud so flipping it on one device propagates to the device that has the entitlement. Full HomeKit Secure Video recording tier ships once Apple completes MFi certification — see docs/ROADMAP.md.
- Liquid Glass throughout — toolbars, sidebars, chips, badges,
popovers, sheets, HUDs all use the iOS 26 / macOS 26
.glassEffect()material via centralized design tokens. - iCloud sync — camera list, grid layout, channel order, and rotations sync across your Apple devices. Passwords stay per- device in Keychain by default; optional iCloud Keychain sync is in Settings.
- Shortcuts & Siri — "Hey Siri, open the Front Door camera in Reolens." Three App Intents: Open Camera, Show Today's Events, Mute Camera Notifications.
- Auto-updates — App Store and TestFlight on every Apple platform.
For the full feature surface and the per-release history, see CHANGELOG.md.
- macOS 26 Tahoe or later on Apple Silicon or Intel
- iPadOS 26 / iOS 26 or later on any device that runs them
- A Reolink camera, NVR, or Home Hub reachable on the local network
- HTTP / HTTPS access to the device's CGI port (default 80 / 443)
Users on macOS 14 / iOS 18 should stay on the 0.4.x security-backport track. See SECURITY.md. FoundationModels-driven features (the Today digest, NL search) fall back to deterministic implementations on devices without Apple Intelligence.
![]() |
![]() |
| Adaptive multi-camera grid | Spotlight layout |
![]() |
![]() |
| Detail view with full PTZ controls | About panel — version + Check for Updates |
Reolens runs entirely on your devices. The network surface is:
- Your Reolink cameras, NVRs, and Home Hubs (over the local network)
- iCloud Drive — syncs your camera list across your own Apple devices
- iCloud CloudKit — relays motion-event pushes between your devices (private database, never touches Apple's public surface)
- Apple's TestFlight / App Store (iOS and macOS, for app updates)
That's it. No third-party analytics. No remote crash reporting. No telemetry. No accounts.
Credentials are device-local. Camera passwords live in each
device's Keychain (kSecAttrSynchronizable: false — explicitly never
synced), and the iCloud Drive sync carries only metadata (display
name, host, port, username, grid layout). The full credential model
is documented in AGENTS.md §3, §4.
┌──────────────────────────────────────────────────────────────────┐
│ App (SwiftUI views, @Observable state, ReolensApp / RootView) │
│ AppiOS/ (iOS twin) + Widget extensions + Live Activity ext. │
├──────────────────────────────────────────────────────────────────┤
│ AppShared (CameraStore split, RecordingsLoader, RecordingIndex, │
│ PollManager, EventNotifier, RelayDiagnostics, │
│ SharedContainer, ReolensGlass design tokens, …) │
├──────────────────────────────────────────────────────────────────┤
│ ReolinkBaichuan (port 9000: talkback, alarm push, findAlarmVideo)│
│ ReolinkStreaming (RTSP + VideoToolbox + H.264 / H.265 + SDP) │
├──────────────────────────────────────────────────────────────────┤
│ ReolinkAPI (CGI client, Commands, Codable models, StreamURLs) │
└──────────────────────────────────────────────────────────────────┘
Dependency-only-downward. ReolinkAPI ships standalone — no UI deps,
testable in isolation. AppShared is the cross-platform behaviour
layer driving the macOS app, the iOS / iPadOS app, both widget
extensions, and the Live Activity extension.
Key concurrency primitives:
CGIClientis an actor — one instance per camera. Reolink devices have a notoriously small global session cap, so the actor serializes login / refresh and reuses one token across all commands.CameraSessionis@MainActor-isolated and@Observable— SwiftUI views observe its state directly.RecordingsLoaderis an@MainActor @Observableclass with a generation counter so a rapid date-flip never publishes stale results on top of the latest reload.PollManagerowns the motion-event polling lifecycle separately fromCameraSessionwith a depth-counted pause/resume primitive.
See AGENTS.md for the engineering principles that gate every change (platform parity by default, no telemetry, no credentials in logs) and AppiOS/README.md for the iOS-specific project layout.
Package.swift — SwiftPM manifest (libs + macOS executable + tests)
App/ — macOS SwiftUI executable
ReolensApp.swift — @main, About panel, Check-for-Updates menu
Views/ — sidebar, grid, detail, PTZ, settings, scrubber,
bookmarks, schedule editors, About
Widgets/ — macOS desktop WidgetKit extension target
AppiOS/ — iOS / iPadOS Xcode project (xcodegen-managed)
Sources/ — RootView, iPadSplitShell, iPhoneTabShell,
SingleChannelView, LiveActivities/
Widgets/ — iOS WidgetKit + Control Center + ActivityKit
extension target (5 widget surfaces total)
UITests/ — XCUITest baseline journeys
project.yml — xcodegen spec; run `xcodegen generate` after edits
Sources/
AppShared/ — cross-platform behaviour layer
ReolinkAPI/ — CGI client + Codable models + StreamURLs
ReolinkStreaming/ — RTSP / VideoToolbox / H.264 + H.265 / SDP
ReolinkBaichuan/ — port-9000 protocol (talkback, push, alarms)
Tests/ — ~340 tests across 68 suites
Scripts/ — build, sign, App Store (TestFlight) upload,
icon generation, coverage gate, version-check gate
docs/ — reolens.io landing page (GitHub Pages),
RELEASE.md + IOS_RELEASE.md + MAC_RELEASE.md runbooks
.github/workflows/
ci.yml — build + test + smoke launch + coverage gate
+ iOS XCUITest job
release.yml — on tag push: archive + upload iOS & macOS
to App Store Connect / TestFlight
swift build # libs + macOS app
swift test # ~340 tests across 68 suites
./Scripts/build-app.sh run # bundled .app (needed for Local Network access)CI gates (also runnable locally):
bash Scripts/check-versions.sh # macOS + iOS marketing versions must match
bash Scripts/coverage-gate.sh # per-target coverage regression gateSee docs/RELEASE.md for the full runbook. Short version:
- Bump
CFBundleShortVersionStringin App/Info.plist and the matchingMARKETING_VERSIONin AppiOS/project.yml. The check-versions gate blocks PRs that drift. - Add a section to CHANGELOG.md.
git tag v0.8.4 && git push --tags.- Watch .github/workflows/release.yml build, sign, and upload the Mac and iOS apps to TestFlight / the App Store, then publish a GitHub Release.
PRs welcome. Before opening one, read AGENTS.md (engineering principles — platform parity, device-local credentials, no third-party analytics) and CONTRIBUTING.md (dev setup, test expectations, commit conventions).
Security issues: please use the private reporting flow described in SECURITY.md rather than filing a public issue.
MIT — see LICENSE.
Reolink is a trademark of Reolink Innovation Inc. Reolens is an unaffiliated third-party client. The Reolink protocol is reverse- engineered from public CGI documentation and community projects:
- Reolink CGI v1.61 reference PDF
- starkillerOG/reolink_aio (Python ref, used by Home Assistant)
- thirtythreeforty/neolink (Rust ref for Baichuan)



