Consuming Strava API With Xamarin.Forms

A guide on how to authenticate, download and upload activities between Strava API and a Xamarin.Forms mobile app

Márcio Júnior
Better Programming

--

Photo by Jay Miller on Unsplash

I’ve been working on a mobile app that is a fitness activity tracker and, one of the backlog items of this project is to synchronize with Strava API. There are some .NET projects on GitHub addressing this issue, however, they didn’t fit in my needs. Some of them are outdated, don’t handle the authentication process, or lack of documentation. So I decided to share what I did and help anyone going through this.

Authentication

Authentication is the first thing you need to do in order to allow your users to connect with their Strava account. This is achieved via OAuth 2.0 — a well know standard used to grant access to applications. Let's assume you already have a Strava account with an app configured. I won’t cover this step because there is a lot of material on the internet (check this video for help). The authorization process works this way:

  1. The app opens a browser and navigates to a page where the user is asked to enter his credentials.
  2. The user enters credentials and is asked to authorize the app to access his data on Strava.
  3. On success, the browser is redirected to a page specified by you (callback domain). There will be a URL parameter called code.
  4. The app detects the redirection, gets the code, and the browser is closed.
  5. The app calls the API and exchanges the code to access the token and refreshes the token.

The main issue here is how to open the browser and wait for call back (step 1). Luckily, the class WebAuthenticator from Xamarin.Essentials can initiate this flow and listen for a callback (check this link to set up in projects). Check below how to do this:

string authURL = 
"https://www.strava.com/api/v3/oauth/authorize" +
"?client_id=ReplaceWithClientID" +
"&redirect_uri=myapp://myapp.com" +
"&response_type=code" +
"&approval_prompt=auto" +
"&scope=activity:read_all,activity:write";
WebAuthenticatorResult authResult =
await WebAuthenticator.AuthenticateAsync(
new Uri(authURL),
new Uri("myapp://myapp.com"));

Another important part is the redirect URI. In the Strava account you will set the callback domain like myapp.com, so when the authorization occurs, the browser will head to something like myapp.com?code=xyz (step 3). However, on app, we need to set the redirect URI to myapp://myapp.com, because when configuring WebAuthenticator we’ll use the url scheme myapp (this topic helped me figure out this).

Now you can get the code from authResult.Properties[“code”] and move to last step where you exchange the code to access token. At this point you should be familiar with Postman (or another API tool) and be able to test the following cURL request (check here of how to import it on Postman):

curl -X POST https://www.strava.com/api/v3/oauth/token \
-d client_id=ReplaceWithClientID \
-d client_secret=ReplaceWithClientSecret \
-d code=ReplaceWithCode \
-d grant_type=authorization_code

When successfully completed, the response will contain some attributes which I suggest you save on your application properties or database. They are expires_at, access_token and refresh_token. Now you can use the access_token to request API, but this token will expire soon (after 6 hours according Strava docs). So if it’s still valid, use it. If not, use the refresh_token to get a new one.

Refreshing Expired Token

Ok, you got the code, exchanged it for token and now the token is not valid anymore! Seems complicated if it’s your first time doing this, but once you have a token, it's just a matter of knowing if it’s valid or not. And if it’s not, you must use refresh token asking for a new one:

if (tokenExpiresAt > DateTime.Now)
return savedToken;
else
// Ask for a new access_token

Every time you need to make a call to Strava API, you should check if the token is valid, so I recommend creating a method that handles this. Once you get a new token, it will come along with another refresh token and an expiring date. Then override the old ones on your application storage.

Downloading Activities

Strava API has an endpoint to list athlete activities that accept a parameter “after” to filter activities that have taken place after a certain time:

"/athlete/activities?after=" + stravaSyncDate

Having downloaded the activities, just update the variable to the actual date/time. Thus, next time you’ll get only new activities after last sync.

You may also want to download other activity data like heart rate or distance. This kind of data is available in endpoint Streams. This endpoint accepts a query parameter keys, in which you inform desired stream types. In my case, I needed heart rate and time, so I made a request with keys=time, heartrate.

Each stream contains the sequence of values and an attribute series_type, that is the base series used in the case the stream was downsampled (can be distance or time). For heart rate, the series type is time, so the sequences match with each other:

{
"heartrate": {
"data": [ 74, 75, 76 ],
"series_type": "time"
},
"time": {
"data": [ 0, 5, 10 ],
"series_type": "time"
}
}

Uploading Activities

You may think that to upload an activity, just make POST calls to the endpoints above. Although the activity endpoint allows this operation, it’s not possible to upload streams. But it’s possible to post a data file to endpoint /uploads.

They accept some formats and I ended up choosing the GPX which is basically an XML file. I know there are XML libraries over there, but in this case the structure is very simple and you can easily create one concatenating some string chunks (check my suggestion on StackOverflow).

Finishing

In this article, I explained how to authenticate, download and upload activities to Strava using Xamarin.Forms mobile app. I tried to keep it succinctly showing just the essential source code, but check out my GitHub repository for a full version of it. Feel free to reach me in case of any doubts.

Full example on GitHub

--

--

I’m a full-stack developer. I work thinking and creating solutions through clean and readable code. I like to share knowledge and help people.