Building WorldPin

Integrating SIOSocket with MapKit

Patrick Perini
MegaBits Lab

--

Edit: This post has been updated to take advantage of SIOSocket v0.2.0

Recently, we released SIOSocket, an open source Objective-C client for socket.io 1.0. Our last post was all about the motivation for and implementation of SIOSocket, but in this post, we’re building a thing! Check out the source on GitHub.

WorldPin

WorldPin is a simple app that shows, in real time, the location of everyone else using the app.

Node.js App

The socket.io implementation of the backend server of this app is rather simple. First, let’s start by building our Node’s package.json file.

https://gist.github.com/pcperini/6dd7bf7e494a7737a4cd

Once this file is created, run npm install and you’ll see your dependency tree builder in action (socket.io will branch to all of its own underlying dependencies, making it easy for us). Next, we’ll fire up our app.js file, which will contain the logic for handling requests. Step one: import the socket.io module.

https://gist.github.com/pcperini/f3193e9fed83ef647cdd

Which brings us to the real meat of our web application. Let’s first write out connection event listener, which will be executed when a client connection occurs.

https://gist.github.com/pcperini/34a7f03b8f169edc097b

Inside the this function, we’ll add in three major components. First, as soon as a client connects, we’ll emit a join event broadcast, which will include the client’s id value.

https://gist.github.com/pcperini/d8ebffdd45597cf13001

For reference, there are a few different types of communication patterns sockets can use. Pure emits will distribute an event message to every client, including itself. Alternatively, broadcast.emits will distribute an event to every client excluding itself. And for debugging’s sake, we’ve thrown in a console printout when a user connects, as well.

Finally, add two event listeners to handle the various events our clients will be firing off to the server.

https://gist.github.com/pcperini/14abff096a2bbc6508d5

We’ll listen for location events and establish our own custom disconnect event (which occurs after socket.io’s standard disconnect event, as a sort of middleware). When we receive the location event, we’ll broadcast an update event, with the string “id:data”, in response. And for the disconnect, we will broadcast a disappear event, with the id as content. Clients who receive this disappear event will then remove pins labeled with the id from the map. For debugging, again, we’ll include a console printout of the socket which has disconnected.

That’s it! With these ~15 lines of code, you’ll be handling real-time communication! To launch, simply trigger node app.js in the command line. (Bonus: you can use DEBUG=socket-io* node app.js to activate Node’s verbose debugging tools.)

iOS App

Having built our incredibly simple sync app, it’s time to get started on the client side. Start a new iOS Single View Application in Xcode, and drop by the Capabilities tab for the project. Add the Maps capability to include MapKit.framework and the necessary entitlements.

We’re using Xcode 6 here, but Xcode 5 should be the same.

As you might expect, the next step is to create the MKMapView in your Storyboard, and hook it up via the necessary IBOutlets. (If this isn’t a familiar process, definitely check out RW’s Map Kit tutorial.) Once you’ve got a working map view hooked up to your View Controller (and vice versa, as its delegate), we’re ready to start responding to map view’s events. To zoom to the user’s location, use this method:

https://gist.github.com/pcperini/34d432adecc4f2d7ef0f

And to add a new pin whenever an annotation is added, use this method:

https://gist.github.com/pcperini/7e5814b29bf80477d0b4

Now the map can accurately display all WorldPin users!

WorldPin lives!

Except, there aren’t any. So we have to get each running instance of WorldPin talking, and to do that, we establish a socketed connection to our Node.js server. To create an intialize an SIOSocket object, we need to call +[SIOSocket socketWithHost:response:], which creates a new socket.io client in a JavaScriptCore context and connects it to the given host.

https://gist.github.com/pcperini/3a433523ce8e67c25444

We’ve also updated our mapView:didUpdateUserLocation: to emit a location event whenever our user’s location is updated. The format for this is incredibly simple: the Node server simply receives and broadcasts the “lat,long” pair.

Finally, we need to tell our SIOSocket to listen for events from the server. The easiest and most obvious events to listen for are connect, join, and disappear.

https://gist.github.com/pcperini/9e8535811117ee420883

When connect and join events are triggered, we call our own mapView:didUpdateUserLocation: method, which fires a location emission, and alerts all other users to our location. When we receive that information, on an update event, we should create or update our list of pins.

https://gist.github.com/pcperini/5d54c823f835f7dd856b

We store pin data as WPAnnotation objects, which are simple objects that implement the MKAnnotation protocol, and are included in the project’s source. Once our socket can respond to update events, we’re ready to see the location of every WorldPin user!

WorldPin running on an iOS simulator and an iPod Touch

And, of course, the real benefit of pairing socket.io and MapKit is real-time updates.

See that? That’s real time.

And there you have it. The source code for this tutorial is available on our GitHub, and if you have any problems, email dev@megabitsapp.com and we’ll try and help!

--

--