Building a Bluesky client in Uno Platform

C# Uno Platform WinUI XAML

12 days ago

This article is part of C# Advent 2024, big thanks to Matthew Groves for organizing it again!

I must admit, I really missed Twitter. With that I mean the actual Twitter not the abomination that is "X". I was therefore rejoyed to see masses of fellow developers signing up for Bluesky. In fact, there is so many users there now, that it really feels right again - lots of interesting posts and actually useful news!

Therefore it felt natural to connect this year's C# Advent blog post with this social network. And because I love Uno Platform, why not combine both and create a very basic cross-platfrom Bluesky client?

Bluesky client running on desktop

Setup

I have started up from my common app template for Uno Platform apps, which contains just some auxiliary helpers which are not actually relevant to the logic of the application itself. It includes:

  • A shell that is the "root" of each app window
  • Integration of CommunityToolkit.Mvvm
  • Scoped Dependency Injection that allows scoping services per application window (to allow for multiwindow apps)
  • Some useful services like IXamlRootProvider (to get access to the XamlRoot associated with the window)
  • Simple NavigationService to navigate between view models

These are generic features, so I will omit discussing them in this post, but for those interested, the whole source code is available on my GitHub

Talking to Bluesky API

Even the simplest of clients need to talk to the Bluesky API. I browsed the existing .NET libraries and found FishyFlip. It is in very active development and has NuGet packages published, so it felt like the ideal choice for my purposes.

Login

There are several ways to authenticate with Bluesky API. The simplest is to ask the user for their handle and an app password. App password can be generated in settings on Bluesky. Then you can pass these to the client's AuthenticateWithPasswordAsync method:

Retrieving timeline

There are many ways you can retrieve and filter posts on Bluesky, but the very simplest is the default timeline feed. FishyFlip provides this via the GetTimelineAsync method:

The method returns an instance of Result<T> which has two possible forms - data (T0) or error (T1) . AsT0 will try to retrieve the data or end up as null. Then the actual posts are in the Feed property, which is a List<FeedViewPost>.

To make things simpler, I created a FeedViewItemViewModel record to hold the most important metadata:

Creating a text post

The very simplest possible post format is plain text. And again, FishyFlip has a pretty simple API for it:

Creating a BlueskyService

With the building blocks above we can put together a simple service that supports authentication, timeline retrieval, and posting.

You might have noticed that I used a ICredentialsService as a dependency. This is to be able to store the user's credentials across application launches. This is not ideal, as ideally we would only store the access tokens, but in such case we would have to go with a more complex OAuth based approach, which would be out of scope of this post. Instead, I am using PasswordVault API in Uno Platform to store the app password in a secured location on each platform.

Note: PasswordVault is not yet supported on desktop targets of Uno Platform, so in those cases I am storing the credentials in plain text in ApplicationData.LocalSettings, which for demonstration purposes only and should not be done in production!

Building the UI

Uno Platform offers quite a wide variety of options for developers to choose from when building their app's presentation layer. One can go with XAML, or venture into the fluent world of C# Markup. One can also start simply with code-behind for logic, or use MVVM, or even the powerful Reactive MVUX design pattern.

To make things easy to follow for new users, I picked XAML and MVVM, but it should be quite simple to rewrite the app to use any combination of the mentioned technologies, as the UI and presentation logic are both very simple.

Uno Platform Hot Design

One of the perks of being a developer on Uno Platform team is the fact that I have early access to the new cool features like Uno Platform Studio Hot Design 😁 .

Hot Design is a one-of-a-kind in-app visual designer which runs on top of Uno Platform's Hot Reload feature and transforms the live application itself into a powerful designer surface. I have used it to design the UI of this sample application and it worked like a charm 🚀 !

If you haven't yet, sign up for the waitlist to try it out yourself!

Here is an example of Hot Design in action, setting up the placeholder of the post input field:

Bluesky client open in Hot Design mode

Login page

The login view of our app will be very simple, just a TextBox, PasswordBox and a login Button:

In the code-behind, we need to implement the PasswordChanged handler to update the view model on changes, as it is not possible to data-bind Password:

The Login view is backed by a simple view model that uses BlueskyService to login. In addition, it also tries to login via the cached credentials on initial navigation, to allow the user to skip the login screen:

Main page

After the user successfully logged in, we want to display the feed itself. I have used a simple ListView to render the posts, and in the bottom added a TextBox input, send Button and a refresh Button:

All of the logic then goes to the MainViewModel which loads the feed on initial navigation and also updates it when the user makes a new post:

App in action

Here is our simple client in all its glory, running as a desktop app:

Bluesky client running on desktop

And because it is a Uno Platform app, it now runs everywhere without changes - for example on Android:

Bluesky client running on desktop

Next steps

Obviously, the resulting client leaves a lot to be desired to make it an actual Bluesky client app, including but not limited to:

  • Incremental loading of posts
  • Viewing reply threads
  • Commenting, liking, and reposting
  • Viewing and editing profile

These APIs are already available via FishyFlip, but I didn't include them in this sample.

The source code of the client is available on my GitHub.

Although this started off just as a C# Advent sample, I really enjoyed working on it and might actually turn this into a full fledged app. Would you want to help? Fork it and make a PR!