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
Future
s.
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).
- Racket: cs,build: add pbchunk object files to libracketcs.a
- Chez Scheme: replace call to iconv procedure with ICONV macro
- Chez Scheme: add {,t}{a6,arm64}ios machine types
- Racket: cs,iOS: add iOS-specific machine type and make OpenSSL work
- Racket: xml,reader: improve performance
- Racket/http-easy: session: improve handling of redirect locations wrt percent encoding
- Racket/sentry: add tracing
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.