A Home Screen Widget for Relay FM's Annual St. Jude Fundraiser
Every year, Relay FM and its surrounding community of tech nerds and podcast listeners rally together to raise money for St. Jude Children’s Research Hospital — one of the premier pediatric cancer research institutions in the world. The fundraiser is back and better than ever with sticker rewards for certain donors, donation thresholds for Flight Simulator streams, a horrifying amount of stickers, and the annual Podcastathon live stream on September 17th. Relay hopes to raise $333,333.33 (though I have a feeling that goal will be raised, since they racked up $456,000 last year to bring their cumulative total to well-over $1,000,000. I’m always delighted to donate to this cause because St. Jude does such amazing work, and this year I also worked out a way to track their progress live right on my iPhone home screen.
Relay runs their St. Jude fundraiser using Tiltify — a service that focuses entirely on campaigns like this, and importantly has a public-facing API. Because of that, it’s fairly easy to pull updates from Relay’s donation page using a POST request and parsing the (rather large) JSON dictionary that returns. An intrepid member of the Relay FM members Discord discovered this fact and created a Siri Shortcut to quickly display the total amount raised…but that gave me a better idea: what about a home screen widget?
When you need to call an API and create a dynamic home screen widget, two apps come to mind: Scriptable and Pyto — given that my only serviceable programming know-how is in Python, I chose the latter and got to work cobbling together a script.
Displaying Campaign Data, Dr. Drang-Style
The first thing I needed to do was connect to the Tiltify campaign with a POST request — big thanks to Ben in the Relay Discord for doing most of the heavy lifting with his Siri Shortcut! Tiltify expects a nightmarishly-long JSON payload in a POST request, but the JSON data it returns is much more straight-forward. Using Pyto, I called the API and parsed the JSON key:value pairs in the returned data to snag the totalAmountRaised
and goal
values from Relay’s 2021 St. Jude campaign. A little conversion turns those raw floats into properly-formatted dollar values with thousands-commas and dollar signs.
#Pull data from the returned JSON payload.
info = (r.json())
rawraised = info["data"]["campaign"]["totalAmountRaised"]["value"]
rawgoal = info["data"]["campaign"]["goal"]["value"]
# Convert pulled values into properly-formatted dollar values:
raised = "$" + '{0:,.2f}'.format(float(rawraised))
goal = "$" + '{0:,.2f}'.format(float(rawgoal))
Once I was able to pull the fundraiser's goal and the current amount raised, the next challenge was displaying that information within an iOS home screen widget. First and foremost, I really wanted a dynamic progress bar but wanted to generate one as simply as possible. My brother (a much more experienced programmer) helped me write this clever snippet of code to simply display a string of emojis 12 characters long, with two emojis (⬜ and 🟩) proportionally placed to form the progress bar. Here's what the code looks like:
# Generate a progress bar using emojis
def progressbar(raised, goal):
# Modify these emoji to change the "empty" and "full" sections of the progress bar.
progress_full = "🟩"
progress_empty = "⬜"
bar_length = 12 # Modify this number to adjust the max width of the progress bar.
progress_percent = float(raised)/float(goal)
progress = ""
progress = progress_full * int(bar_length * progress_percent)
return '{:⬜<{x}}'.format(progress, x=bar_length)
#If you modify progress_empty above, you need to put it in this return statement as well.
The length of the progress bar needs to be fiddled with based on the width of the device; I'm sure there are much more clever ways to generate the proper number of emojis based on the device the code is being run on, but that was above my pay grade! Twelve emojis wide was just about perfect for a medium widget on my iPhone XS — your mileage may vary based on your device.
All that's left is to actually generate the widget to display all of this data on my home screen. I was expecting this to be much more challenging, since I had never used Pyto or its widget
library, but luckily when you open a new script Pyto offers to automatically populate an example widget! A bit of hacking around with the example code and some digging in their widget documentation let me insert the variables I was interested in, change the background color, and remove the option of a small widget (it's just too small). In short order, I could present the amount raised, the fundraiser goal, the percent progress, and a delightful emoji progress bar right on my home screen! Here's the code:
if wd.link is None:
widget = wd.Widget()
wd.wait_for_internet_connection()
background = wd.Color.rgb(219.7/255, 182.8/255, 72.2/255)
#You can modify the background color by altering the RGB values to your liking
# Populate four rows of data, and accompanying font sizes:
text1 = wd.Text("Raised: " + raised)
text1.font = wd.Font.bold_system_font_of_size(20)
text2 = wd.Text("Goal: " + goal)
text2.font = wd.Font.bold_system_font_of_size(20)
text3 = wd.Text("Progress: " + progress)
text3.font = wd.Font.bold_system_font_of_size(20)
text4 = wd.Text(bar) #Progress bar
text4.font = wd.Font.bold_system_font_of_size(18)
# Supported layouts (the small widget is too small)
layouts = [widget.medium_layout, widget.large_layout]
for layout in layouts:
layout.add_row([text1])
layout.add_row([text2])
layout.add_row([text3])
layout.add_row([wd.Spacer()])
layout.add_row([text4])
layout.set_background_color(background)
layout.set_link("https://stjude.org/relay")
wd.schedule_next_reload(900) # Suggested refresh time in seconds
wd.show_widget(widget)
else:
open(wd.link) #This opens the link above when the widget is tapped.
And here's what the resulting widget looks like:
I think it looks pretty nice, given that I spent almost zero time optimizing the padding, font size, and background color in the Pyto code. Their widget documentation has many more UI customization options that intrepid readers can explore to make more advanced or aesthetically appealing widgets for themselves, but I'm pretty happy with where this simple version landed. As a nice bonus, Pyto also has a way to alter what tapping on the widget does — you'll notice that the entire widget generation code is wrapped in an IF-ELSE
statement; tapping the widget sets wd.link
to TRUE and instead runs the open(wd.link)
function...which naturally directs to Relay's St. Jude Fundraiser page (if you've read this far and haven't donated yet, now's the time!). I think that having to bounce back into Pyto to make this happen is a bit clunky, but beggars can't be choosers. The widget also seems to refresh relatively often — Apple says that widgets dynamically refresh somewhere between 15-70 minutes depending on user behavior, which is more than enough to keep the raised amount relatively up-to-date.
I Don't Want to Read Your Code, I Just Want the Widget!
Fair enough, let's actually walk through the steps for you, dear reader, to get this widget on your iPhone:
- Download Pyto from the app store. Pyto has a 3-day free trial, but you'll need to pay the $2.99 in-app purchase to run this script beyond then.
- Download this python script from my GitHub page. It handles all of the steps I discussed above: Pulling the fundraiser data, generating a progress bar, and creating the widget using Pyto. Save it in the Files app so you can run it using Pyto (please don't judge my horrendous code too harshly, I'm still new to programming!) If you're feeling adventurous, you can also modify the font size and color, background color, and even what text is displayed — just dive into the code!
- Run the script once using Pyto
- Edit your home screen, and add the Run Script widget from Pyto (Medium or Large, your choice). Configure that widget to run the
StJudeRelay2021.py
script you just saved in Pyto
And just like that, you'll be able to monitor Relay's annual St. Jude fundraiser from your home screen. If you still haven't donated, you absolutely should click here now and do so — there's no better cause than St. Jude, and the Relay folks' partnership with them is exceptionally wholesome. And after you donate, you'll want to mark your calendar for the culmination of the campaign: the Podcastathon on September 17th over on Relay's Twitch channel. It's always a wild and wacky event full of fun shenanigans, all for a spectacular cause.