Personal Project

MusicCount

Match your play counts before cleaning up duplicates.

Year

2025

Technologies

Swift 6.1, SwiftUI, MediaPlayer, Swift Concurrency

Source

GitHub

Live Site

Visit

The Product

I built this because my own library was a mess. Years of re-importing albums and switching between Spotify and Apple Music left me with duplicate songs everywhere, each with different play counts. It was also my first Swift app. I wanted to learn iOS development properly, and building something I'd actually use seemed like the best way to do it.

MusicCount scans your library, groups duplicates by normalised title and artist, and lets you pick the version you want to keep. The app adds it to your queue enough times to match the play count of the duplicate - once played, you can delete the duplicate yourself (Apple Music's API doesn't allow apps to delete songs). Your listening history stays intact on the song that matters.

Library view sorted by play count on first load

Features

Intelligent Duplicate Detection

Your most-played song split across three versions? String normalisation catches duplicates even when titles don't match exactly, grouping by both title and artist to avoid false positives.

Groups with biggest play count gaps float to top. Sort by play count, title, artist, album, or version count. Real-time search. Dismiss songs or entire groups, persisted between launches.

Suggestions tab showing duplicate groups sorted by play count difference
Comparison view

Interactive Play Count Comparison

Side-by-side view with blurred album artwork. Tap the version to keep. Match Mode queues it enough to match the duplicate's play count, Add Mode adds the duplicate's total. Insert Next or Replace Queue. Once played, delete the duplicate with your play history intact.


How It's Built

Architecture Decisions

Native State Management: iOS 17's observation system makes ViewModels optional. Views stay thin, business logic lives in services shared via dependency injection.

Modular Package Architecture: Features live in a Swift Package. The app target is a thin wrapper. Tests sit alongside feature code.

Background Thread Scanning: Library queries run off the main thread so the UI stays responsive during initial load.


Security & Privacy

No data collection, no network calls, no external dependencies.


Performance

1.7MB app size. Background queries keep large libraries responsive.


Accessibility

VoiceOver labels and hints throughout. Tab bar badges show suggestion count. Comparison cards grouped for screen readers. Dynamic Type support.


Testing

Swift Testing framework with coverage across grouping logic, dismissal persistence, queue behaviour, model validation, and all seven sort algorithms. Mock service with deterministic data for tests and UI development.


Tech Stack

UI: SwiftUI (iOS 17.0+)

Language: Swift 6.1 with strict concurrency checking

Concurrency: async/await, actors, main thread isolation

State: Native observation system, dependency injection

Testing: Swift Testing framework

Architecture: Modular package structure