Get a Free Estimate!

6 min read

Category: Development

21 Nov 2018

21 Nov 2018

6 min read / Category: Development

Using SignalR to create real-time mobile app

Łukasz Lech & Jakub Tomalski

Share

Facebook Twitter LinkedIn

Regular REST API is not enough to create a chat or a multiplayer game. In order to do that, we need to be notified every time the server receives new data. SignalR comes to the rescue!

Let's imagine an app helping friends locate themselves. Every minute the app collects the current user location and sends it to the server, which resends this location to other connected users. In this blog post, we will show you how to setup SingalR on both server-side and mobile app.

What is SignalR?

SignalR is a .NET library to create real-time applications. Opposite to regular HTTP communication, SignalR allows pushing messages from server-side to connected clients without any previous request. Thanks to bi-directional communication the mobile app does not have to ask the server to be up to date with the server content.


SignalR frees developer of selecting transport type, by selecting the best available transport given by client and server side. Websockets are not supported by the server? No problem, let's talk with Long Pooling, or Server Sent Events. SignalR provides an API that makes selected transport transparent.

Setup SignalR server

All SignalR server-side code is contained in a Hub which contains methods that can be called similarly to MVC Controllers. To be able to use those fully one need to register it in Startup.Configure as follows:

app.UseSignalR(route =>
{
    route.MapHub<SomeFancyNameHub>("/FancyHub");
});


Due to inheritance from SignalR Hub SomeFancyNameHub: Hub you already have access to Context, Clients and Groups properties.

 public abstract class Hub : IDisposable
    {
        protected Hub();

        public IHubCallerClients Clients { get; set; }
        public HubCallerContext Context { get; set; }
        public IGroupManager Groups { get; set; }

        public void Dispose();
        public virtual Task OnConnectedAsync();
        public virtual Task OnDisconnectedAsync(Exception exception);
        protected virtual void Dispose(bool disposing);
    }


This means for example that by overriding OnConnectedAsync() method for each connected client you can get it's connectionId from Context.ConnectionId, put it in one of the group from Groups using Groups.AddToGroupAsync() and notify all other Clients that a new one just joined the club Hub! From now till the hub is disposed server can access all those connections and notify clients whenever there is a need. The only thing that limits you is your


In our case, there is a mobile app that displays team member location on a map. The teams are defined earlier so backend knows how many members there are, which ones are active and while they are, what is their location. Hub starts when first team member joins and based on his team name he is already grouped with.

Groups.AddToGroupAsync(Context.ConnectionId, usersTeamName);


From now on all team members joining will be assigned to this group. And when they do, those methods would be invoked:

await Clients.Caller.SendAsync("TeamJoined", JsonConvert.SerializeObject(teammates));

await Clients.OthersInGroup(usersTeamName).SendAsync("TeammateJoined", JsonConvert.SerializeObject(teammember));


where TeamMates is a collection of TeamMembers and TeamJoined along with TeammateJoined are messages/methods our iOS client listenes to.

So when a new team member joins the group, his name, profile picture and location is sent to other team members. This gives us the possibility to draw his avatar on a map in a precise location so his teammates could know where he currently is. At the same time, the team member gets the same info about his mates to draw on his map.

To keep hub alive and to stop it from being disposed each team member sends its location once in a while. This updated location is then being sent to other teammates so the map could refresh.

Thanks to SignalR hub we can have as many Groups as we like and keep the communication flow within that group (or team in our case).

Setup SignalR iOS client

To setup iOS client we will use mooozyk/SignalR-Client-Swift github repository.

Let's begin with initialization of Hub instance. In order to do that use HubConnectionBuilder class.

let hubConnection = HubConnectionBuilder(url: URL(string: "https://angrynerds.pl/stream")!).build()


HubConnectionBuilder allows us to customize connection options, by setting custom HTTP headers, or append authorization token.

let hubConnection = HubConnectionBuilder(url: URL(string: "https://angrynerds.pl/stream")!)
    .withHttpConnectionOptions { options in
        options.headers = ["foo": "bar"]
        options.accessTokenProvider = {
            return userProvider.token
        }
}.build()


After invoking build() method we are ready to begin conversation with the server. To start connection call start() on HubConnection object.

SignalR API provides 2 basic methods to communicate - one to send the message, and the other to listen to any.

In our example, we will notify the server about the current user location. To do it, we need to call invoke(method: String, arguments: [Any?], invocationDidComplete: (Error?) -> Void)

  • method: String - method server is listening to
  • arguments: [Any?] - array of objects we want to send
  • invocationDidComplete: (Error?) -> Void) - completion handler after completed invocation

Like this:

hub.invoke(method: "updateLocation", arguments: [email, latitude, longitude], invocationDidComplete: nil)


To be notified on each player location update we will use hub.on(method: String, callback: ([Any?], TypeConverter) -> Void)

  • method: String - method server is listening to
  • callback: ([Any?], TypeConverter) -> Void) - callback method with 2 parameters, arguments and converter object, which helps us decode arguments to desired type

We are expecting json data as the only parameter in the server message. JSON should contain a list of connected users with their current location.

hub.on(method: method.rawValue, callback: { (args, typeConverter) in    
    guard let jsonString = try? typeConverter.convertFromWireType(obj: args[0], targetType: String.self),
      let data = jsonString?.data(using: .utf8),
      let users = try? JSONDecoder().decode([User].self, for: data)
      else { return }
    self.updateUI(with: users)


If we need to be up to date with connection status we can implement HubConnectionDelegate methods.

func connectionDidOpen(hubConnection: HubConnection!)
func connectionDidFailToOpen(error: Error)
func connectionDidClose(error: Error?)



Want to know more about the magic you can do with SignalR? Let us know if there's a specific topic you'd like us to cover!


About the Authors
Łukasz Lech is an iOS developer at Angry Nerds - and one of the main creators of our iconic Cats and Dogs app! He’s a Swift enthusiast and he likes to explore this language’s potential as a backend technology. Łukasz is always up to date with all things Apple and he’s also interested in UX/UI design for mobile apps.

Jakub Tomalski is one of our most experienced .NET developers. As he says himself, he is fascinated by the power of API, so backend was the only career path he could choose! He specializes in backend development for mobile apps, but he has also hands-on experience working with web solutions.

Łukasz Lech & Jakub Tomalski

Share

Facebook Twitter LinkedIn
comments powered by Disqus
Let's get in touch!

Let’s get in touch!

Contact us today to receive a free quote for your app or project.

Get a Free Estimate! Arrow right