I designed and developed IOS and Android app for UC Davis' freeform college radio station KDVS. This is a detailed walk through of my development process and documentation of the technical aspects of this project.
The project was incepted in Febuary of 2023 when I heard the station was looking for somebody to make new website and app. I had minimal experience in Swift development going into this and a little Android development experience. However I was looking for a project to walk through and develop my Swift skills so I immediately jumped on the opportunity and started drafting up ideas for features.
My plan was to start out by creating a simple app with the focus being on being able to play the station's Icecast web-stream, and showing basic show information. I was hoping to be able to access the show information via the Library site API which is how the KDVS website access show info. However the API is locked behind a firewall maintained by the IT department. To bypass this I resorted to using SwiftSoup to web-scrape this information from the current kdvs.org site.
Rough Sketches of envisioned features
After listing out the essential features of this first version of the app I drafted up a mid-fidelity layout of the app on Figma. The V1 UI was loosely based on the default IOS 16 lock screen music player which emphasized the album cover to draw visual interest to the other show information. However I found this version of the app wasn't very unique so I settled on an MP3 player concept which is the V1.1 you see in the slideshow. The look is inspired by yuhang lu's Voice Recorder concept. The show time was also scraped off the home page and displayed on top of the playlist image so users knew what time this show was playing until. I also pulled the 1990 KDVS Logo from archive.org and made it so 1/5 of the app launches displayed this alternate header.
The programming of this base model was straightforward, I had a barebones Show struct which was built from a kdvs.org web-scraping function. This caused some less than ideal buffer time which required me to create a "loading" view shown to the left.
In these next iterations of the app I wanted to solve 3 big problems that DJ's and listeners alike were facing:
The development process of these features was much more technical than the base model of the app I had created earlier and I spent easily more than 2 months as a new developer learning how to implement all of these features.
I created a simple Remind View, which used "Today's" date, the scraped showtime, and a hard coded array of Season start/end dates to let users turn on notifications for the currently playing show. When notifications are turned on I used UNUserNotificationCenter to schedule a notification once a week. I then saved the show struct into a Core Data JSON file, which is pulled every time the remindView is opened. When a show's notifications are turned off, it is removed from the JSON file and all app notifications are rebuilt with the new list of notification-on shows.
This implementation was less than ideal as around half of all KDVS shows are alternating (meaning they aren't every week), and you could only add and remove shows as they were live which made notification management very inconvenient. I tackled many of these problems in the updated Remind View Version below.
Next I wanted to solve a problem I had as a DJ, knowing how many people are listening to my show. This was simple to implement but required me to set up a separate server to display this listener count. At first I approached this counter by setting up a Firebase server which had a single variable in it's database, which was incremented every time a user connected to it, and decremented which a user disconnected. However there was one big problem, whenever a user's app crashed or ineloquently disconnected, the app wouldn't properly decrement the user counter. So instead I made a Glitch NodeJS server which used Socket.IO to track how many app instances were connected to the server. And on the app end I connected to the server every time I played the livestream and disconnected whenever the pause button was pressed. This implementation worked perfectly and instantaneously updated whenever a user played. I then designed and created a barebones front end site to display the listener count for DJ's to access as they spin their records.
Finally from input from one of my friends (a fellow KDVS DJ) I decided to tackle the task of programmatically identifying live songs on air. This small view that hides at the bottom of the "Now Playing" sheet was by far the most complicated and time consuming feature to implement on this app. It was all made possible by the ShazamKit API. The working version of this app works by recording the KDVS web-stream by playing it with AVPlayer just as I do for the regular player. However this doesn't play audibly and uses a custom extension of AVPlayer called CachingPlayerItem, which caches the raw audio data as it's being played and saves it as an AAC file on the app. There's one catch though as when you record a livestream you interrupt the basic structure of this audio file which is a header (containing important formatting information) then audio data. As a result the saved audio file is corrupted as it doesn't start with a header. To solve this I created a function that removes all excess data before the next available header. From there I convert to a mono audio .wav file which is what ShazamKIT reads in, converts to its "song fingerprint" (a time-frequency graph AKA a spectrogram) and sends back a song, if it can recognize it. The most painstaking part of this process by far was debugging the corrupted header which took me a month to figure out due to my limited knowledge of audio file formatting. However I do feel much more confident working with ShazamKit and think this feature is worth the time I put into it.
I decided to tackle the last big feature I had originally drafted in my idea for the app. I avoided this because it required a LOT of web scraping which added a lot off buffering to load time on the app. Nonetheless I decided it was an essential feature. I created 2 new web-scraping functions. One which scraped the kdvs.org/programming/schedule-grid/ site to build the current season's schedule, and another function which read in each showpage (e.g.). The schedule is built whenever the user opens the app, which adds around 5 seconds to the loading time. Access to the schedule grid and each show's information allowed me to access extra show information such as accurate show dates even for alternating shows, genre information and playlist information.
Demo: Link
Figma: Link
Github: Link
IOS Download: Link
Android Download: Link