youtube-safer-streamer

Let's kids watch YouTube channels on Apple TV that parents approve on their iPhone.

0
0
0
Swift
public

SaferStreamer

Setup time: ~1 hour if you’re an iOS developer with Xcode already installed. ~3–5 hours if you’re new to all of this. After setup, daily use takes about 10 seconds (Face ID → tap a channel). See the How long will this take? section for the full breakdown.

A “safe YouTube” app for kids and Apple TV. The parent’s iPhone is used to add YouTube channels to a whitelist; the Apple TV shows only those channels, with no search bar, no recommendations, no comments, and no way for the child to wander off into the rest of YouTube.

  • iPhone app (parent): Face ID gate → search for YouTubers → tap to approve → swipe to remove.
  • Apple TV app (child): big tiles of approved YouTubers → tap one → pick a video → watch. Nothing else.
  • How it stays in sync: iCloud carries the channel list privately between your iPhone and Apple TV. There’s no server, no account to create, no telemetry.

If you’re new to building Apple apps and you’ve been “vibe coding” with AI tools — this guide is written for you. Follow the steps in order, copy and paste the commands as written, and don’t worry if some sections seem long: the detail is there so you don’t get stuck.


How long will this take?

If you are… Total time Where the time goes
iOS developer — Xcode installed, comfortable with Apple Developer / Google Cloud / iCloud dashboards ~1 hour Mostly clicking through dashboards (Apple Dev container creation, Google Cloud API key, CloudKit indexes). About 5 minutes of actual sed/Terminal work.
Some tech background — used Terminal before, never built an iOS app, no developer accounts yet ~2 hours Add 30 min for Xcode install + first-time Apple Developer / Google Cloud enrollment.
First-time vibe coder — never used Xcode, never used Terminal beyond cd, no developer accounts ~3–5 hours Most of the time is waiting on Xcode to download (~10 GB, can take an hour on home internet) and learning to navigate Apple’s and Google’s dashboards. Plan to do it across two sittings if needed.

You do not write any Swift code. Setup is all configuration: creating accounts, copy-pasting commands into Terminal, clicking through dashboards. The longest single waits are the Xcode download (~1 hour for non-developers who don’t have it) and the first Apple TV pairing (~5 minutes of fiddling).

You can absolutely pause partway through. Setup is one-time. Daily use after setup is trivial.


Read this first (5 minutes)

This is not an App Store app. SaferStreamer extracts YouTube video streams using a community library called YouTubeKit. This is against YouTube’s Terms of Service in a grey way, and it will not pass App Store review. Apple won’t punish you for installing it on your own family’s devices — but don’t try to publish it or sell it.

You’ll install it onto your own iPhone and Apple TV using Xcode (Apple’s free developer tool). This process is called “sideloading.” It’s safe and supported, but it does require you to sign up for Apple’s developer program (free for testing, or $99/year if you want the install to last more than 7 days).

You also need a Google Cloud account to get a free YouTube API key. This is what lets the iPhone search YouTube. The free quota is far higher than any family will ever use.

For the full time breakdown by experience level, see How long will this take? above. The short version: ~1 hour if you’ve done iOS dev before, ~3–5 hours if this is your first time with Xcode and Apple’s dashboards.


What you need before starting

Hardware

  • A Mac running macOS 14 (Sonoma) or later. You’ll need ~20 GB of free disk space for Xcode.
  • An iPhone running iOS 17 or later (this is the parent’s phone — used to add channels).
  • An Apple TV 4K running tvOS 17 or later (any generation — this is what the child watches).
  • A USB-C or Lightning cable to connect your iPhone to your Mac for the first install.

Accounts (all free unless noted)

  • An Apple ID, signed in to all three devices (your Mac, iPhone, and Apple TV). The iPhone and Apple TV must be on the same Apple ID for iCloud sync to work.
  • An Apple Developer account. Free is fine to start — installs last 7 days, then you redeploy. Paid ($99/yr) means installs last a year. Sign up at developer.apple.com/programs/enroll — or use the free tier first to verify everything works.
  • A Google Cloud account. Free. Sign in with any Google account at console.cloud.google.com.

Software (~30 min to install if you don’t have them)

You install these on your Mac, once. Open the Terminal app (press Cmd+Space, type “Terminal”, press Enter) and paste each block in order.

1. Install Xcode — Apple’s developer tool. Open the App Store, search for “Xcode”, click “Get” or “Install”. This is a ~10 GB download, plan accordingly. Once installed, open Xcode once, agree to the license, and let it install additional components.

2. Install Homebrew — a package manager that makes installing other tools painless. Paste this in Terminal:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Follow the prompts (it will ask for your Mac password — type it; you won’t see characters as you type, that’s normal). When it’s done, it tells you to run a couple of “Next steps” commands — do those, they add Homebrew to your shell.

3. Install XcodeGen — a tool this project uses to generate the Xcode project file. Paste this:

brew install xcodegen

You’re now ready to set up the project.


Setup walkthrough

The steps below are in the order you should do them. Each one is a checkpoint — if you get stuck, the next step won’t make sense, so stop and fix the problem (or ask AI — see the Getting help section).

Step 1 — Clone the repository to your Mac

Open Terminal. Paste:

cd ~
git clone https://github.com/Proxy/youtube-safer-streamer.git
cd "youtube-safer-streamer/youtube safer streamer app"

The folder name has spaces — the quotes are required.

You should now be inside a folder called “youtube safer streamer app”. Confirm by running pwd — it should print a path ending in youtube safer streamer app. Leave this Terminal window open; you’ll keep using it.

Step 2 — Generate the Xcode project

xcodegen generate

You’ll see a few lines about generating plists and writing the project. The last line should say Created project at .../SaferStreamer.xcodeproj. If you see xcodegen: command not found, you skipped the Homebrew install in the previous section.

Step 3 — Find your Apple Developer Team ID

You need a 10-character string from Apple. Get it from:

developer.apple.com/account#MembershipDetailsCard — sign in with your Apple ID

The page shows “Team ID” with a 10-character value like A1B2C3D4E5. Copy it.

If you haven’t enrolled in the Apple Developer program yet, you’ll be prompted to do so here. Free enrollment is fine to start.

Step 4 — Paste your Team ID into the project

In Terminal, replace ABCDE12345 below with your actual Team ID, then paste:

TEAM_ID="ABCDE12345"

sed -i '' "s/YOUR_TEAM_ID/${TEAM_ID}/g" project.yml
xcodegen generate

If you forget to change ABCDE12345 to your real ID, the build will fail later with “No matching signing certificate.” Just rerun this step with the correct ID and rerun xcodegen generate.

Step 5 — Pick a unique namespace and replace the bundle IDs

Every iOS app has a globally-unique “bundle ID” like com.yourname.appname. The current bundle IDs in this project use com.jonzhao.saferstreamer, which belongs to the original author and won’t work in your Apple Developer account. You need to change jonzhao to your own unique string.

Use something short, all lowercase, no dots — typically your name, initials, or company. For example alicedev, johnsmith42, or your initials plus a number. It doesn’t have to be a real domain you own.

In Terminal, replace yourname with your chosen namespace, then paste:

NAMESPACE="yourname"

sed -i '' "s/com\\.jonzhao\\.saferstreamer/com.${NAMESPACE}.saferstreamer/g" \
  project.yml \
  Shared/CloudKit/CloudKitConfig.swift \
  Shared/Utilities/Logger+Categories.swift \
  iOS/SaferStreamer-iOS.entitlements \
  tvOS/SaferStreamer-tvOS.entitlements

xcodegen generate

Verify it worked. Run:

grep -r "com.jonzhao" .

You should see no output. If you see any lines, the replacement missed something — re-run the sed block. Make sure you actually changed yourname to your real namespace before running.

From now on, write down your namespace — you’ll use it again in the Google and Apple consoles. Example: if you chose alicedev, your bundle IDs are now:

  • com.alicedev.saferstreamer (the iPhone app)
  • com.alicedev.saferstreamer.tv (the Apple TV app)
  • com.alicedev.saferstreamer.tests (the test bundle — ignore this one)

Step 6 — Create your iCloud container in Apple’s Developer console

The iCloud container is the private storage area where your iPhone and Apple TV exchange the channel list.

  1. Go to developer.apple.com/account/resources/identifiers/list/cloudContainer
  2. Sign in if asked.
  3. Click the blue + button to register a new identifier.
  4. Select iCloud Containers and click Continue.
  5. Description: type anything, e.g. SaferStreamer.
  6. Identifier: type iCloud.com.<your-namespace>.saferstreamer — exactly that, with your namespace substituted. Example: iCloud.com.alicedev.saferstreamer.
  7. Click Continue, then Register.

You don’t need to manually create App IDs — Xcode will do that automatically when you build in Step 12. You only needed to create the container ahead of time.

Step 7 — Get a YouTube Data API key

This is what lets the iPhone search YouTube. It’s free at the volume a single family uses.

  1. Go to console.cloud.google.com and sign in with any Google account.
  2. If this is your first time: you’ll see a welcome screen. Pick your country and accept the terms.
  3. At the top of the page, click the project dropdown (next to “Google Cloud”). Click NEW PROJECT.
  4. Project name: type anything, e.g. saferstreamer. Leave Organization as “No organization.” Click CREATE.
  5. Wait ~30 seconds for the project to be created. Then click the project dropdown again and select your new project.

If Google prompts you to set up a billing account: you can skip / dismiss this. The YouTube Data API works under the free 10,000 units/day quota without a billing account attached. (Family use is around 200 units/day, so you’re not at risk of exceeding it.) If the prompt blocks you and won’t go away, you can attach a billing account but you won’t be charged for normal use — Google requires you to add a card in some flows but YouTube API itself is in the always-free tier.

  1. In the left sidebar, click APIs & ServicesLibrary.
  2. In the search box, type YouTube Data API v3. Click the result. Click the blue ENABLE button. Wait ~10 seconds for it to enable.
  3. In the left sidebar, click APIs & ServicesCredentials.
  4. Click + CREATE CREDENTIALS at the top, then API key. A box pops up showing your key (it starts with AIza...). Click EDIT API KEY to add restrictions (don’t just copy the key and close the box).
  5. On the edit page:
    • Name: rename to SaferStreamer iOS or similar (optional).
    • Application restrictions: select the iOS apps option.
    • Click + ADD and enter your iOS bundle ID: com.<your-namespace>.saferstreamer
    • Click + ADD again and enter your tvOS bundle ID: com.<your-namespace>.saferstreamer.tv
    • API restrictions: click Restrict key, then in the dropdown check YouTube Data API v3 only, then OK.
  6. Scroll down and click SAVE.
  7. Back on the Credentials page, click the eye icon next to your key (or click the key’s name) and copy the full key value. You’ll paste it in the next step.

Step 8 — Add your API key to the project

The API key is a secret. You’ll put it in a file that is automatically excluded from git (so it never accidentally gets pushed to GitHub).

In Terminal (still in the youtube safer streamer app folder), run:

cp Config/Secrets.xcconfig.example Config/Secrets.xcconfig
open -e Config/Secrets.xcconfig

A TextEdit window opens with the file contents. You’ll see a line like:

YOUTUBE_API_KEY = REPLACE_WITH_YOUR_YOUTUBE_DATA_API_V3_KEY

Replace REPLACE_WITH_YOUR_YOUTUBE_DATA_API_V3_KEY with the actual key you copied (starts with AIza...). Save the file (Cmd+S) and close TextEdit.

Step 9 — Open the project in Xcode

In Terminal:

open SaferStreamer.xcodeproj

Xcode launches and shows the project. The first time you open it, Xcode will spend ~1–2 minutes downloading the YouTubeKit Swift package (you’ll see a progress indicator at the top). Wait for it to finish.

Step 10 — Connect your iPhone and prepare it for development

  1. Plug your iPhone into your Mac with a USB cable. If your iPhone asks “Trust This Computer?”, tap Trust and enter your iPhone passcode.
  2. In Xcode, look at the top toolbar. There’s a scheme/destination selector (looks like “SaferStreamer-iOS > iPhone 17 Pro” or similar). Click on the device name and select your real iPhone from the list.
  3. On your iPhone, enable Developer Mode (required by iOS 16+):
    • Open Settings → Privacy & Security → scroll to the bottom → tap Developer Mode → toggle it on.
    • Your iPhone will restart. Unlock it after restart and confirm Developer Mode is enabled (it’ll prompt).
  4. Make sure Face ID (or Touch ID) is enrolled on your iPhone. SaferStreamer uses biometric authentication only — there is no passcode fallback. If you don’t have Face ID set up, do that in Settings → Face ID & Passcode first.
  5. Make sure your iPhone is signed in to iCloud. Settings → tap your name at the top → confirm you see your Apple ID and that iCloud Drive is on.

Step 11 — Enable iCloud capability in Xcode

Xcode needs to know your app uses the iCloud container you created in Step 6.

  1. In Xcode, click on the blue SaferStreamer project icon at the very top of the left sidebar.
  2. In the main panel, you’ll see a list of “TARGETS” — click SaferStreamer-iOS.
  3. At the top of the main panel, click the Signing & Capabilities tab.
  4. Under “Signing”, make sure:
    • Automatically manage signing is checked.
    • Team shows your name / Apple ID. If it shows “None”, click the dropdown and select your team.
  5. Scroll down. You should see “iCloud” already listed as a capability (the project comes with it preconfigured). If you don’t see it, click + Capability at the top and add iCloud.
  6. Under iCloud, make sure:
    • CloudKit is checked.
    • In the Containers list, you should see iCloud.com.<your-namespace>.saferstreamer. If it’s not there, click the + button under Containers and add it.
  7. Repeat the same steps for the SaferStreamer-tvOS target (click it in the TARGETS list, then go to Signing & Capabilities, same checks).

If Xcode shows red error text like “Provisioning profile doesn’t include the iCloud entitlement” — click the Try Again button if it appears, or right-click your team name in the Signing section and choose “Download Manual Profiles.”

Step 12 — Build and run on your iPhone

In Xcode’s top toolbar, with your iPhone selected as the destination, click the ▶︎ Play button (or press Cmd+R).

Wait ~30–60 seconds for the build to complete. Xcode will compile the app, sign it, install it on your iPhone, and launch it. The first run is the slowest; subsequent runs are fast.

If the build fails with a code signing error:

  • The most common cause is that the App IDs haven’t been created yet on Apple’s side. Xcode should auto-create them — wait a few seconds, try Cmd+R again.
  • If it still fails: click the red error in the Issue Navigator (left sidebar, exclamation point icon) and read the message. The “Try Again” button often works.

If the build succeeds but the app crashes on launch or shows a “Trust” error:

  • Go to your iPhone → Settings → General → VPN & Device Management → tap your Developer App → Trust.
  • Re-launch the app from your home screen.

Step 13 — Add your first channel from the iPhone

  1. The app launches and shows a lock screen with the SaferStreamer logo. Face ID fires automatically — confirm your face.
  2. The management view appears, with a search bar at the top.
  3. Type a YouTuber’s name in the search bar (e.g., Mark Rober). Wait ~half a second — results appear.
  4. Tap the channel you want. A sheet slides up showing the channel art, name, and subscriber count.
  5. Tap Add. The sheet dismisses, and the channel appears in your “Approved channels” list. You’ll get a notification confirming the add.

If search fails:

  • “Can’t reach YouTube” → check internet connection.
  • “Too many searches today” → quota hit (very unlikely on first use).
  • “Something went wrong” → most likely cause: API key issue. See Troubleshooting below.

Step 14 — Set up CloudKit indexes (one-time, after first add)

Now that you’ve added at least one channel, you need to do one final piece of setup in the CloudKit Console. Without this, the app will appear to “lose” your channels on the next launch.

  1. Go to icloud.developer.apple.com and sign in with your Apple ID.
  2. At the top, switch the dropdown to show your team and select the container iCloud.com.<your-namespace>.saferstreamer.
  3. Make sure you’re in the Development environment (toggle at the top, not Production).
  4. Click Schema in the left sidebar, then Indexes.
  5. For each row in the table below, click Add Index, select the Record Type and Field, choose the Index Type, and Save:
Record Type Field Index Type
Channel recordName (Metadata) Queryable
Channel addedAt Queryable + Sortable
AuditEntry recordName (Metadata) Queryable
AuditEntry timestamp Queryable + Sortable
WatchProgress recordName (Metadata) Queryable
WatchProgress lastWatchedAt Queryable + Sortable

For “Queryable + Sortable” rows, add the index twice (once with Queryable, once with Sortable). For recordName (Metadata), the field is listed under “Metadata Index” / “System Fields” — you’ll see it in the dropdown.

Why this exists: CloudKit auto-creates the record types when your app writes them, but doesn’t auto-create the indexes needed to query them back. Without these, the app silently shows empty lists on reload even though your data is saved.

If you don’t add the indexes, you’ll see this error in Xcode’s console:

Field 'recordName' is not marked queryable

Once the indexes are added, restart the iPhone app and confirm your channel is still in the list.

Step 15 — Install on your Apple TV

The Apple TV needs to be paired with your Mac over the same Wi-Fi network. Both should be on the same Apple ID.

  1. On your Apple TV: Settings → Remotes and Devices → Remote App and Devices.
  2. On your Mac (Xcode open): menu bar → Window → Devices and Simulators.
  3. In the “Devices” tab, you should see your Apple TV appear under “Discovered” (it may take 10–20 seconds).
  4. Click your Apple TV → click Pair. A 6-digit code appears on your TV. Type it into Xcode.
  5. Wait for pairing to complete (~30 seconds). Once paired, your Apple TV appears in Xcode’s normal device list with a green dot.
  6. Back in the main Xcode project window: at the top, change the scheme to SaferStreamer-tvOS and the destination to your Apple TV.
  7. Press Cmd+R. First build takes ~1 minute. The app installs and launches on your TV.

If the Apple TV doesn’t show up in Xcode:

  • Make sure both devices are on the same Wi-Fi network.
  • Restart Xcode.
  • Restart the Apple TV (Settings → System → Restart).

If you see “Could not launch app because the device is not paired” on first launch:

  • On the Apple TV, open Settings → General → Profiles & Device Management → tap your developer profile → Trust.

Step 16 — Confirm sync works end-to-end

This is the moment of truth — did all that setup actually work?

  1. On the Apple TV, with the app running, you should see the channel you added on the iPhone.
  2. If you see “Ask your parent to add YouTubers on their phone.” — wait 10–30 seconds, then close and reopen the app (Apple TV remote: TV button → swipe up on the app → swipe up again to close → tap to reopen). The Apple TV pulls updates from iCloud on each launch.
  3. Tap your channel on the TV. You should see a grid of recent videos.
  4. Tap a video. It should start playing within a few seconds (the app extracts the stream URL on demand).

If you got here, the setup worked. 🎉

Add more channels from the iPhone whenever you want. They’ll appear on the TV within a few seconds (or definitely on the next TV-app launch).


Using the app day-to-day

Parent (iPhone):

  • Open the app. Pass Face ID.
  • Search → tap → confirm-add. Channel appears.
  • Swipe left on an approved channel to remove. Confirm in the alert. You have 5 seconds to undo.
  • Tap (top right) for list-level actions:
    • Edit list → enters edit mode: drag the 3-line grabbers to reorder, or tap the red minus circle to remove a channel. Tap Done when finished.
    • Sort list → choose Manual (drag-to-reorder, what you set with Edit list), Alphabetical, or Date added (newest first). The Apple TV grid reorders to match.
    • SettingsApple TV background (tap a color swatch to change what shows behind the channel tiles on the Apple TV), length cap (Hide videos longer than, default 40 min), and Activity Log (add/remove history).

Child (Apple TV):

  • Pick a channel → pick a video → watch.
  • Press the TV remote’s Back button to go back.
  • Press the TV button on the remote to exit the app.
  • There are no settings, search, comments, recommendations, or ads visible inside the app.

Auto-updates:

  • Channels added on the iPhone appear on the TV within seconds (when both are awake).
  • If the Apple TV was off when you added a channel, the channel will appear the next time the TV app is opened.

Troubleshooting

“Couldn’t load channel info” or “Couldn’t search” on the iPhone

Most likely: API key problem. In order, try:

  1. Open Config/Secrets.xcconfig and confirm your key is there (starts with AIza..., no quotes around it).
  2. In Google Cloud Console → Credentials → click your key → confirm:
    • Application restrictions are set to iOS apps with both bundle IDs listed: com.<your-namespace>.saferstreamer and com.<your-namespace>.saferstreamer.tv.
    • API restrictions are set to Restrict key with YouTube Data API v3 checked.
  3. Confirm the YouTube Data API v3 is enabled for your project: APIs & Services → Enabled APIs & services → look for “YouTube Data API v3” in the list.

The iPhone shows empty list even though I added channels

Almost always: missing CloudKit indexes. Re-do Step 14 above. Restart the app.

The Apple TV shows “Ask your parent to add YouTubers” even after I added channels on the iPhone

  1. Confirm both devices are signed in to the same iCloud account (Settings → Apple ID on iPhone; Settings → Users and Accounts on Apple TV).
  2. Confirm the Apple TV has internet (Settings → Network).
  3. Close and reopen the app on the TV (force-quit: TV button → swipe up on app → swipe up again).
  4. Wait 30 seconds. The TV app reloads from iCloud on each foreground.

“Trust this developer” error on iPhone or Apple TV

  • iPhone: Settings → General → VPN & Device Management → your developer profile → Trust.
  • Apple TV: Settings → General → Profiles & Device Management → your developer profile → Trust.

“No matching profile found” or other Xcode signing errors

  • In Xcode → project icon → Signing & Capabilities → make sure Automatically manage signing is checked and your team is selected.
  • Click Try Again if a button appears.
  • If it persists: Xcode menu → Settings → Accounts → click your Apple ID → click Download Manual Profiles. Then return to the project and try building again.

Videos won’t play (just spinner forever, or “This video isn’t available right now”)

  • YouTubeKit (the library that extracts stream URLs) occasionally breaks when YouTube changes their player format. Check the YouTubeKit releases page for a newer version. To update: edit project.yml, find from: "0.4.8", change to the newer version, run xcodegen generate, rebuild.
  • Some videos can’t be extracted no matter what (live streams, music videos with embedding disabled, region-locked content). Try a different video on the same channel.

Free Apple Developer account: the app stopped launching after a week

That’s expected. Free developer signing expires after 7 days. Reopen Xcode, plug in the device, press Cmd+R to redeploy.

To skip this hassle, enroll in the paid Apple Developer program ($99/yr) — installs then last a year.

I changed something and now nothing works

If you edited project.yml, always run xcodegen generate afterward — otherwise Xcode is still looking at the old project file.

If you added or moved any .swift file, run xcodegen generate too.


Getting help when you’re stuck

You’re doing this with AI tools. When something breaks, your AI assistant is your first line of help. Paste the exact error message into your AI (Claude, ChatGPT, or wherever you code) along with what step you were on, what you expected to happen, and what actually happened.

Useful prompt template:

I'm setting up the SaferStreamer iOS app following its README.
I'm on Step [N] — [step name].
I expected: [what you thought would happen].
What actually happened: [paste the error message verbatim].
My setup: macOS [version], Xcode [version], free / paid Apple Developer account.

If the error includes a long stack trace, paste the whole thing — AI assistants are good at parsing them.

For Xcode-specific errors: in Xcode, click the red exclamation point in the left sidebar (Issue Navigator) and click on each red line to see the full message.


For developers (architecture, contributing)

The setup above is all most users need. The rest of this section is for people who want to read the code or modify it.

Project layout

Shared/        Models, Stores, CloudKit, Services, Views, Utilities (used by both targets)
iOS/           iPhone-specific: Auth (BiometricGate), Notifications, parent UI
tvOS/          Apple TV-specific: child-facing viewer, AVPlayer playback
Tests/         121 pure-logic unit tests across 9 files (no async CloudKit mocking)
Config/        Secrets.xcconfig (gitignored)
project.yml    XcodeGen spec — source of truth

Key design notes

  • State: @Observable classes, @MainActor-isolated, hosted via @State and passed via .environment(...).
  • Persistence: optimistic-local + best-effort CloudKit; private database; reload() on launch and on scenePhase == .active.
  • Playback (tvOS): YouTubeKit extracts streams; AVPlayer prefers an HD composition (separate video+audio DASH streams merged via AVMutableComposition, enabling 1080p / 4K) and falls back to the highest muxed stream (~720p) if composition fails. WKWebView is unavailable on tvOS, so the IFrame Player API approach was not feasible.
  • Security boundary: iOS/Auth/BiometricGate.swift uses .deviceOwnerAuthenticationWithBiometrics (Face ID only — no passcode fallback).
  • Channel ordering: a Yahoo-Finance-style menu on iOS exposes Edit list / Sort list (Manual / Alphabetical / Date added) / Settings. The selected sort mode lives on the shared Settings CloudKit record, so the Apple TV grid stays in sync with whatever order the parent’s iPhone shows. Manual mode uses a dense sortIndex per Channel record, renumbered on every drag-to-reorder via .onMove. Inline minus-circle delete (.onDelete) and trailing swipe-to-remove both route through the same confirmation alert + 5-second undo toast.
  • Banner fallback color: when a channel has no banner art, the iOS app extracts the avatar’s dominant color at add-time (via CoreImage CIAreaAverage) and stores it as a hex string. The tvOS tile renders a gradient from that color instead of a flat fallback.

Running tests

xcodebuild -project SaferStreamer.xcodeproj -scheme SaferStreamerTests -destination "platform=iOS Simulator,name=iPhone 17 Pro" test

All 121 tests should pass in well under a second. Tests cover the pure-logic helpers: CKRecord round-trip mapping (including missing-field migration paths for sortIndex, channelSortMode, and backgroundChoice), biometric freshness/cooldown math, channel sort-order migration, channel sort-mode dispatch (sorted(_:by:)), Channel.withFallbackColor field-preservation invariants, settings clamping + three-field independence (length cap / sort mode / background), hex color round-trip, watch-progress completion thresholds.


License

MIT — do whatever you want with the code. Attribution appreciated but not required.

Support

I made this for personal use and don’t have bandwidth to answer issues or review pull requests. Fork it, modify it, share it with friends — but please don’t open issues asking for help. Your AI coding assistant is a much faster path to a working setup than waiting on me.

v0.3.3[beta]