As our user base at Peloton continues to grow we’ve found some interesting opportunities to connect members to each other. We think that creating more opportunities for shared, real-time workout experiences is an important initiative and something we wanted to invest in. The first large feature based around this goal was something we called Working Out Now, which involved letting members know in real-time that another member they had followed on our platform was currently working out. To support this, we wanted a mechanism that could easily notify members anywhere in the product. We wanted it to be minimally invasive while still providing detailed information when needed.
Real-Time Messaging Integration
We were already using PubNub to send real-time updates for our user activity feed, which shows members their friends’ activity. We decided to continue using PubNub for our real-time messaging and simply expand on what we had in place.
For each member we create a PubNub channel that their device subscribes to upon login. Whenever we need to send a real-time update to that member, we publish a message on that member’s channel. Each message has a type which we use to identify the schema of the payload. This allows us to easily create and consume new messages types.
On the client side, we encapsulate the ability to connect to PubNub and receive messages in our Messaging Service, which can easily be injected throughout the app for use. This Service exposes a Rx Observable that supports filtering the message stream by type. This gives clients a simple way to only get messages they care about and know how to consume.
Know When Your Friends Are Working Out
In order to know which of a member’s friends are currently working out, we need to know every time one of those members joins or leaves a workout. We defined two types of messages: “Someone you follow joined a workout” and “Someone you follow left a workout”. Whenever a member joins or leaves a workout, the Peloton API publishes a message to the PubNub channel of each of the member’s followers.
After defining these message, we hooked up the client to receive them in our bottom navigation component. We decided to build a cache that would update every time we received one of these messages from the server and then request additional information as needed. This worked well while a member was on their Bike or Tread, but we still needed a way to know who was in a workout when the member first started their session.
We created a new API endpoint that allowed us retrieve all the data for a member’s followers that we could leverage when our cache was stale or needed to be initialized. There was also a risk that the client’s local list of friends who were working out got out of date for a number of reasons. Instead of the client incrementally updating their local list, they request the full list of friends in classes each time they receive a message. Once the client has this information we use it render our Working Out Now dialog that members see when clicking on a notification.
As we added more types of messages that were used by different parts of the app, it became more important to document the schema of these messages. We use an OpenAPI specification to document the schema of our REST API and applied this same pattern to real-time messages by treating them like endpoints. This allowed for all client teams to easily consume any new messages that were added and understand how they could be used.
After allowing members to see which classes their friends were in, we built on top of this by allowing them to see their friends while they were in any class, including On Demand classes. We were then able to work with our Leaderboard team to provide an experience that better connected members to friends in class as well. This real-time messaging infrastructure gives us a base to expand upon for new features in the future.