Contributing to TinyClips
August 12, 2026 · View on GitHub
Thank you for your interest in contributing to TinyClips! This guide covers everything you need to get started.
Table of Contents
- Requirements
- Project Setup
- Project Structure
- Development Tips
- Contribution Workflow
- Pull Request Guidelines
- Code Style
- Reporting Bugs
Requirements
- macOS 15.0 (Sequoia) or later
- Xcode 16.0 or later
- An Apple Developer account (needed for screen recording entitlements when running locally)
Project Setup
1. Fork and clone the repository
git clone https://github.com/<your-username>/tiny-clips.git
cd tiny-clips
2. Open the project in Xcode
open mac/TinyClips.xcodeproj
3. Add the Sparkle dependency (direct distribution target only)
Sparkle is not committed to the repository and must be added via Xcode:
- Go to File → Add Package Dependencies…
- Enter URL:
https://github.com/sparkle-project/Sparkle - Select Up to Next Major Version from
2.8.1 - Add the
Sparkleframework to theTinyClipstarget only (notTinyClipsMAS)
See docs/sparkle-setup.md for full details.
4. Select a scheme and run
| Scheme | Description |
|---|---|
TinyClips | Direct distribution — non-sandboxed, Sparkle-enabled |
TinyClipsMAS | Mac App Store — sandboxed, no Sparkle |
Press ⌘R to build and run the selected scheme.
Building without code signing (CI / headless)
# Direct distribution
xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
# Mac App Store variant
xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClipsMAS -configuration Debug \
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
Running macOS unit tests
The TinyClipsTests target covers deterministic capture math, settings, hotkey,
filename, and analytics behavior without requiring capture permissions or hardware:
xcodebuild test -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
Permissions
TinyClips requires Screen Recording permission at runtime. On first launch, macOS will prompt you to grant access in System Settings → Privacy & Security → Screen Recording.
Project Structure
mac/TinyClips/
├── TinyClipsApp.swift # App entry point, MenuBarExtra, CaptureManager
├── Models/
│ ├── CaptureSettings.swift # @AppStorage settings model and shared types
│ └── ClipItem.swift # Model for saved clips
├── Capture/
│ ├── RegionSelector.swift # Fullscreen overlay for region selection
│ ├── ScreenshotCapture.swift # SCScreenshotManager → PNG
│ ├── VideoRecorder.swift # SCStream → AVAssetWriter → MP4
│ └── GifWriter.swift # SCStream → CGImageDestination → GIF
├── Services/
│ ├── SaveService.swift # File saving, clipboard, Finder, notifications
│ ├── PermissionManager.swift # Screen recording permission handling
│ ├── SparkleController.swift # Sparkle auto-update integration
│ ├── StoreService.swift # StoreKit subscription management
│ └── ProManager.swift # Pro feature gating (MAS only)
└── Views/
├── SettingsView.swift # Settings window
├── ClipsManagerWindow.swift # Clips Manager window
├── VideoTrimmerWindow.swift # Post-recording trim editor
├── CapturePickerPanel.swift # Floating mode/countdown picker
├── ScreenPickerWindow.swift # Multi-display screen picker
├── RegionIndicatorPanel.swift # Red region outline overlay
├── StartRecordingPanel.swift # Floating record panel
├── StopRecordingPanel.swift # Floating stop panel
└── ...
docs/ # Additional setup guides
Two build targets share one codebase. Use #if APPSTORE compilation conditions for any Mac App Store–specific behavior and #if canImport(Sparkle) to guard Sparkle imports.
Development Tips
Architecture overview
- Menu bar app using SwiftUI
MenuBarExtrawith no Dock icon (LSUIElement = true). - Mixed SwiftUI + AppKit: SwiftUI for the menu, settings, and Clips Manager; AppKit
NSWindow/NSPanelsubclasses for capture-time overlays. CaptureManager(inTinyClipsApp.swift) is the central coordinator that owns recorders and capture window lifecycles.- Singleton services:
CaptureSettings.shared,SaveService.shared,PermissionManager.shared,SparkleController.shared.
Key conventions
- Use
ObservableObject/@Published/@StateObject— not the@Observablemacro. - Use
@AppStoragefor all user preferences (seeCaptureSettings.swift). - Mark all UI-facing classes
@MainActor. Use@unchecked Sendable+DispatchQueuefor off-main-thread capture classes. - Use
// MARK: -comments for section organization within files. - Single
CaptureErrorenum conforming toLocalizedError; surface errors viaSaveService.shared.showError().
Accessibility
Accessibility is treated as a release gate. When adding or changing UI:
- Add explicit
.accessibilityLabel,.accessibilityHint, and.accessibilityValueon icon-only buttons, custom controls, toggles, and stateful elements. - Ensure custom controls have keyboard alternatives (default/cancel shortcuts,
Esccancel paths). - Validate both
TinyClipsandTinyClipsMASschemes with VoiceOver and keyboard-only navigation.
Capture flows
- Screenshot: permission → picker (region/screen/window + countdown) → optional screen picker for multi-display → optional region indicator → capture → optional editor → save.
- Video/GIF: permission → picker → optional region indicator → start panel → optional countdown → record → stop panel → optional trimmer → save.
Contribution Workflow
- Check existing issues — search open issues before opening a new one or starting work.
- Open or claim an issue — comment on the issue you intend to work on so others know it's in progress.
- Create a feature branch off
main:git checkout -b feature/short-description - Make your changes following the code style guidelines below.
- Run unit tests and build both schemes to confirm nothing is broken:
xcodebuild test -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \ CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClips -configuration Debug \ CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO xcodebuild build -project mac/TinyClips.xcodeproj -scheme TinyClipsMAS -configuration Debug \ CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - Push your branch and open a pull request against
main.
Pull Request Guidelines
- One concern per PR — keep pull requests focused on a single feature, fix, or improvement.
- Descriptive title — use a short, imperative sentence (e.g., "Add countdown timer to GIF picker").
- Fill in the PR description — explain what changed and why. Link the related issue with
Closes #123. - Both schemes must build — the CI workflow builds both
TinyClipsandTinyClipsMAS; ensure your changes compile under both. - No Sparkle in
TinyClipsMAS— guard any Sparkle references with#if canImport(Sparkle)or#if !APPSTORE. - Update the CHANGELOG — add an entry under the appropriate section (
Added,Improved, orFixed) inCHANGELOG.md. - Accessibility — if your change affects any UI, verify it works with VoiceOver and keyboard-only navigation.
- Small, incremental commits — prefer clear, atomic commits with descriptive messages.
Code Style
- Swift 5, targeting macOS 15.0+.
- Follow the existing file and naming conventions — see the
// MARK: -section organization in existing files as a reference. - Keep SwiftUI views defined as
private structinside their window/panel file. - Use
popover(item:)overpopover(isPresented:)for data-dependent popovers. - Prefer closures/callbacks for inter-component communication — no
NotificationCenterposting. - Use
withCheckedContinuation/withCheckedThrowingContinuationto bridge callback APIs toasync/await. - Output file names follow the pattern
TinyClips yyyy-MM-dd 'at' HH.mm.ss.{ext}.
Reporting Bugs
Please use the bug report template when opening a new issue. Include:
- macOS version
- TinyClips version
- Steps to reproduce
- Expected vs. actual behavior
- Any relevant logs or screenshots
Thank you for helping make TinyClips better!