A cross-platform Spotify playlist management app built with SwiftUI and SwiftData.
⚠️ Disclaimer: This app modifies your Spotify playlists. While safety features are built in (undo system, verification checks, empty-data protection), always back up important playlists before performing bulk operations. The authors are not responsible for any data loss. Use at your own risk. See LICENSE for full terms.
| Platform | Minimum Version |
|---|---|
| macOS | 26.0+ |
| iOS | 26.0+ |
| iPadOS | 26.0+ |
- OAuth 2.0 Authentication — Authorization Code Flow with PKCE-ready implementation
- Secure Token Storage — Keychain-backed credential management
- Rate Limiting — Exponential backoff with automatic retry
- Network Monitoring — Connectivity-aware operations
- Full CRUD Operations — Create, edit, delete, and shuffle playlists
- Drag & Drop Reordering — Reorder tracks within editable playlists
- Bulk Operations — Select and modify multiple tracks at once
- Playlist Folders — Local organization (Spotify API doesn't support folders)
- Like/Unlike — Manage your Spotify library
- Delete with Undo — Recover from accidental deletions
- Track Inspector — View detailed metadata
- Swipe Actions — Quick actions on iOS
- Cross-Playlist Search — Find tracks across all playlists
- Add New Tracks — Search Spotify catalog and add to playlists
- Duplicate Detection — Find and manage duplicate tracks
- Local Caching — SwiftData-backed cache reduces API calls by ~80%
- CSV Export — Export playlist data using TabularData framework
- CSV/URL Import — Import tracks from files or Spotify URLs
- Analytics — Playlist statistics and insights
Timor includes multiple safeguards to protect your data:
- Undo/Redo System — Recover from accidental deletions
- Post-Save Verification — Validates operations after completion
- Empty Data Protection — Never overwrites cache with empty track lists
- Track Count Validation — Warns when local data diverges from Spotify
- Atomic Operations — Operation IDs prevent race conditions
- macOS 26.0+ / iOS 26.0+
- Xcode 26.0+
- Spotify account
- Spotify Developer App credentials
None — Timor uses only Apple frameworks:
- SwiftUI
- SwiftData
- Combine
- TabularData
- Network
- AuthenticationServices
- UniformTypeIdentifiers
- os.log
- Go to Spotify Developer Dashboard
- Click "Create App"
- Fill in app details:
- App name: Timor (or your preference)
- Redirect URI:
timor://spotify-callback
- Save and note your Client ID and Client Secret
- Open Timor
- Go to Settings:
- macOS: ⌘, (Command + Comma)
- iOS: Tap the gear icon
- Enter your Client ID and Client Secret
- Tap "Connect to Spotify"
- Authorize in your browser
git clone https://github.com/welshofer/Timor.git
cd Timor# Open in Xcode
open Timor.xcodeproj
# Or build from command line
xcodebuild -project Timor.xcodeproj -scheme Timor -configuration Release build
# Build iOS target
xcodebuild -project Timor.xcodeproj -scheme "Timor iOS" -configuration Release -destination 'generic/platform=iOS' buildxcodebuild -project Timor.xcodeproj -scheme Timor -destination 'platform=macOS' testTimor/
├── TimorApp.swift # App entry point, SwiftData container
├── ContentView.swift # Main NavigationSplitView coordinator
├── Constants.swift # Centralized configuration
│
├── Spotify Integration
│ ├── SpotifyManager.swift # Central state manager (~1,500 lines)
│ └── SpotifyWebAPI.swift # OAuth, API calls, rate limiting (~1,400 lines)
│
├── Data Models
│ ├── CachedPlaylist.swift # SwiftData model for caching
│ └── PlaylistFolder.swift # Local playlist organization
│
├── Views
│ ├── PlaylistSidebarView.swift
│ ├── PlaylistDetailView.swift
│ ├── TrackTableView.swift # macOS track table
│ ├── TrackListView.swift # iOS track list
│ ├── TrackInspectorView.swift
│ ├── TrackFilterView.swift
│ ├── TrackSearchView.swift
│ ├── DuplicateFinderView.swift
│ ├── ImportView.swift
│ ├── PlaylistStatsView.swift
│ └── SettingsView.swift
│
├── Utilities
│ ├── KeychainManager.swift
│ ├── ImageCache.swift
│ └── PlaylistUndoManager.swift
│
└── TimorTests/
├── RateLimiterTests.swift
├── PlaylistUndoManagerTests.swift
└── SpotifyErrorTests.swift
- SwiftUI — Declarative UI with platform-specific adaptations
- SwiftData — Modern persistence for local caching
- Combine — Reactive state management
- async/await — Structured concurrency for API operations
- @MainActor — Thread-safe UI updates
- Singleton SpotifyManager — Single source of truth
- Atomic Load Operations — UUID-based race condition prevention
- Rate Limiting Actor — Exponential backoff with jitter
- Platform Conditionals —
#if os(macOS)/#if os(iOS)for native experiences
Comprehensive technical documentation is available in the docs/ folder:
| Document | Description |
|---|---|
| Architecture | System overview, component diagrams, threading model |
| Data Models | SwiftData schemas, runtime types, relationships |
| OAuth Flow | Authentication sequence, token management |
| API Reference | Complete API documentation for all public types |
| State Management | Observable patterns, view bindings, state flow |
| Caching | Cache strategy, invalidation, performance |
| Security | Credential storage, certificate pinning, threat model |
MIT License — See LICENSE file for details.
Contributions welcome! Please see CONTRIBUTING.md for guidelines.
- Clean DerivedData:
rm -rf ~/Library/Developer/Xcode/DerivedData - Reset package cache:
xcodebuild -resolvePackageDependencies
- Verify redirect URI exactly matches:
timor://spotify-callback - Check Spotify app settings at developer.spotify.com
- Clear Keychain items (Service: "com.timor.spotify")
- Reset SwiftData: Delete app container and restart