130 Widgets

Building tools. Learning to build tools. Learning to build learning tools.

0. Preface & Orientation

What FredCam is, what you’ll learn, and how this tutorial is organized.

What You’ll Build

FredCam is an iOS app that streams the live camera feed from a Bambu Lab P2S 3D printer to your iPhone or iPad. The printer streams over RTSPS — a TLS-encrypted variant of the Real-Time Streaming Protocol — using a self-signed certificate that most media players refuse to touch. FredCam works around that constraint to deliver a live feed you can monitor from across the house, complete with Portrait/landscape layout adaptation and Picture-in-Picture so you can watch the bed level while reading the next step of a recipe.

Bambu Lab P2S Local Network FredCam (iOS) ┌─────────────┐ ┌─────────┐ ┌───────────────────────────────┐ │ camera │───────▶│ RTSPS │────────▶│ KSPlayer (FFmpeg backend) │ │ port 322 │ │ TLS │ │ ↓ │ └─────────────┘ │ self- │ │ StreamState machine │ rtsps://bblp: │ signed │ │ ↓ │ <code>@<ip>:322 └─────────┘ │ SwiftUI view hierarchy │ │ portrait / landscape / PiP │ └───────────────────────────────┘

The app is small — six Swift files, around 650 lines of code total — but it packs in a surprising number of real-world iOS concepts: bridging UIKit into SwiftUI, state machines, reactive data models, adaptive layout, Picture-in-Picture, and the full dependency decision-making process including license implications. That’s what this tutorial is really about.

What You’ll Learn

This tutorial teaches three subjects in parallel:

iOS iOS & SwiftUI Network Networking Arch Architecture
ObservableObject — Swift’s reactive data model for driving UI from persistent state. RTSP / RTSPS — The streaming protocol used by IP cameras, and why the “S” creates problems. State machines — Modeling idle / connecting / live / error as a Swift enum with associated values.
UIViewRepresentable — The bridge that lets you embed any UIKit view inside a SwiftUI hierarchy. TLS and self-signed certificates — Why the printer’s certificate is rejected, and how FFmpeg bypasses the check. Coordinator pattern — Routing delegate callbacks from UIKit back into SwiftUI state.
Picture-in-Picture — Keeping the video feed alive while you switch apps. FFmpeg avOptions — Low-level transport and buffering knobs exposed through KSPlayer. Stable view identity — Why moving a view in the SwiftUI tree tears down and recreates it, and how to avoid that.
verticalSizeClass — Detecting landscape vs. portrait for adaptive layout. Local network permissions — iOS 14+ requirements for mDNS and local TCP connections. Dependency decisions — How to evaluate libraries: capability, license, maintainability, and what you’re signing up for.

Prerequisites

You need:

Tip

If you’ve already read the spectrum analyzer tutorial, much of the SwiftUI groundwork (stacks, modifiers, @State, @main) is already familiar. This tutorial focuses more on the inter-layer bridging and dependency decisions than on SwiftUI basics. You can skim the iOS-tagged sections if you’re comfortable with SwiftUI.

How This Tutorial Is Organized

Sections are tagged with one of three labels:

Unlike a build-along tutorial where you type code incrementally, this tutorial explains an existing app. Every section walks through real code from the FredCam source, explains each decision, and highlights the tradeoffs. You’re encouraged to have the source open in Xcode alongside these pages.

The Six Source Files

The entire app lives in six Swift files. Here’s the map before we dive in:

File Lines Role
FredCamApp.swift 10 App entry point. Creates the WindowGroup containing ContentView.
PrinterSettings.swift 25 Persistent data model. Stores printer IP and access code in UserDefaults. Builds the RTSPS URL.
CameraView.swift 96 The UIKit–SwiftUI bridge. Wraps KSPlayer inside a UIViewRepresentable and routes player events back into StreamState.
SettingsView.swift 77 Sheet-based UI for entering the printer IP and access code.
ContentView.swift 310 Main orchestrator. Defines StreamState, controls the view hierarchy, handles orientation, and renders all overlays.
TLSProxy.swift 125 Unused reference implementation. A local TCP proxy that tunnels plain-text RTSP over TLS. Kept for documentation value.
iOS Concept

The TLSProxy.swift file is an interesting artifact. It was written as an alternative approach — a local proxy that would accept plain RTSP and forward it over TLS, which would have let even VLC-based players work. It turned out to be unnecessary once KSPlayer’s direct tls_verify: 0 option was discovered. The file was kept because architectural dead ends are worth documenting; the next person to solve a similar problem might find the approach useful.