Showing posts with label ios frameworks. Show all posts
Showing posts with label ios frameworks. Show all posts

Tuesday, 4 November 2014

Variable-Sized Items in UICollectionView

Variable-Sized Items in UICollectionView

UICollectionView was added last year with iOS 6 and to this date I had no real chance to get acquainted with it since most of my apps were still supporting iOS 5. Doing a fresh app only supporting the latest iOS version finally allowed me to dig into it and share the journey with you.
The special scenario we want to look at today is how we could configure variable-sized collection view cells for items like tags. We want to have the cells adjust their size automatically based on the tag string and ideally we don’t want to have to write any layout code for determine the needed sizes.
Please forgive if the following has a few places where I stumbled. It is these temporary snags that I believe you learn from the most, so I left them in the final article.

Basic Training

We start out with a fresh project from the “Single View Application” template. On the ViewController.xib we add a UICollectionView to fill the entire view.
Adding a UICollectionView
The Collection View – identical to it’s grandfather UITableView – has a delegate and a dataSource outlet which we connect to “File’s Owner”, which is the ViewController class. This puts our view controller also in charge of the collection view’s contents and interactions. We also need an outlet for a reference to the collection view, so we add that with the assistant editor. The outlet property we add to ViewController.h can be weak because being a subview of the ViewController’s view means it is also sufficiently retained.

Cells

For the content we need to design a prototype cell. If you’re using the collection view in a view controller’s XIB you have to register it for use in the collection view in code. If you add a collection view inside a storyboard you create prototype cells inside the collection view’s area and thus won’t have to register the cell identifier. Since we started without a story board, let’s proceed with the manual steps.
We create a prototype cell in Interface Builder, give it a white background, add a UILabel and add some constraints to link the edges with the superview’s edges, 5 points distance. From the Editor menu we choose “Size to Fit Content” for the Label because we want the label text to determine the item size. We’ll see later if that works as we think.
Adding the Prototype Cell
We set the Identifier to “TagCell” so that we can reference our design.
Here we hit the first snag. If you are not using story boards there is no way to get the collection view to use a Collection View Cell from the same NIB file. These are the two methods available for registering a cell design:
  • – registerClass:forCellWithReuseIdentifier:
  • – registerNib:forCellWithReuseIdentifier:
The first instantiates a certain class to be used for a given reuse identifier. This we would use if we created the cell’s view hierarchy entirely in code. The second requires a NIB and thus requires that the cell needs to be the only item in this NIB. This is the second important reason why it is way less work using Collection Views with storyboards, to avoid this hassle.
To remedy this we create a new “empty Interface Builder Document”, which we name TagCollectionViewCell. Thankfully we can simply CMD+X the cell design from one ViewController NIB and CMD+V it into the empty document.
Cell Design moved to own NIB
Next we’ll try to register the cell for use in the collection view and want to see a number of items displayed.

Registering the Cell Design

We need to register the NIB to be used whenever we want a cell for identifier “TagCell”, so we add the following to ViewController.m in the viewDidLoad section.
UINib *cellNib = [UINib nibWithNibName:@"TagCollectionViewCell" bundle:nil];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:@"TagCell"];
We only need to implement more methods belonging to the UICollectionViewDataSource protocol to get something to show.
#pragma mark - UICollectionView
 
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
   return 100;
}
 
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
   UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TagCell" forIndexPath:indexPath];
 
   return cell;
}
Launching the app now, we see 100 items all using our – probably not award-winning – cell design.
Cells showing up
The first observation to make is that all cells have the same size, 50×50 points to be exact. This comes from us not changing the default values in Interface Builder.
UICollectionView Cell Defaults
But before we proceed to giving the cells individual sizes, let’s first improve the design a bit. We remove the white background, add a new TagCollectionViewCell class and specify this as the cell’s class.

Prettier Cells

Instead of relying on iOS to fill a rectangle with the cell’s background color, instead we want to have a rounded rect around our labels.
- (void)drawRect:(CGRect)rect
{
   // inset by half line width to avoid cropping where line touches frame edges
   CGRect insetRect = CGRectInset(rect, 0.5, 0.5);
   UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:rect.size.height/2.0];
 
   // white background
   [[UIColor whiteColor] setFill];
   [path fill];
 
   // red outline
   [[UIColor redColor] setStroke];
   [path stroke];
}
Note: You need to set the cell’s contentMode to redraw because otherwise a modification of its size would not trigger a redraw of this good looking background.
This results in the following look, with parts of the black background shining through. Note that we need to inset the outline drawing slightly because otherwise Quartz would clip the red circle on the outside sections where it touches the view’s frame.
New Background
The next challenge will be to size the cells appropriately for the current Label contents.

Individual Item Sizing

Of course we are not content with using 1) a static size for all items and b) want to have the size automatically adjust to the contents of our cells. Remember that we specified for the cell’s label to use the text size to size it. The edges of the background view are all distanced 5 points away from the edges of the label. So it would be awesome if there was a method to retrieve a cells actual size. Then it would be even more awesome to tell the collection view to use these individual sizes for the items.
A quick test in LLDB shows that the cell returns an intrinsicContentSize of -1, -1 which is the same as not defined. We add an outlet for the label to the TagCollectionViewCell’s header and then we find that conveniently the UILabel returns the size needed by the currently set text/font.
Since we know the amount of space around the constraints we can add that to the label’s intrinsic content size to derive the cell’s size.
// cache for margins configured via constraints in XIB
static CGSize _extraMargins = {0,0};
 
@implementation TagCollectionViewCell
 
- (CGSize)intrinsicContentSize
{
   CGSize size = [self.label intrinsicContentSize];
 
   if (CGSizeEqualToSize(_extraMargins, CGSizeZero))
   {
      // quick and dirty: get extra margins from constraints
      for (NSLayoutConstraint *constraint in self.constraints)
      {
         if (constraint.firstAttribute == NSLayoutAttributeBottom || constraint.firstAttribute == NSLayoutAttributeTop)
         {
            // vertical spacer
            _extraMargins.height += [constraint constant];
         }
         else if (constraint.firstAttribute == NSLayoutAttributeLeading || constraint.firstAttribute == NSLayoutAttributeTrailing)
         {
            // horizontal spacer
            _extraMargins.width += [constraint constant];
         }
      }
   }
 
   // add to intrinsic content size of label
   size.width += _extraMargins.width;
   size.height += _extraMargins.height;
 
   return size;
}
 
@end
This approach gets the actual margin widths from the constrains we have set in Interface Builder. This allows us to keep tweaking the amounts in IB without having to change some constant or #define in code. Since we don’t plan to modify any constraints, we’re lazily setting the static _extraMargins the first time this method is called, as those are constant.
Now the tricky part is dequeue a cell for reuse from the collection view to get the size. This is tricky because the method that calls our dataSource’s collectionView cellForItemAtIndexPath: is also calling collectionView:layout:sizeForItemAtIndexPath:. This prohibits us from dequeuing a cell in the latter method because that would lead to an endless loop.
Some people get their data from a model object and then have a class method on the cell to calculate the needed size. However this doesn’t make use of any constraints which we want to be able to configure in Interface Builder. The smartest solution I could come up with for this chicken and egg problem is to have a separate instance of one such cell as a template and then use its freshly overridden intrinsicContentSize method.
@implementation ViewController
{
   TagCollectionViewCell *_sizingCell;
}
 
- (void)viewDidLoad
{
   [super viewDidLoad];
 
   UINib *cellNib = [UINib nibWithNibName:@"TagCollectionViewCell" bundle:nil];
   [self.collectionView registerNib:cellNib forCellWithReuseIdentifier:@"TagCell"];
 
   // get a cell as template for sizing
   _sizingCell = [[cellNib instantiateWithOwner:nil options:nil] objectAtIndex:0];
}
 
#pragma mark - UICollectionView
 
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
   return 100;
}
 
- (void)_configureCell:(TagCollectionViewCell *)cell forIndexPath:(NSIndexPath *)indexPath
{
   if (indexPath.row%2)
   {
      cell.label.text = @"A";
   }
   else if (indexPath.row%3)
   {
      cell.label.text = @"longer";
   }
   else 
   {
      cell.label.text = @"much longer";
   }
}
 
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
   TagCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TagCell" forIndexPath:indexPath];
 
   [self _configureCell:cell forIndexPath:indexPath];
 
   return cell;
}
 
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
   [self _configureCell:_sizingCell forIndexPath:indexPath];
 
   return [_sizingCell intrinsicContentSize];
}
 
@end
Right after registering the NIB for use by the collection view I’m creating an instance of the cell and put it into my _sizingCell IVAR. To get an actual item’s cell we have a _configureCell:forIndexPath: method which we call on a dequeued reusable cell instance. For sizing we apply the same method on our sizing cell so that we get the correct intrinsicContentSize for that.

Once More With Auto Layout

When I asked my Tweeps about how I could ask a cell for how much size it would want based on the set constraints,Martin Pilkington was quick to point me towards  -systemLayoutSizeFittingSize:. At first I failed in its application, kept getting {0,0}. But after having written the above workaround I took a heart and tried it once more.
And it worked right away. All that’s needed is to replace this method:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
   [self _configureCell:_sizingCell forIndexPath:indexPath];
 
   return [_sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
}
This method determines the layout size that fits the passed size the closest. Two standard values are available, UILayoutFittingCompressedSize for getting the smallest size based on the layout constraints, UILayoutFittingExpandedSize to get the largest.
With this in please we can actually get rid of all the intrinsicContentSize stuff we wrote earlier. Also we can fully use the layout constraints to further restrict individual parts of our item cell. Say that you want the label to not go below a certain width for very short labels, and if the label gets too unwieldy then we would want to have the tail truncated.
Further Constrained
This results in the following result, proving that this is indeed what we want.
Custom Sizes
Isn’t that awesome? Let me stress that one more time: you do not need to calculate sizes in a cell class method, essentially duplicating the calculations auto layout would execute. You just ask the system .

Conclusion

In this blog post I have shown how you would create item cells for collections views coming from NIB files. An alternative – and in some aspects more convenient – method is to use the collection view in a storyboard. There the cell prototype would be positioned not in a separate XIB file, but right in the structure hierarchy of the collection view. Exploring how to get a sizing cell there is left as an exercise for the reader.
We found that UILabel conveniently exposes an intrinsicContentSize which is used by auto layout to size it if you specify that in Interface Builder. We explored a technique where we would use a cell instance as template for determine the optimal item size. Then we went one step further and employed a method that actually tells us the perfect size based on all layout constrains and intrinsic sizes.
The code for this example is available on the Cocoanetics Examples repo on GitHub.

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!

203 Favorite JavaScript Utilities

https://1loc.dev/