Blog


Feb

Using websockets in native iOS and Android apps


This is an example of using websockets to communicate in real time from browser, iOS, and Android clients.

Why would we want to do that? Since the same real time backend can be reused for all platforms, we can keep the architecture simple and the number of distinct components that can fail to a minimum. Or maybe we just want to add a native client to an existing web service that already uses websockets, without having to change stuff on the backend. This post will demonstrate that it is quite straightforward to connect to a websocket server from iOS and Android as well as from the browser.

We recently used this approach in the Need for Speed™ Network application, which is available for all three platforms. Check out the trailer video on YouTube to see it in action. We used websockets to send the real time in-game positions of the user's friends in order to display them on a map, among other things.

The websocket client API is very simple, and is pretty much identical across the different client implementations used in this example. Adding TLS encryption is also pretty easy from the client's point of view, since we just need to change the ws:// URL to a wss:// one.

The example app is a simple chat service. There are no separate channels or nicknames to keep it as bare bones as possible. When a new client connects, it automatically sends a message including what device or browser it is running on.

The full example code (server and the three clients) is available at https://github.com/elabs/mobile-websocket-example. The apps are very basic and only intended to show how to use the API - they contain very little error handling, no tests, are not prepared for localization, etc.

Server

This is a basic websocket server implemented in Ruby using the EM-WebSocket gem.

Here is the entire code for the server implementation:

require "em-websocket"

EventMachine.run do
  @channel = EM::Channel.new

  EventMachine::WebSocket.start(host: "0.0.0.0", port: 8080, debug: true) do |ws|
    ws.onopen do
      sid = @channel.subscribe { |msg| ws.send(msg) }
      @channel.push("#{sid} connected")

      ws.onmessage { |msg| @channel.push("<#{sid}> #{msg}") }

      ws.onclose { @channel.unsubscribe(sid) }
    end
  end
end

The server accepts websocket connections on port 8080. When a new client connects, it subscribes to an internal channel. Every time a client sends data it is pushed to the channel, and when new data is available on the channel, it is sent to all connected clients. Clients are identified by their subscription ID which is an incrementing integer. When the client disconnects, it is unsubscribed from the channel.

To get the server running, run bundle install and then ruby server.rb.

Browser client

The biggest reason to use websockets in the first place (instead of, say, regular sockets) is that they are possible to use from a modern browser context. The JavaScript code looks like this:

$(document).ready(function() {
  ws = new WebSocket("ws://" + location.hostname + ":8080/");

  ws.onmessage = function(event) {
    $("#messages").append("<p>" + event.data + "</p>");
  };

  ws.onclose = function() {
    console.log("Socket closed");
  };

  ws.onopen = function() {
    console.log("Connected");
    ws.send("Hello from " + navigator.userAgent);
  };

  $("#new-message").bind("submit", function(event) {
    event.preventDefault();
    ws.send($("#message-text").val());
    $("#message-text").val("");
  });
});

We connect to the server on port 8080 (the port we listened to in the server) and implement callbacks for when the connection is established, when a message arrives, and when the connection is closed.

We also add an event handler for sending a new message when the user submits a form.

This code only supports modern browsers. There are projects that can be used to support older browsers via a Flash implementation.

To start serving the browser client on port 8000, run python -m SimpleHTTPServer from the directory with the HTML and JS file.

iOS client

To build the iOS client you need a Mac and the Apple developer tools (Xcode) installed. To run it on an actual device (and not the simulator), an Apple developer program membership is required.

For iOS, we use the websocket client implementation SocketRocket. The easiest way to include the library is via Cocoapods. This is what our Podfile looks like:

platform :ios, "7.0"

pod "SocketRocket"

If you haven't got Cocoapods installed, run gem install cocoapods and pod setup. Then run pod install from the iOS directory with the Podfile, and we're good to go.

The relevant code is in ViewController.m. To connect to the websocket server:

- (void)connectWebSocket {
  webSocket.delegate = nil;
  webSocket = nil;

  NSString *urlString = @"ws://localhost:8080";
  SRWebSocket *newWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:urlString]];
  newWebSocket.delegate = self;

  [newWebSocket open];
}

Replace localhost with the relevant hostname if not running the simulator on the same computer that runs the websocket server.

Then we implement the SRWebSocketDelegate protocol:

- (void)webSocketDidOpen:(SRWebSocket *)newWebSocket {
  webSocket = newWebSocket;
  [webSocket send:[NSString stringWithFormat:@"Hello from %@", [UIDevice currentDevice].name]];
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
  [self connectWebSocket];
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
  [self connectWebSocket];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
  self.messagesTextView.text = [NSString stringWithFormat:@"%@\n%@", self.messagesTextView.text, message];
}

Note that this is very similar to the Websockets browser Javascript API.

Here is how to send a new message:

- (IBAction)sendMessage:(id)sender {
  [webSocket send:self.messageTextField.text];
  self.messageTextField.text = nil;
}

The remaining code in the iOS project is mainly for dealing with resizing views when showing or hiding the keyboard. The app targets iOS 7.0 and is not optimized for iPad.

Android client

The Android app is built using Android Studio.

For Android we will use the client library Java WebSockets. Since we're using Android Studio, the easiest way to include the code from Maven is by listing it in the gradle dependencies in build.gradle:

dependencies {
  compile "org.java-websocket:Java-WebSocket:1.3.0"
}

Gradle will take care of downloading it and making it available to the project the next time we build.

The relevant code for interacting with the websocket server is in MainActivity.java. To connect to the websocket server:

private void connectWebSocket() {
  URI uri;
  try {
    uri = new URI("ws://websockethost:8080");
  } catch (URISyntaxException e) {
    e.printStackTrace();
    return;
  }

  mWebSocketClient = new WebSocketClient(uri) {
    @Override
    public void onOpen(ServerHandshake serverHandshake) {
      Log.i("Websocket", "Opened");
      mWebSocketClient.send("Hello from " + Build.MANUFACTURER + " " + Build.MODEL);
    }

    @Override
    public void onMessage(String s) {
      final String message = s;
      runOnUiThread(new Runnable() {
        @Override
        public void run() {
          TextView textView = (TextView)findViewById(R.id.messages);
          textView.setText(textView.getText() + "\n" + message);
        }
      });
    }

    @Override
    public void onClose(int i, String s, boolean b) {
      Log.i("Websocket", "Closed " + s);
    }

    @Override
    public void onError(Exception e) {
      Log.i("Websocket", "Error " + e.getMessage());
    }
  };
  mWebSocketClient.connect();
}

Make sure to change websockethost to whichever address hosts the websocket server.

And to send a new message:

public void sendMessage(View view) {
  EditText editText = (EditText)findViewById(R.id.message);
  mWebSocketClient.send(editText.getText().toString());
  editText.setText("");
}

The Android app targets Android 4.0+. It requires the android.permission.INTERNET permission to connect to the websocket server.

Hopefully this example illustrates that using Websockets from native mobile apps is a viable alternative, especially if there is a web browser client consuming the same API.

Again, all the example code above is available at https://github.com/elabs/mobile-websocket-example. If you clone that repo you should have everything you need to build and run the server and all three clients.

Jan

The Year 2013 — A Summary


Every December, I write a recap of the year that’s gone. I’m a couple of days late this time, but I’d still like to write about some of our highlights from 2013.

Need For Speed™ Network

2013 was dominated by our biggest and most ambitious project yet: Need For Speed™ Network for our new clients Ghost Games and EA. We had pretty much the full team working on this project for most of the year. We’re not completely done with it yet, and we’ll have more to say when we’re finished. In the mean time, check out the promo video for the app.

ProjectPuzzle

Our own product ProjectPuzzle has been left mostly unattended since we launched it in 2012. We had several new customers sign up during 2013 though, and we received lots of great feedback from people who tried it out. We’ve started working on the next version of ProjectPuzzle, and I’m really looking forward to sharing the progress of that with you in the coming months.

Nordic Ruby

The 4th annual Nordic Ruby was the best one yet. It was probably also the last one, in a way. We’re changing things a bit for 2014.

I wrote a blog post about Nordic Ruby 2013 with lots of great photos and video. Check it out if you haven’t already.

Part of the team at Nordic Ruby 2013

Anders T

At the end of November we said goodbye to Anders Törnqvist. Anders was one of the original 4 team members at Elabs, and after 5 great years here he wanted to try switching from consulting to working in a product company. We wish him the best of luck with his new job at PugglePay.

Anders Törnqvist

What’s next?

Right now we’re working hard on finishing the last pieces of the Need For Speed™ Network project, and after that we’re looking for new client projects. If you have something you think we can help with, please get in touch.

Other than client work, we’re also very excited about getting to work on the next versions of ProjectPuzzle and Nordic Ruby.

Here’s to 2014.

/ CJ

Jun

Nordic Ruby 2013


Earlier this month, we held the fourth edition of Nordic Ruby, the conference we organise every year. Just like last year we held it at Hasseludden Yasuragi, a beautiful Japanese-style spa in the Stockholm archipelago.

Terrace

I couldn't be happier with the way the conference turned out this year. We had some incredible talks from our fantastic speakers, and I had a wonderful time meeting old friends and making new ones.

CJ on stage

If you want to see more of what Nordic Ruby was like this year, you'll find slides, notes, blog posts and photos in the coverage section of our Lanyrd page.

Some standout links that you'll definitely want to check out:

Speaking of what Nordic Ruby is all about, I think this year's conference showed that it's not about Ruby in any case. It's never been really, but we haven't done a good job communicating that. That's changing.

Teppanyaki

Nordic Ruby will be back next year, but it won't be called Nordic Ruby any more. The name of the conference will be different, but the feeling of it will stay the same. If you want to keep up with our announcements about it, make sure you follow @nordicruby on Twitter.

See you next year!

Nordic Ruby 2013 from Alexander Lang on Vimeo.

Apr

My Craftsman Swap with Bendyworks


Last week I flew from Sweden to Madison, WI to work for a week together with the developers at Bendyworks, or as they call themselves, Bendyworkers.

After the 23 hour flight I was met by Stephen who didn’t hesitate for a moment to welcome me to Madison in the middle of the night on a Sunday. Over the week I’ve learned that a great talent for hospitality is something that all the Bendyworkers have in common. Even though it was late Stephen gave me a quick tour around the beautiful Capitol building located right next to Bendywork’s office.

Capitol building

Bendyworkers perform their craft in a rustic triangle-shaped building built before the 1900’s. The office is located right downtown and is surrounded by a bursting number of cafés, restaurants, and even a theatre. Inside I found that the rooms are all very open and people are moving naturally between desks and programming pairs. During Bendywork’s monthly “release valve” meeting I learned that not even the owners Stephen, Brad, and Jim take a dedicated office for granted. To me this illustrates well how flat and transparent the company structure is at Bendyworks.

I had the opportunity to work on two different projects over the week. On Monday I worked together with Chris on a CMS for Internet Week New York. The project was wrapping up, since all the major features already were delivered we got some time to spend on refactoring a few acceptance tests and have them execute faster. Tuesday through Thursday I paired up with Josh on work for SEOmoz. Josh has some serious shell and terminal vim skills going on, while I’m more of a mvim user depending a bit more on Mac OS X to do window handling for me. The SEOmoz work spanned across three different Rails-based applications with a very heavy emphasis on client side JavaScript.

Office

Bendyworkers all have a genuine passion for their craft. When they don’t attend meetups they’re working on numerous open source projects or catching up with their self-assigned book club related homework. Lunches are spent preparing for the book club or sharing knowledge through more organized presentations, like when Joe had a great walkthrough of his blogpost on giving yourself a security makeover.

With all that time spent on perfecting their craft you’d think Bendyworkers wouldn’t know how to have fun. Well, you’re wrong. Ping-pong games, comedy clubs, taco-tuesdays, arcade halls, great food and drinks just to name a few of the activities Bendyworkers have treated me to over the week.

With that, I’d like to thank Bendyworks for a week full of fun, productive, and educational experiences!

Apr

Steve Jobs on what's important in the development of a product


You know, one of the things that really hurt Apple was after I left John Sculley got a very serious disease. It’s the disease of thinking that a really great idea is 90% of the work. And if you just tell all these other people “here’s this great idea,” then of course they can go off and make it happen.

And the problem with that is that there’s just a tremendous amount of craftsmanship in between a great idea and a great product. And as you evolve that great idea, it changes and grows. It never comes out like it starts because you learn a lot more as you get into the subtleties of it. And you also find there are tremendous tradeoffs that you have to make. There are just certain things you can’t make electrons do. There are certain things you can’t make plastic do. Or glass do. Or factories do. Or robots do.

Designing a product is keeping five thousand things in your brain and fitting them all together in new and different ways to get what you want. And every day you discover something new that is a new problem or a new opportunity to fit these things together a little differently.

And it’s that process that is the magic.

— Steve Jobs, Triumph of the Nerds

Via 37signals and CNN Fortune Tech