Monday 18 August 2014

How To Create A Socket Based iPhone App and Server (Networking Tutorial for iOS)

Many iOS apps use HTTP to communicate to a web server, because it’s easy, convenient, and well-supported.
However, in some cases you might find the need to go a bit lower level than HTTP, and communicate using TCP sockets to your own custom server.
The advantages of doing this are several:
  • You can send just the exact data you need to send – making your protocol lean and efficient.
  • You can send connected clients data whenever you want, rather than requiring the clients to poll.
  • You can write socket servers without a dependency of a web server, and can write in the language of your choice.
  • Sometimes you just have to use sockets, if you are connecting to a legacy server!
In this networking tutorial, you’ll get some hands-on experience writing an iPhone app that communicates to a TCP socket server using NSStream/CFStream. Also, you’ll write a simple socket server for it to connect to, using Python!
The iPhone app and chat server will implement chat functionality, so you can chat between multiple devices in real-time!
This tutorial assumes you have a basic familiarity with Python and iOS programming. If you are new to Python programming, check out the official Python tutorial. If you are new to iOS programming, check out some of the iOS tutorials on this site first.
Without further ado, let’s do some socket programming!

What is a socket?

A socket is a tool that allows you to transfer data in a bidirectional way.
So a socket has two sides, as you can see in the diagram below. Your side, and somebody else – usually another process, whether on the same machine or a machine across the network.
A diagram of a socket connection between two PCs
Each side is identified by a combination of two elements: the IP address and port. The first identifies a computer, and the second is connected to a process.
There are many different types of sockets, that differ in the way data is transferred (protocol). The most popular types are TCP and UDP. In this tutorial, we’ll deal with TCP sockets.

Clients and Servers!

It is almost impossible to talk about sockets without talking about clients and servers. It kinda makes you think of a restaurant!
Socket programming is like a restaurant!
Socket programming is like a restaurant!
Image credit: keb
A server is a process that listens for connections from clients. You can think of this like the maitre-d at a restaurant, greeting customers as they walk in the door.
Since a lot of clients may try to connect or order at the same time, a server often starts a child process for each connection (socket) established with a client. You can think of this as the maitre-d assigning a separate waiter for each customer.
Once a connection is established, the client and server can begin to send and receive data through their socket. You can think of the customer and waiter chatting back and forth at this point.
The socket closes when the client closes the communication or when the server gets stopped. Any further attempt at using the socket will result in an error. You can think of this like a waiter getting fed up with a rude customer, kicking them out of the restaurant, and flipping up their hand and saying “brick wall!” whenever the customer tries to speak!

Writing a Simple TCP Server: Overview

Before we write our iPhone app, we’re going to start out by creating our TCP server first. The server we’re going to write needs to:
  • listen for incoming connections
  • keep track of connected clients (identified by the socket and the name)
  • dispatch events (e.g. a new user has joined the chat)
To dispatch events, the server needs to define a protocol defining an expected format/sequence of data that the client/server send back and forth.
For this app, we’re going to use a very simple string based protocol.
For example, when a user joins the chat, its iPhone will send a special string to the server that looks like this:
ima:cesare
The server will be looking for the “iam” string, and when it sees it it will treat that as a “user has joined the chat” command and handle it appropriately. A similar process can be followed for other commands, such as when the user sends a message or leaves the chat.
To build a simple TCP server like this, you can use a variety of different languages. However, for this tutorial we’re going to use the Python language, because it is widely known and available on all platforms.
You can make a TCP server like this with the built-in Python APIs if you want, but to make things easier we’re going to leverage the capabilities of Twisted, a Python framework focused on networking.

What is Twisted?

Twisted is a event-based engine that makes it easy to build web applications using TCP, UDP, SSH, IRC, or FTP.
If we had to build a TCP from scratch we’d need to study Python classes and learn tricks to cope with sockets and network communication. But Twisted comes with a set of handy classes to manage connections and dispatching, so we can focus on the issues specific to our app – broadcasting messages.
Twisted is built around a design pattern you may have heard of called the reactor pattern. This pattern is simple but powerful – it starts a loop, waits for events, and reacts to them, as you can see in the image below:
The reactor pattern, used in Twisted
Don’t worry if you’ve never used Twisted before – we’ll show you how to use it step-by-step in this networking tutorial!

Installing Twisted

If you are working on MacOSX, Python comes already installed. Just open the Terminal (Applications > Utilities > Terminal) and type ‘python -V’ to find out the version. MacOSX 10.6 also includes Twisted, so you are good to go!
If you are working on 10.5 just download and install Twisted from its official site, following the instructions there.
Enough talk – let’s get to code!

Writing a Simple TCP Server: Implementation

We will include all the code to run the server in a file named ‘server.py’, so open your preferred text editor and create it.
To leverage Twisted features we need to import some Python classes. The most important is the reactor, so start your file out by importing it:
from twisted.internet import reactor
Then, add a line of code to create a Twisted event loop:
reactor.run()
In two lines we already have the backbone of our application. Pretty easy, eh?
You can run this from the command line like the following:
python server.py
But as you’ll see, so far the does nothing. It has the control of the program, but doesn’t have any instructions about how to react.
As we said earlier the task of the server is to manage connections
with each client, like multiple customers coming into a restaurant and ordering.
So we need a mechanism to create an object which handles data
transmission each time a new connection is established. And that connection has to implement a protocol, that is the machinery to handle communication.
We will see the protocol implementation in a minute. Now let’s focus a bit on connection management.

Listening for Connections

If we wanted to write the code to handle incoming connections in raw Python, we’d need to create a child process for each connection.
But Twisted makes this much easier! We can use the built-in Factory method which does exactly that: creates a management machinery to handle each connection established with the clients. So let’s update our code as follows:
from twisted.internet.protocol import Factory
from twisted.internet import reactor
 
factory = Factory()
reactor.listenTCP(80, factory)
reactor.run()
You see? Five lines of code and we have an implementation of a reactor pattern, with a related factory which handles the connections. This is great! Now we can focus on the core logic of the server, that is the way it reacts to events.

Defining a Protocol

The protocol is exactly the logic of our server application. This is where we state what to do when a client connects, sends messages and so on.
We include this logic in a class, which extends Twisted “Protocol” class, which in turn allows to define methods to manage events.
Let’s try this out by modifying server.py to look like this:
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor
 
class IphoneChat(Protocol):
    def connectionMade(self):
        print "a client connected"
 
factory = Factory()
factory.protocol = IphoneChat
reactor.listenTCP(80, factory)
print "Iphone Chat server started"
reactor.run()
We have created a new class “IphoneChat”, which extends “Protocol”, and extends the connectionMade ‘hook’ that prints a message when a new connection is made. We have also assigned our class as the protocol of our factory.
We are ready to run the first test of our server.
Move to the server.py folder and type “sudo python server.py”. You should see the “Iphone Chat server started” message appear on the screen.
At this point, the server has started and is listening on localhost (our machine) on port 80. We chose port 80 because it is open by default, since it’s the standard port for http connections. This will allow to test the application running on a real device and connected via wireless without modifying the settings of our router.
The reason we had to run this as sudo is that it requires administer access to listen on a port on the machine.
Instead of building the iPhone app now, we can use telnet just to test the connection. This is a tool which is installed on every operative system and it is a great client substitute to test servers. Open another instance of Terminal (or a separate tab) and type:
telnet localhost 80
If everything is fine the server shell will display “a client connected” and the client shell shows the following:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Congratulations, you have established a connection! If you’d like, you can test the multitasking capabilities of the server by opening another shell and connecting. You should see the same behavior as above.

Keeping Track of Clients

As you remember one of tasks of the server is to keep track of the connections. Each client has a socket assigned, so we need to store that information in an array.
So try this out by modifying the connectionMade hook as follows:
    def connectionMade(self):
        self.factory.clients.append(self)
        print "clients are ", self.factory.clients
And initialize the array of clients as empty right after the line creating the Factory:
factory.clients = []
This way when we have two clients connected there server shell will print something like this:
clients are  [<__main__.IphoneChat instance at 0x101300830>, <__main__.IphoneChat instance at 0x101300a28>]
These are instances of our protocol class. Each of these instances have the capabilities send and receive data from a client over a connection.
For sake of completeness we should also manage when a client disconnects (connectionLost callback), so the code so far looks like this:
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor
 
class IphoneChat(Protocol):
    def connectionMade(self):
        self.factory.clients.append(self)
        print "clients are ", self.factory.clients
 
    def connectionLost(self, reason):
        self.factory.clients.remove(self)
 
factory = Factory()
factory.protocol = IphoneChat
factory.clients = []
reactor.listenTCP(80, factory)
print "Iphone Chat server started"
reactor.run()

Reacting to Chat Events

Now that we’re accepting connections and keeping track of clients, it’s time to get to the fun stuff – responding to chat commands from clients!
For this networking tutorial, we will use a very simple format to exchange messages. We will use strings separated by a “:”. Before that character we have the command, which can be “iam” or “msg”.
  • The aim message is used when someone joins the chat and is followed by a nickname of who joined the chat.
  • The message command sends a message to all clients. There is no need for “msg” to carry the name of the sender, because that is managed server side, in the self.factory.clients list.
When you make your own apps, don’t feel that you need to make simple string-based protocols like this – you can use JSON, XML, a custom binary format, or whatever else you like!
To receive data, the callback that we have to override has the following signature:
def dataReceived(self, data):
In this callback, data is the message received via the socket. In this piece of code we will do the following:
  1. Split the string to find out the command.
  2. If it is “iam”, store the name of the client.
  3. In both cases built a custom message, such as “Cesare has joined” or “Ray says hello”.
  4. In both cases, broadcast the message to the other clients.
Let’s see what this looks like in code! Add the following to server.py, right after the connectionLost method:
    def dataReceived(self, data):
        a = data.split(':')
        print a
        if len(a) > 1:
            command = a[0]
            content = a[1]
 
            msg = ""
            if command == "iam":
                self.name = content
                msg = self.name + " has joined"
 
            elif command == "msg":
                msg = self.name + ": " + content
                print msg
 
            for c in self.factory.clients:
                c.message(msg)
When we broadcast the message we call the method “message”, which we haven’t written yet. Add the code for this next:
    def message(self, message):
        self.transport.write(message + '\n')
It is important to add the ‘\n’ character so the socket detects when the message transmission has completed.
Believe it or not, at this point you have a completely functional chat server!
You can test it for yourself by connecting to your chat server with telnet, and sending commands by manually typing them in, such as “aim:cesare” and “msg:hi”. It’s more fun if you open up multiple telnet sessions at once!

The iPhone Client

Now that the server is ready, we can focus on the client. The client has to manage three main operations:
  1. Joining a chat room
  2. Sending messages
  3. Receiving messages
We will organize the view in a layered way. There will be a main view that is always visible. This will contain two subviews, one for the login and one for the chat, where you can send and receive messages, as you can see in the diagram below:
Diagram of the views in the iPhone chat app we'll make
Let’s get started. Fire up XCode, go to File\New\New Project, choose iOS\Application\View-based Application, and click Next. Name the product ChatClient, click Next, choose a folder on your hard drive, and click Create.
Once you’re done you should have the following classes already created:
Classes made with View-based Application template
Let’s start by working on the view, to reproduce the structure explained above.
The automatically generated code contains the usual application delegate plus a view controller, with its own xib file. So we are already halfway there because a view controller is already set up to display on startup.
Open up ChatClientViewController.xib, and select the third tab in the View area in the top toolbar to open up the library tab. Drag a UIView into the area (on top of the already existing UIView).
Then drag a text field and a button into the view, to get the layout as shown in the screenshot below:
Interface builder layout for join chat view
The text field will be used to enter the nickname to be used in the chat, and the button will trigger the action to join the chat.
Next, connect the text field and button to your view controller by taking the following steps:
  • With ChatClientViewController.xib selected, click the second button in the Editor area in the toolbar to bring up the Assistant Editor.
  • Make sure the assistant editor is on the bottom (for easy use) by selecting View\Assistant Layout\Assistant Editors on bottom.
  • Make sure the Assistant Editor is set up as “Automatic” so it displays ChatClientViewController.h.
  • Select the text field, and control-drag the text field into ChatViewController.h right below the @interface. Make sure the connection type is Outlet, set the name to inputNameField, and click Connect.
  • Select the UIView that contains the text field and button, and control-drag it below the @interface also. Make sure the connection type is outlet, set the name to joinView, and click Connect.
  • Similarly, control-drag the UIButton below the @interface, but set the connection type to Action, name it joinChat, and click Connect.
At this point you can compile and run your code and you should see the view, but it won’t do anything because we haven’t written the code for it yet.
Next we have to write the code to join the chat by establishing a connection with the server. To do that we initialize some variables in the code, but first some introduction.

Stream Programming: An Introduction

To establish a socket connection on iOS we use streams.
A stream is an abstraction over the mechanism of sending and receiving data. Data can be contained in different places like a file, a C buffer or a network connection. Moreover a stream has a delegate associated, which allows to react according to specific events like “the connection is open”, “data have been received”, “the connection has been closed”, and so on.
There are there important classes related to streams included in the Cocoa Framework:
  1. NSStream: This is the super class which defines some abstract features like open, close and delegate.
  2. NSInputStream: A subclass of NSStream for reading input.
  3. NSOutputStream: A subclass of NSSTream for writing output.
These classes are built on top of CFStream, a lower level component pertaining to the Core Foundation layer. If you’re feeling particularly brave, you can rebuild this application just using classes from the CoreFoundation layer.
But to keep things easier for this tutorial, we’re going to stick with the NSStream classes, which are easier to use. The only problem is that NSStream class cannot connect to a remote host, which is exactly what
is required in our application :(
But don’t worry – NSStream and CFStream are sort of bridged, so it’s easy to get a NSStream from a CFStream, there is just one “magic function” to call before.
OK, let’s try this out! Open up ChatClientViewController.h and add the following new instance variables:
NSInputStream *inputStream;
NSOutputStream *outputStream;
Then open up ChatClientViewController.m and add the following new method:
- (void)initNetworkCommunication {
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"localhost", 80, &readStream, &writeStream);
    inputStream = (NSInputStream *)readStream;
    outputStream = (NSOutputStream *)writeStream;
}
The “magic function” CFStreamCreatePairWithSocketToHost helps us to bind two streams to a host and a port. Once you have called it you can freely cast CFStreams to NSStreams. This is the only excursion outside of Objective-C code.
So far we just prepared the connection but there is no communication established. To open it you have
to call the ‘open’ method on both streams, but first there is some work left to do.
We said NSStreams have a delegate. We have to set it before opening the connection, otherwise
we do not receive any notification. We will use the current class as delegate, so add the following at the end of initNetworkCommunication:
[inputStream setDelegate:self];
[outputStream setDelegate:self];
And of course, to make our compiler happy we’ll have to mark ChatClientViewController as implementing NSStreamDelegate, so modify the @interface in ChatClientViewController.h to the following:
@interface ChatClientViewController : UIViewController <NSStreamDelegate>
Our streams have to continuously be ready to send or receive data. To enable this we
have to schedule the stream to receive events in a run loop. If we do not assign a run loop
the delegate will block the execution of our code until there is no data on the stream to read or write, which is a case we want to avoid.
The app has to react to stream events but not be at the mercy of them. Run-loop scheduling allows to execute other code (if needed) but ensuring that you get notifications when something happens on the stream.
So to schedule our input streams to have processing in the run loop, add the following to the end of initNetworkCommunication:
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
Now we are ready to open the connection! Add this next:
[inputStream open];
[outputStream open];
One more thing to do: uncomment the viewDidLoad method, and call initNetworkCommunication when the view is loaded:
[self initNetworkCommunication];
That’s it for now! Compile and run the app, and after it loads take a look at your server output. The shell of the server should show that there is one client connected, as shown in the figure below:
Chat server showing client connected message

Joining the Chat

Now that we’re connected, we’re ready to join the chat!
Remember that the join message has the form “iam:name”. So we need to build a string like that and write it to the outputStream.
Note that we cannot write a string directly on a stream – we need to convert it to a NSData first. So open up ChatClientViewController.m and replace joinChat with the following:
- (IBAction)joinChat:(id)sender {
 
 NSString *response  = [NSString stringWithFormat:@"iam:%@", inputNameField.text];
 NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
 [outputStream write:[data bytes] maxLength:[data length]];
 
}
Believe it or not, that’s it – pretty simple to send a message out, eh?
Compile and run, enter a name in the text field, and tap Join. Switch over to your server shell, and you should see a message verifying your “iam” command, as shown in the screenshot below:
Chat server showing nickname message

Creating the Chat View

Next we need to create a user interface for sending and displaying chat messages.
Open up ChatClientViewController.xib, and drag a new UIView so it is a child of the main UIView (and a sibling of the UIView with the Join button). Then move the ordering of the two views so that your new view is the second in the list of the main UIView’s children (hence on top).
Then drag a text field, button, and table view from the library into the view, to match the layout shown in the screenshot below:
Interface builder layout for chat
Next you need to connect the UI elements to your class. Take the following steps:
  • Control-drag the Text field down below the @interface in your Assistant Editor displaying ChatClientViewController.h. Make sure the connection type is Outlet, name it inputMessageField, and click Connect.
  • Control-drag the Table view below the @interface also. Make sure the connection type is Outlet, name it tView, and click Connect.
  • Control-drag the Button down below the @interface also, but set the connection type to Action, the name to sendMessage, and click Connect.
  • Control-click the Table view, and drag lines from the small white circles to the right of Data Source and Delegate up to “File’s Owner.”
  • Control-click the View itself (the new one you just made containing the table view, et.) down below the @interface. Make sure the connection type is Outlet, name it chatView, and click Connect.
  • Finally, move the new view back above the original view so the original view (with the Join button) is once again in front and visible.
Then you need to modify the @interface declaration in ChatClientViewController.h as follows:
@interface ChatClientViewController : UIViewController <NSStreamDelegate, UITableViewDelegate, UITableViewDataSource> {
Then switch to ChatClientViewController.m and add the following new methods:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"ChatCellIdentifier";
 
 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
 
 return cell;
 
}
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
 return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
 return 0;
}
Finally, you need to add a bit of code to switch the chat view to the front after the user joins the chat. Simply add the following line at the end of joinChat:
[self.view bringSubviewToFront:chatView];
Compile and run your code, enter your name and tap join, and now your new user interface should appear!
The chat view in the iPhone app

Sending Messages

Now we are left with the core functionality of our app, sending and receiving messages from other peers.
We’ll start by implementing the sendMessage method, which we’ve set up to run when the user clicks the Send button. We’ll implement it in a way very similar to join chat – we just need to switch “iam:” with “msg:”.
So try this out by replacing sendMessage with the following:
- (IBAction)sendMessage:(id)sender {
    NSString *response  = [NSString stringWithFormat:@"msg:%@", inputMessageField.text];
 NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
 [outputStream write:[data bytes] maxLength:[data length]];
}
And that’s it! Compile and run your app, log in and try sending a message. If you switch to your server shell, you should see the test message appear!

Receiving Messages

To tell you the truth the app is also receiving messages from the server, but we did not write the
code to display them. So let’s work on that next.
First, add a new instance variable to ChatClientViewController.h inside the @interface:
NSMutableArray * messages;
Then, switch to ChatClientViewController.m and make the following changes:
// At bottom of viewDidLoad
messages = [[NSMutableArray alloc] init]; 
 
// In dealloc
[messages release];
 
// At bottom of tableView:cellForRowAtIndexPath:, right before return cell
NSString *s = (NSString *) [messages objectAtIndex:indexPath.row];
cell.textLabel.text = s;
 
// Replace return 0 in tableView:numberOfRowsInSection: with the following
return messages.count;
This is pretty standard stuff – we’re just setting up a NSMutableArray to contain a list of strings, and setting up our table view to display them.
Now we are left with the implementation of the NSStream delegate. As you remember, one the key elements of NSStream is the delegate property which is defined as NSStreamDelegate.
Its definition includes the message stream:handleEvent: which will enable our application to react to the activity happening on streams. We already set the delegate so we just need to implement the following method, checking the type of event.
So start things out by adding the following simple implementation:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
 NSLog(@"stream event %i", streamEvent);
}
The constants for NSStreamEvent are the following.
typedef enum {
   NSStreamEventNone = 0,
   NSStreamEventOpenCompleted = 1 << 0,
   NSStreamEventHasBytesAvailable = 1 << 1,
   NSStreamEventHasSpaceAvailable = 1 << 2,
   NSStreamEventErrorOccurred = 1 << 3,
   NSStreamEventEndEncountered = 1 << 4
};
You might play with the to see what happens. In our case we are particularly interested in:
  • NSStreamEventOpenCompleted, just to check the connection has been opened
  • NSStreamEventHasBytesAvailable, fundamental to receive messages
  • NSStreamEventErrorOccurred, to check issues during the connection
  • NSStreamEventEndEncountered, to close the stream when the server goes down
So modify the implementation to switch on the ones we care about:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
 
 switch (streamEvent) {
 
  case NSStreamEventOpenCompleted:
   NSLog(@"Stream opened");
   break;
 
  case NSStreamEventHasBytesAvailable:
   break;   
 
  case NSStreamEventErrorOccurred:
   NSLog(@"Can not connect to the host!");
   break;
 
  case NSStreamEventEndEncountered:
   break;
 
  default:
   NSLog(@"Unknown event");
 }
 
}
The core point here is the NSStreamEventHasBytesAvailable case. Here we should:
  • read bytes from the stream
  • collect them in a buffer
  • transform the buffer in a string
  • add the string to the array of messages
  • tell the table to reload messages from the array
So let’s get started! Replace the NSStreamEventHasBytesAvailable case with the following:
case NSStreamEventHasBytesAvailable:
 
    if (theStream == inputStream) {
 
        uint8_t buffer[1024];
        int len;
 
        while ([inputStream hasBytesAvailable]) {
            len = [inputStream read:buffer maxLength:sizeof(buffer)];
            if (len > 0) {
 
                NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
 
                if (nil != output) {
                    NSLog(@"server said: %@", output);
                }
            }
        }
    }
    break;
First we want to be sure that the event comes from the inputStream. Then we prepare a buffer
(size 1024 is enough for the purposes of this tutorial).
Finally we start a while loop to collect the bytes of the stream. The read method returns 0 when there is nothing left in the stream. So when the result is greater than zero we convert the buffer into a String and we print the result.
We did the most difficult part. Now we have just to store the string into the array of messages
and reload the table view. To do this we add a new method like this.
- (void) messageReceived:(NSString *)message {
 
 [messages addObject:message];
 [self.tView reloadData];
 
}
Then call this new method right below the NSLog statement:
[self messageReceived:output];
We are done! Let’s run the server, connect to it with an iPhone app, and with telnet at the same time. You should be able to log on and send messages back and forth. Congratulations!
A fully functional socket-based iPhone app and server

Tweaks

There are a few tweaks which might improve the applicationt. First we should clean up the message field when the message has been sent. To do this, just add the following to the bottom of sendMessage:
inputMessageField.text = @""; // clean the field
Second, as the number of messages grows, the table ‘hides’ them below the keyboard. To enable a sort of autoscrolling just add the following to the bottom of messageReceived:
NSIndexPath *topIndexPath = 
    [NSIndexPath indexPathForRow:messages.count-1 
                       inSection:0];
[self.tView scrollToRowAtIndexPath:topIndexPath 
                  atScrollPosition:UITableViewScrollPositionMiddle 
                          animated:YES];
As for streams, when the server closes the connection, we should remember to close the stream
and remove it from the run-loop. Just replace the case for NSStreamEventEndEncountered with the following:
case NSStreamEventEndEncountered:
 
    [theStream close];
    [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
 
break;
Compile and run, and enjoy an improved app!

Running the app on the device

If you want to run this app on your device, if you’re running your server on your local machine you might run into issues with your router.
If you do, here are some tricks. First, remember to switch the “localhost” string
with the ip of your computer. To find the current IP go to “System Preferences > Network” (see figure below)
Finding your ip address in Mac OS X
Your device should be connected via wireless to the same router which serves you computer. If you want to use 3G connection you should configure your router to accept connections from outside your network on the port 80 (not recommended).

Where To Go From Here?

Here is a zip file with all of the code from the above tutorial.
At this point you should know the basics of creating a socket-based iPhone app and creating a simple server for it to connect with using Python and Twisted.
If you’d like to play around with this some more, here’s some things that might be fun to try:
  • Detect when a connection to the server is not possible
  • Identify when a nickname is already used and notify the user
  • Add a timestamp to each message
  • Use different cell colors according to the message sender
If you implement any of these and want to share the code with others, or have any questions or comments, please join the forum discussion below!

No comments:

Post a Comment

Please comment here...

203 Favorite JavaScript Utilities

https://1loc.dev/