Development Guide
Last Updated: 2026-02-03 | Reading Time: 20 minutes
Complete guide for contributing to PasteShelf development.
Table of Contents
Section titled “Table of Contents”- Development Environment
- Project Structure
- Architecture Overview
- Coding Standards
- Building and Running
- Testing
- Debugging
- Feature Development
- Pull Request Process
Development Environment
Section titled “Development Environment”Required Tools
Section titled “Required Tools”| Tool | Version | Installation |
|---|---|---|
| Xcode | 15.0+ | Mac App Store |
| Swift | 5.9+ | Bundled with Xcode |
| Git | 2.30+ | xcode-select --install |
| Homebrew | Latest | brew.sh |
| SwiftLint | Latest | brew install swiftlint |
| SwiftFormat | Latest | brew install swiftformat |
Environment Setup
Section titled “Environment Setup”# 1. Install Xcode from Mac App Store
# 2. Install Xcode Command Line Toolsxcode-select --install
# 3. Install Homebrew/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 4. Install development toolsbrew install swiftlint swiftformat
# 5. Clone the repositorygit clone https://github.com/pasteshelf/pasteshelf.gitcd pasteshelf
# 6. Open in Xcodeopen PasteShelf.xcodeprojXcode Configuration
Section titled “Xcode Configuration”Recommended Settings
Section titled “Recommended Settings”Xcode → Settings → Text Editing: ✓ Show line numbers ✓ Page guide at column: 120 ✓ Trim trailing whitespace Indentation: 4 spacesBuild Settings
Section titled “Build Settings”Product → Scheme → Edit Scheme: Build Configuration: Debug Diagnostics: ✓ Address Sanitizer ✓ Thread Sanitizer (optional) ✓ Main Thread CheckerProject Structure
Section titled “Project Structure”PasteShelf/├── PasteShelf/ # Main application source│ ├── PasteShelfApp.swift # App entry point│ ├── ContentView.swift # Main view│ ├── Persistence.swift # CoreData controller│ ├── PasteShelf.xcdatamodeld/ # CoreData model│ ││ ├── Core/ # Core business logic│ │ ├── Clipboard/ # Clipboard monitoring│ │ │ ├── ClipboardMonitor.swift│ │ │ ├── ClipboardItem.swift│ │ │ └── ContentType.swift│ │ ├── Search/ # Search engine│ │ │ ├── SearchEngine.swift│ │ │ ├── SearchIndex.swift│ │ │ └── SearchResult.swift│ │ ├── Storage/ # Data persistence│ │ │ ├── StorageManager.swift│ │ │ └── MigrationManager.swift│ │ ├── Sync/ # CloudKit sync (Pro)│ │ │ ├── SyncEngine.swift│ │ │ └── ConflictResolver.swift│ │ ├── Security/ # Encryption & privacy│ │ │ ├── EncryptionManager.swift│ │ │ ├── SensitiveDataDetector.swift│ │ │ └── BiometricAuth.swift│ │ └── Plugins/ # Plugin system│ │ ├── PluginManager.swift│ │ └── PluginProtocol.swift│ ││ ├── UI/ # User interface│ │ ├── FloatingPanel/ # Quick access panel│ │ │ ├── FloatingPanelView.swift│ │ │ └── ClipboardRowView.swift│ │ ├── MainWindow/ # Main application window│ │ │ ├── MainWindowView.swift│ │ │ └── SidebarView.swift│ │ ├── MenuBar/ # Menu bar integration│ │ │ └── MenuBarController.swift│ │ ├── Preferences/ # Settings UI│ │ │ └── PreferencesView.swift│ │ ├── Onboarding/ # First-run experience│ │ │ └── OnboardingView.swift│ │ ├── Upgrade/ # Upgrade prompts (Pro)│ │ │ └── UpgradeView.swift│ │ └── Components/ # Shared UI components│ │ ├── SearchField.swift│ │ └── ItemPreview.swift│ ││ ├── Models/ # Data models│ │ └── Item+Extensions.swift│ ├── Extensions/ # Swift extensions│ │ └── String+Extensions.swift│ ├── Utilities/ # Helper utilities│ │ └── Logger.swift│ └── Resources/ # Assets & localization│ ├── Assets.xcassets/│ └── Localizable.xcstrings│├── PasteShelfTests/ # Unit tests│ └── PasteShelfTests.swift├── PasteShelfUITests/ # UI tests│ └── PasteShelfUITests.swift│├── .github/ # GitHub configuration│ ├── workflows/ # CI/CD workflows│ │ ├── ci.yml│ │ └── release.yml│ └── ISSUE_TEMPLATE/│├── docs/ # Documentation├── fastlane/ # Automation scripts└── Configuration files ├── .swiftlint.yml ├── .swiftformat └── .gitignoreArchitecture Overview
Section titled “Architecture Overview”High-Level Architecture
Section titled “High-Level Architecture”┌─────────────────────────────────────────────────────────────┐│ PasteShelf │├─────────────────────────────────────────────────────────────┤│ ┌─────────────────────────────────────────────────────┐ ││ │ UI Layer │ ││ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ ││ │ │ Floating │ │ Main │ │ Menu Bar │ │ Prefs │ │ ││ │ │ Panel │ │ Window │ │ │ │ │ │ ││ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ │ ││ └───────┼────────────┼────────────┼───────────┼───────┘ ││ │ │ │ │ ││ ┌───────┴────────────┴────────────┴───────────┴───────┐ ││ │ Core Layer │ ││ │ ┌───────────┐ ┌────────┐ ┌─────────┐ ┌──────────┐ │ ││ │ │ Clipboard │ │ Search │ │ Storage │ │ Security │ │ ││ │ │ Monitor │ │ Engine │ │ Manager │ │ Manager │ │ ││ │ └─────┬─────┘ └───┬────┘ └────┬────┘ └────┬─────┘ │ ││ │ │ │ │ │ │ ││ │ ┌─────┴───────────┴───────────┴───────────┴─────┐ │ ││ │ │ Sync Engine (Pro) │ │ ││ │ └────────────────────┬───────────────────────────┘ │ ││ └───────────────────────┼──────────────────────────────┘ ││ │ │├──────────────────────────┼───────────────────────────────────┤│ ┌───────────────────────┴───────────────────────────────┐ ││ │ Data Layer │ ││ │ ┌──────────────┐ ┌───────────┐ ┌────────────────┐ │ ││ │ │ CoreData │ │ CloudKit │ │ Keychain │ │ ││ │ │ (Local) │ │ (Sync) │ │ (Secrets) │ │ ││ │ └──────────────┘ └───────────┘ └────────────────┘ │ ││ └───────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘Data Flow
Section titled “Data Flow”┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐│ Clipboard │───▶│ ClipboardMonitor │───▶│ StorageManager ││ (System) │ │ - Poll/Observe │ │ - CoreData │└──────────────┘ │ - Parse types │ │ - Index │ └──────────────────┘ └────────┬────────┘ │ ▼┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐│ User │◀───│ UI Components │◀───│ SearchEngine ││ Interface │ │ - SwiftUI │ │ - Full-text │└──────────────┘ │ - View Models │ │ - Semantic │ └──────────────────┘ └─────────────────┘Key Patterns
Section titled “Key Patterns”- MVVM Architecture: Views bind to ViewModels
- Dependency Injection: For testability
- Protocol-Oriented Design: Interfaces over implementations
- Async/Await: Modern concurrency
- Combine: Reactive streams for data flow
See Architecture Documentation for details.
Coding Standards
Section titled “Coding Standards”Swift Style Guide
Section titled “Swift Style Guide”We follow the Swift API Design Guidelines with these additions:
Naming Conventions
Section titled “Naming Conventions”// Types: UpperCamelCasestruct ClipboardItem { }class SearchEngine { }enum ContentType { }
// Properties/Methods: lowerCamelCaselet clipboardHistory: [ClipboardItem]func searchItems(query: String) -> [ClipboardItem]
// Constants: lowerCamelCaselet maximumHistoryCount = 1000
// Abbreviations: treat as wordslet urlString: String // not: uRLStringlet httpResponse: HTTPResponse // type abbreviations OKCode Organization
Section titled “Code Organization”// MARK: - Protocol conformance order// 1. Properties// 2. Initialization// 3. Public methods// 4. Private methods// 5. Protocol conformance (separate extensions)
final class ClipboardManager { // MARK: - Properties private let storage: StorageProtocol private let monitor: ClipboardMonitorProtocol
// MARK: - Initialization init(storage: StorageProtocol, monitor: ClipboardMonitorProtocol) { self.storage = storage self.monitor = monitor }
// MARK: - Public Methods func startMonitoring() { // ... }
// MARK: - Private Methods private func processItem(_ item: ClipboardItem) { // ... }}
// MARK: - ClipboardMonitorDelegateextension ClipboardManager: ClipboardMonitorDelegate { func didCaptureItem(_ item: ClipboardItem) { // ... }}SwiftLint Rules
Section titled “SwiftLint Rules”Key rules from .swiftlint.yml:
line_length: warning: 120 error: 150
type_body_length: warning: 300 error: 400
function_body_length: warning: 40 error: 60
nesting: type_level: 2 function_level: 3Run before committing:
swiftlintswiftformat .Building and Running
Section titled “Building and Running”Debug Build
Section titled “Debug Build”# Using xcodebuildxcodebuild -scheme PasteShelf -configuration Debug build
# Or use Xcode: ⌘RRelease Build
Section titled “Release Build”xcodebuild -scheme PasteShelf -configuration Release buildBuild for Testing
Section titled “Build for Testing”xcodebuild -scheme PasteShelf -configuration Debug \ -destination 'platform=macOS' \ build-for-testingClean Build
Section titled “Clean Build”# Clean build folderxcodebuild clean -scheme PasteShelf
# Or: ⌘⇧K in XcodeTesting
Section titled “Testing”Unit Tests
Section titled “Unit Tests”# Run all unit testsxcodebuild test -scheme PasteShelf -destination 'platform=macOS'
# Or: ⌘U in XcodeExample test structure:
import XCTest@testable import PasteShelf
final class SearchEngineTests: XCTestCase { var sut: SearchEngine! var mockStorage: MockStorageManager!
override func setUp() { super.setUp() mockStorage = MockStorageManager() sut = SearchEngine(storage: mockStorage) }
override func tearDown() { sut = nil mockStorage = nil super.tearDown() }
func test_search_withEmptyQuery_returnsAllItems() { // Given mockStorage.items = [ ClipboardItem(content: "Hello"), ClipboardItem(content: "World") ]
// When let results = sut.search(query: "")
// Then XCTAssertEqual(results.count, 2) }
func test_search_withQuery_returnsMatchingItems() { // Given mockStorage.items = [ ClipboardItem(content: "Hello World"), ClipboardItem(content: "Goodbye Moon") ]
// When let results = sut.search(query: "Hello")
// Then XCTAssertEqual(results.count, 1) XCTAssertEqual(results.first?.content, "Hello World") }}UI Tests
Section titled “UI Tests”import XCTest
final class PasteShelfUITests: XCTestCase { var app: XCUIApplication!
override func setUp() { super.setUp() continueAfterFailure = false app = XCUIApplication() app.launch() }
func test_openingApp_showsMainWindow() { XCTAssertTrue(app.windows["PasteShelf"].exists) }
func test_globalHotkey_opensFloatingPanel() { // Simulate ⌘⇧V app.typeKey("v", modifierFlags: [.command, .shift])
XCTAssertTrue(app.windows["FloatingPanel"].waitForExistence(timeout: 2)) }}Code Coverage
Section titled “Code Coverage”Target: 70% minimum coverage
View coverage in Xcode:
Product → Scheme → Edit Scheme → Test → Options → Code Coverage ✓Debugging
Section titled “Debugging”Console Logging
Section titled “Console Logging”import os
private let logger = Logger(subsystem: "com.pasteshelf.PasteShelf", category: "Clipboard")
func captureItem() { logger.debug("Starting clipboard capture") logger.info("Captured item: \(item.id)") logger.error("Failed to save: \(error.localizedDescription)")}Breakpoints
Section titled “Breakpoints”Common breakpoints to set:
ClipboardMonitor.captureItem()- Debug capture issuesSearchEngine.search()- Debug search resultsStorageManager.save()- Debug persistence
Instruments
Section titled “Instruments”Profile with Instruments (⌘I):
- Time Profiler: CPU usage
- Allocations: Memory leaks
- Core Data: Database performance
- Network: CloudKit sync
Feature Development
Section titled “Feature Development”Application Settings
Section titled “Application Settings”User preferences are managed via @AppStorage:
class AppSettings: ObservableObject { static let shared = AppSettings()
@AppStorage("cloudSyncEnabled") var isCloudSyncEnabled = false @AppStorage("maxHistoryItems") var maxHistoryItems = 1000 @AppStorage("autoDeleteAfterDays") var autoDeleteAfterDays = 30}Adding a New Feature
Section titled “Adding a New Feature”-
Create feature branch
Terminal window git checkout -b feature/smart-folders -
Implement the feature
- Add Core logic in
Core/ - Add UI in
UI/ - Add tests
- Add Core logic in
-
Check settings if needed
if AppSettings.shared.isSmartFoldersEnabled {// Show smart folders UI} -
Update documentation
- Add to relevant docs
- Update CHANGELOG.md
-
Submit PR
- Follow PR template
- Request review
Pull Request Process
Section titled “Pull Request Process”Before Submitting
Section titled “Before Submitting”# 1. Ensure code compilesxcodebuild -scheme PasteShelf build
# 2. Run linterswiftlint
# 3. Format codeswiftformat .
# 4. Run testsxcodebuild test -scheme PasteShelf -destination 'platform=macOS'
# 5. Commit changesgit add .git commit -m "feat(clipboard): add smart folders support"PR Requirements
Section titled “PR Requirements”- Code builds without warnings
- All tests pass
- Code coverage maintained (≥70%)
- SwiftLint passes
- Documentation updated
- CHANGELOG.md updated
- Follows commit message convention
Review Process
Section titled “Review Process”- Create PR from feature branch to
develop - CI runs automatically
- Request review from maintainers
- Address feedback
- Maintainer approves and merges
Related Documentation
Section titled “Related Documentation”| Document | Description |
|---|---|
| Contributing Guide | Contribution guidelines |
| Architecture | System architecture |
| Testing Guide | Testing best practices |
| CI/CD | Continuous integration |
Last updated: 2026-02-03