Socket.IO on iOS
We are pleased to announce the immediate availability of the Socket.IO Swift Client! Youll now be able to write code that runs natively on iOS and OSX, while maintaining the simplicity and expressiveness of the JavaScript client!
import Foundation
let socket = SocketIOClient(socketURL: "localhost:8880")
socket.on("important message") {data, ack in
println("Message for you! \(data?[0])")
ack?("I got your message, and I'll send my response")
socket.emit("response", "Hello!")
}
socket.connect()
To show how you can use it in a real project, Ill show you how to create a small Tic Tac Toe app like the one shown above.
Overview
In this tutorial well look at creating a small iOS app that demonstrates
socket.io and iOS. If you learn better from looking at code you can look
at it here. The point of the tutorial is not to explain developing an
iOS app, but to demonstrate how you can incorporate
socket.io-client-swift
into your projects! So it is assumed you have
a basic knowledge of XCode. Note: This example uses Swift 1.2. However,
1.2 isnt much different from Swift 1.1, and the library has branches for
Swift 1.1 and 1.2. The only difference in this guide is I use 1.2s
expanded if let
construct to avoid nesting. Note 2: While this
library is written in, and meant for, Swift applications, it can be used
with Objective-C projects, but will require some extra work (youll
probably need to create a Swift class that can interface with your
Objective-C code, as not all methods in the client will be available to
Objective-C i.e emit, onAny). See this for more information.
Introduction
I designed socket.io-client-swift
to be as close to
socket.io-client
as I could. So many of the ways you do things in
socket.io-client look similar here! This is not a step-by-step tutorial
for making a Tic Tac Toe app, only adding the socket.io-client part of
it.
Setting up the Project
From Xcode, create a new project with the layout of single-view iOS
application. You can name it whatever you like, Ill be naming mine
TicTacIOiOS
. The next step is getting the code for
socket.io-client-swift
, you can either use git
to clone the repo
to a directory, or simply download a release. Either way you get it, the
process of adding it to your project is the same. Simply drag the folder
named SwiftIO to the same place you copied SocketRocket! (Again making
sure you select copy.) And thats it, the hardest part of putting our app
together is done! At this point, if you want to test that its setup
properly, try building and running the app, it should compile.
Adding Our Code
Now, assuming youve created your user interface. Its time to add the
code that will interface with our socket.io server! In our demo
application we have one UIViewController subclass, named ViewController.
All of our code will be added there. First, we need to add a member
named socket
of type SocketIOClient
to our ViewController.
let socket = SocketIOClient(socketURL: "localhost:8900")
Now, in our overridden viewDidLoad
method we want to add two things,
a method call that will add our handlers to our socket, and a call to
connect our socket.
self.addHandlers()
self.socket.connect()
Handlers
Now that we have our method calls, its time to implement the method that adds the handlers!
func addHandlers() {
// Our socket handlers go here
}
Since were about to add the handlers, I think its worth mentioning the
syntax I use for closures in Swift. Swift has many different ways of
expressing closures, and they can be found here. But the form I use for
adding handlers is a trailing closure, without explicit type
annotations. You can use other closure forms if you wish. Im not going
to show you all the handlers in our demo app here, just a few that
demonstrate important things youll need to know when working with
socket.io-client-swift
. The first handler were going to add is one
that will be called on any event, since its useful for debugging the
API.
// Using a shorthand parameter name for closures
self.socket.onAny {println("Got event: \($0.event), with items: \($0.items)")}
The next handler that well add is the one that tells the app that the game has started.
self.socket.on("startGame") {[weak self] data, ack in
self?.handleStart()
return
}
Now to explain some things. [weak self]
is a capture list. It tells
the compiler that the reference to self in this closure should not add
to the reference count of self. This is so when the socket object goes
out of scope, the capture made in the closure doesnt keep it from being
deallocated. The first parameter in all .on
callbacks is an optional
NSArray, it will contain all the data received with the event, or nil.
The second parameter in the callback is an optional with the type of
AckEmitter. AckEmitter is simply a typealias of
(AnyObject...) -> Void
. Well see this used later. The next
handler well add is the one for a win.
self.socket.on("win") {[weak self] data, ack in
if let name = data?[0] as? String, typeDict = data?[1] as? NSDictionary {
self?.handleWin(name, type: typeDict)
}
}
As mentioned before, this is new syntax for if let
introduced in
Swift 1.2. It simplifies optional unwrapping (pyramid of doom). The
important thing to gather from this handler is that you do not need to
force unwrap the array to get the object. Also note that a JSON object
will be exposed to Swift as an NSDictionary. Another thing to note is
that for almost all your handlers that have data, youll be doing some
kind of optional unwrapping and type casting. This is an unfortunate
consequence of working with JavaScript. The final handler that Ill
demonstrate here is the one that handles whether the player wants to
play again.
self.socket.on("gameReset") {data, ack in
ack?(false)
}
In this simplified example, we simply send an acknowledgement to the server that we dont ever want to play again. Remember that AckEmitter has a variadic definition, so you can send multiple things at once if you wanted.
Emitting Events
The next thing you’ll probably want to know is how to send events from the client. Youll be pleased to know that it has a form almost exactly the same as socket.io-client! In our ViewController we have a method that handles when a user wants to make a move. Without going into the logic of that, well show how we send the data to the server.
@IBAction func btnClicked(btn:UIButton) {
let coord:(x:Int, y:Int)
// Long switch statement that determines what coord should be
self.socket.emit("playerMove", coord.x, coord.y)
}
Thats all you need to do for sending data! Some other examples of sending which arent covered in our demo are:
Sending JSON
As mentioned before, JSON in Swift is best represent as a Dictionary. Thankfully for you, you dont need to worry about turning it into something socket.io will understand, thats all done under-the-hood.
let myJSON = [
"name": "bob"
]
socket.emit("jsonTest", myJSON)
Sending Binary
Binary data is also handled by the client, so you dont need to worry about it.
let data = "Hello, ".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let data2 = "World".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
socket.emit("dataTest", data, ["world": data2])
Requesting Acks
The client can also request that the server send an ack for an event.
This is done by using the emitWithAck
method which returns an object
to which you can add a handler.
socket.emitWithAck("needsAck", "test").onAck {data in
println("got ack with data: (data)")
}
A Note About Multitasking in iOS
As you probably know, iOS is very picky about what you can do in the background. As such, dont expect that your socket connection will survive in the background! Youll probably stop receiving events within seconds of the app going into the background. So its better to create a task that will gracefully close the connection when it enters the background (via AppDelegate), and then reconnect the socket when the app comes back into the foreground. If you want to learn more about the client, check out the README! We also invite you to contribute by submitting issues, patches, documentation and examples.