A full-screen video player iOS app designed for controlled playlist playback via touch gestures, external keyboard, Bluetooth remote, or OSC (Open Sound Control) messages — with a companion macOS control app.
VideoPromptOSC is a minimalist video player for iPhone and iPad that allows users to manage a playlist of videos and control playback through simple gestures, an external keyboard, a Bluetooth remote, or OSC messages from a computer. The app is designed for presentation and teleprompter-style use cases where clean, distraction-free video playback is essential.
This repository contains two apps:
- VideoPromptOSC (iOS) — The video player app with playlist support
- VideoPromptDesktop (macOS) — A companion control surface for sending OSC commands via WiFi
- Full-screen video playback in landscape orientation
- Multi-video playlist with drag-to-reorder support
- Add videos via:
- Media library (PHPicker)
- AirDrop from Mac or other devices
- Touch gesture controls:
- Single tap: Play/Pause toggle
- Double tap: Reset to playlist start
- Right swipe: Play current video, then advance to next
- Left swipe: Load previous video
- External keyboard support with customizable key bindings:
- Default: Space (toggle), Arrow keys (next/previous)
- Press-to-assign configuration in settings
- Persistent bindings across sessions
- Bluetooth remote support — single press toggles play/pause, double press resets
- OSC control support — Play, Pause, Toggle, Reset, Next, Previous commands via UDP
- Visual feedback — on-screen indicator when OSC/remote/keyboard commands are received
- Playlist position indicator — shows current video number (e.g., "2 / 5")
- OSC status indicator — shows connection status and port number
- Minimal UI — persistent time display and settings gear in top-right corner
- Respects silent/ringer switch — audio follows device silent mode
- Universal app — works on both iPhone and iPad
- Six control buttons — Play, Pause, Toggle, Reset, Previous, Next
- Keyboard shortcuts — including arrow keys for playlist navigation
- WiFi-based OSC — sends commands directly to iPhone's IP address
- Always-on-top window — stays visible while working in other apps
- Connection status — shows configured host and port
- iOS 18.0 or later (deployment target; external keyboard support requires
.onKeyPress, iOS 17+) - iPhone or iPad device
- Xcode 15.0 or later (for building)
- macOS 13.0 (Ventura) or later
- Xcode 15.0 or later (for building)
VideoPromptRemote/
├── VideoPromptRemote.xcodeproj/ # iOS app Xcode project
├── VideoPrompt/ # iOS app source
│ ├── VideoPromptApp.swift # App entry point, audio session, OSC & AirDrop init
│ ├── ContentView.swift # Main view with gesture & keyboard handling
│ ├── Info.plist # App configuration, permissions & document types
│ ├── Models/
│ │ ├── PlaylistItem.swift # Playlist item data model
│ │ └── KeyBinding.swift # Keyboard binding data model
│ ├── Views/
│ │ ├── VideoPlayerView.swift # AVPlayer wrapper (UIViewRepresentable)
│ │ ├── VideoPickerView.swift # PHPicker for video selection
│ │ ├── PlayerOverlayView.swift # Top-right HUD (time + gear icon)
│ │ ├── TimeDisplayView.swift # Time display component (MM:SS / MM:SS)
│ │ ├── OSCFeedbackView.swift # OSC command feedback & status indicator
│ │ ├── PlaylistEditorView.swift # Playlist management with drag-to-reorder
│ │ └── KeyboardSettingsView.swift # Keyboard binding configuration UI
│ ├── ViewModels/
│ │ └── VideoPlayerViewModel.swift # Playback state, playlist navigation & business logic
│ ├── Services/
│ │ ├── VideoStorageService.swift # Video persistence to Documents folder
│ │ ├── OSCService.swift # OSC server for receiving commands
│ │ ├── VolumeButtonService.swift # Bluetooth remote volume button detection
│ │ ├── PlaylistService.swift # Playlist state management & persistence
│ │ └── KeyboardService.swift # External keyboard event handling
│ ├── Utilities/
│ │ └── TimeFormatter.swift # Time formatting helper
│ └── Assets.xcassets/
│ ├── AppIcon.appiconset/ # App icon (blue with white "VP")
│ └── AccentColor.colorset/ # Accent color
│
└── VideoPromptDesktop/ # macOS companion app
├── VideoPromptDesktop.xcodeproj/ # macOS app Xcode project
└── VideoPromptDesktop/ # macOS app source
├── VideoPromptDesktopApp.swift # App entry, window config, keyboard shortcuts
├── ContentView.swift # Main UI with button grid & status
├── Views/
│ └── ControlButton.swift # Styled button component
└── Services/
├── OSCClientService.swift # OSC UDP client for sending commands
└── IProxyService.swift # iproxy process management (optional)
- Open
VideoPromptRemote.xcodeprojin Xcode - Wait for Swift Package Manager to fetch the OSCKit dependency
- Select your development team in Signing & Capabilities
- Connect your iOS device
- Select your device as the build target
- Press
Cmd + Rto build and run
# Navigate to project directory
cd /path/to/VideoPromptRemote
# Build for device (requires signing configuration)
xcodebuild -project VideoPromptRemote.xcodeproj \
-scheme VideoPromptOSC \
-sdk iphoneos \
-configuration Release \
build
# List available schemes
xcodebuild -list -project VideoPromptRemote.xcodeproj- Open
VideoPromptDesktop/VideoPromptDesktop.xcodeprojin Xcode - Wait for Swift Package Manager to fetch OSCKit
- Select your development team in Signing & Capabilities
- Build and run (
Cmd + R)
See VideoPromptDesktop/README.md for detailed instructions.
- Open the app
- Tap the gear icon (⚙️) in the top-right corner
- Tap the + button to add videos from your photo library
- Optionally, AirDrop videos from your Mac
- Reorder videos by dragging them up/down in the list
- Tap Done to start playback
| Action | Result |
|---|---|
| Single tap on screen | Play/Pause toggle |
| Double tap on screen | Reset to playlist start (first video, beginning) |
| Right swipe | Play current video; if finished, advance to next |
| Left swipe | Load previous video |
| Camera remote shutter | Play/Pause toggle |
| Bluetooth remote (Volume Up) | Play/Pause toggle |
| Bluetooth remote (Volume Up x2) | Reset to playlist start |
| Gear icon tap | Open playlist editor & settings |
Connect an external keyboard (Bluetooth or USB) for hands-free control:
| Default Key | Action |
|---|---|
| Space | Toggle Play/Pause |
| → (Right Arrow) | Next video |
| ← (Left Arrow) | Previous video |
- Tap the gear icon (⚙️)
- Tap Keyboard Controls
- Tap any action row or the Set button
- Press the desired key on your keyboard
- The binding is saved automatically
Available actions to bind:
- Toggle Play/Pause
- Play
- Pause
- Next Video
- Previous Video
- Reset Playlist
The app supports Bluetooth remotes that simulate volume button presses:
- Single press of Volume Up → Toggle Play/Pause
- Double press of Volume Up (within 0.4 seconds) → Reset to beginning
This works with most Bluetooth camera shutter remotes and presentation clickers that send volume key events.
The top-right corner shows:
- Current playback position
- Total video duration
- Format:
MM:SS / MM:SS(orH:MM:SSfor videos over 1 hour)
The bottom-left corner shows:
- Green dot: OSC server running
- Red dot: OSC server not running
- Port number (default: 9000)
VideoPromptOSC includes a built-in OSC server that listens for control messages over UDP.
| Setting | Value |
|---|---|
| Protocol | UDP |
| Port | 9000 |
| OSC Address | Action |
|---|---|
/videoprompt/play |
Start playback |
/videoprompt/pause |
Pause playback |
/videoprompt/toggle |
Toggle play/pause |
/videoprompt/reset |
Reset to playlist start |
/videoprompt/next |
Load and play next video |
/videoprompt/previous |
Load previous video |
To control the app via WiFi from a Mac:
-
Ensure both devices are on the same WiFi network
-
Find your iPhone's IP address:
- On iPhone: Settings → WiFi → tap the connected network → IP Address
-
Configure VideoPromptDesktop (or your OSC app):
- Click the gear icon (⚙️)
- Enter the iPhone's IP address in the Host field
- Port:
9000
-
Send OSC commands using the control buttons or keyboard shortcuts
Using oscsend (install via brew install liblo):
# Play
oscsend localhost 9000 /videoprompt/play
# Pause
oscsend localhost 9000 /videoprompt/pause
# Toggle play/pause
oscsend localhost 9000 /videoprompt/toggle
# Reset to beginning
oscsend localhost 9000 /videoprompt/resetWhen an OSC command is received:
- A colored overlay briefly appears in the center of the screen
- The command name is displayed (PLAY, PAUSE, TOGGLE, RESET)
- Each command type has a distinct color for easy identification
- Uses
AVPlayerLayeras the view's layer class for optimal performance - Implements
UIViewRepresentablefor SwiftUI integration - Includes identity checking to prevent unnecessary layer recreation
@MainActorannotated for thread safety- Manages
AVPlayerinstance and playback state - Handles time observation (updates every 0.5 seconds)
- Playlist-aware navigation (next, previous, reset)
- Implements
OSCCommandHandlerandKeyboardCommandHandlerprotocols
- Manages ordered list of
PlaylistItemobjects - Handles video file copying to Documents directory
- Persists playlist order in
UserDefaultsas JSON - Supports add, remove, move (reorder) operations
- Migrates single-video users to playlist format
- Captures external keyboard events via SwiftUI
.onKeyPress - Maps keys to actions with customizable bindings
- Stores bindings in
UserDefaultsas JSON - Supports press-to-assign configuration
- Default bindings: Space, Arrow keys
- Uses OSCKit library for OSC message parsing
- Runs UDP server on configurable port (default: 9000)
- Supports play, pause, toggle, reset, next, previous commands
- Manages visual feedback state for UI updates
- Detects volume button presses from Bluetooth remotes
- Observes
AVAudioSession.outputVolumechanges to detect button presses - Implements double-press detection with configurable interval (0.4s)
- Uses hidden
MPVolumeViewto suppress system volume HUD - Resets volume after each press to prevent maxing out
- OSCKit (Swift Package Manager) - https://github.com/orchetect/OSCKit
- Used for OSC message parsing and UDP server
AVAudioSession.sharedInstance().setCategory(.ambient, mode: .moviePlayback)Using .ambient category ensures the app respects the device's silent/ringer switch.
The app is locked to landscape orientation via Info.plist:
UIInterfaceOrientationLandscapeLeftUIInterfaceOrientationLandscapeRight
The app requires the following permissions (configured in Info.plist):
- Photo Library Access (
NSPhotoLibraryUsageDescription): Required to select videos from the user's media library - Local Network Access (
NSLocalNetworkUsageDescription): Required for receiving OSC messages over the network
The app icon features:
- Blue background (#007AFF - Apple system blue)
- White "VP" text in Helvetica Bold
- 1024x1024 resolution
To regenerate the icon:
cd VideoPrompt/Assets.xcassets/AppIcon.appiconset
magick -size 1024x1024 xc:'#007AFF' \
-gravity center \
-font "Helvetica-Bold" \
-pointsize 420 \
-fill white \
-annotate 0 "VP" \
AppIcon.png- Ensure
VideoPlayerViewuseslayerClassoverride (not manual layer management) - Check that player identity comparison uses
!==to prevent unnecessary updates
- Ensure your Bluetooth remote sends Volume Up key events
- Try pressing the button once to verify the app detects volume changes
- Check that the app is in the foreground
- Some remotes may need to be re-paired with the device
- Check that the app has write access to Documents directory
- Verify
UserDefaultsis saving the file path correctly
- Check that the OSC status indicator shows a green dot (server running)
- Confirm both devices are on the same WiFi network
- Confirm the Mac OSC app is sending to the iPhone's WiFi IP address on port
9000 - Approve the iOS "Local Network" permission prompt on first launch
- Check that no firewall is blocking UDP traffic on port 9000
- Commands are being received but feedback may be too fast to see
- Check console output for "Received OSC command:" messages
VideoPromptDesktop is a native macOS app for controlling VideoPromptOSC via WiFi. It provides:
- Six control buttons: Play, Pause, Toggle, Reset, Previous, Next
- Keyboard shortcuts including arrow keys for playlist navigation
- WiFi-based OSC communication (enter iPhone's IP address)
- Always-on-top floating window
- Configurable host/port settings
See VideoPromptDesktop/README.md for full documentation.
- Ensure Mac and iPhone are on the same WiFi network
- Launch VideoPromptOSC on iOS
- Launch VideoPromptDesktop on Mac
- Enter iPhone's IP address in Settings (gear icon)
- Use buttons or keyboard shortcuts to control playback
| Button | Keyboard Shortcut |
|---|---|
| Play | Enter (↵) |
| Pause | Escape (⎋) |
| Toggle | Space (␣) |
| Reset | R |
| Previous | ← (Left Arrow) |
| Next | → (Right Arrow) |
-
3.0 - Playlist & Keyboard Control
- Multi-video playlist support with drag-to-reorder
- AirDrop support for adding videos from Mac
- Swipe gestures for playlist navigation (right = next, left = previous)
- External keyboard support with customizable key bindings
- Press-to-assign keyboard configuration UI
- New OSC commands:
/videoprompt/next,/videoprompt/previous - Desktop app: Added Previous/Next buttons with arrow key shortcuts
- Desktop app: WiFi-based OSC (direct IP connection, no USB tunneling)
- Playlist position indicator in UI
-
2.1 - Bluetooth Remote Improvements
- New
VolumeButtonServicefor reliable Bluetooth remote detection - Volume button observation replaces remote control events
- Hidden
MPVolumeViewsuppresses system volume HUD - Automatic volume reset prevents maxing out
- New
-
2.0 - OSC Control Update
- Added OSC server for remote control via USB
- OSC commands: play, pause, toggle, reset
- Visual feedback for received OSC commands
- OSC status indicator in UI
- Renamed app to VideoPromptOSC
- Added OSCKit dependency via SPM
- Added VideoPromptDesktop macOS companion app
-
1.0 - Initial release
- Full-screen video playback
- Touch gesture controls
- Camera remote support
- Persistent video selection
- Time display overlay
If you're picking up development on this project, start with HANDOVER.md.
It documents the architecture, control-input fan-in, persistence model, known issues
(including the desktop app's legacy libimobiledevice gate), and suggested next steps.
Released under the MIT License — free to use, modify, and distribute with no restrictions.
Copyright © 2026 Marco Tempest - MagicLab. Third-party dependencies (OSCKit, CocoaAsyncSocket, SwiftASCII) retain their own licenses.