I Made a Podcast Player

I made a podcast player for iOS. It's called Podcatcher and it's live on the iOS App Store! As with most things I've made in the past several years, it's implemented using a combination of Racket and Swift. This approach continues to be a very fun and productive way for me to write apps, and working on these projects has allowed me to refine it further. For example, I've recently added async support to Noise and made it compliant with Swift Concurrency while maintaining backwards-compatibility with the existing implementation based on Futures.

Why a podcast player? Because I listen to podcasts a lot (like a lot, a lot, a hundred hours a month a lot) and the podcast player I used to use recently made some changes I didn't like, so I decided to make an app suited to my liking and listening style. As you might expect from a modern podcast player, it's got audio enhancements like an equalizer that boosts voices, silence trimming, variable speed with pitch adjustment, and a "shower mode" that boosts voices further, trading quality for loudness when you need it. It's also free, has no ads or tracking, and it's local-first except for the podcast directory and sync. There is a backend component that ingests podcast feeds periodically and sends push notifications to the app to update its local database when new episodes get released.

Compared to some of the other apps, cross-device sync via iCloud is maybe the main thing that is currently missing, and the UI is less fancy/custom, but that's what I prefer anyway. In a future release, I plan to add a sleep timer, support for favorites & bookmarks and a watch companion app. I'm also open to suggestions, so let me know what you think if you give it a try!

On the Racket and OSS side of things, the following changes directly resulted from my work on the app, either because they were needed to make the project work at all (i.e. some of the build changes, and the work to support OpenSSL) or because working on the app exposed certain problems (i.e. the performance issue with the XML library, and the redirect issue in http-easy).

Plus, the changes to Noise I mentioned earlier. I am still debating whether or not I want to make the app itself source available, like I've done for Franz and Remember. Maybe if there's interest.

In terms of lines of code per language, the app looks like this:

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Swift                           68            621             25           6392
Racket                          73            590             42           4965
-------------------------------------------------------------------------------
SUM:                           141           1211             67          11357
-------------------------------------------------------------------------------

The Racket numbers include the API component, which is about 1800 lines of code on its own, but the Racket code is a lot more dense than the Swift code, which has only one external library dependency, compared to the Racket core which has several:

  • actor-lib
  • binfmt-lib
  • buid-lib
  • db-lib
  • define-query-lib
  • deta-lib
  • html-lib
  • http-easy-lib
  • monocle-lib
  • noise-serde-lib
  • north
  • sentry-lib
  • struct-define
  • threading-lib

The Swift part also includes about 1.5k lines of code representing structs and ser/de code generated by Noise.