iOS simple TCP connection example

@Mohamad Chami's example was really great, tried to write a Swift version of it
GitHubLink

class SocketDataManager: NSObject, StreamDelegate {

    var readStream: Unmanaged<CFReadStream>?
    var writeStream: Unmanaged<CFWriteStream>?
    var inputStream: InputStream?
    var outputStream: OutputStream?
    var messages = [AnyHashable]()
    weak var uiPresenter :PresenterProtocol!

    init(with presenter:PresenterProtocol){

        self.uiPresenter = presenter
    }
    func connectWith(socket: DataSocket) {

        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (socket.ipAddress! as CFString), UInt32(socket.port), &readStream, &writeStream)
        messages = [AnyHashable]()
        open()
    }

    func disconnect(){

        close()
    }

    func open() {
        print("Opening streams.")
        outputStream = writeStream?.takeRetainedValue()
        inputStream = readStream?.takeRetainedValue()
        outputStream?.delegate = self
        inputStream?.delegate = self
        outputStream?.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
        inputStream?.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
        outputStream?.open()
        inputStream?.open()
    }

    func close() {
        print("Closing streams.")
        uiPresenter?.resetUIWithConnection(status: false)
        inputStream?.close()
        outputStream?.close()
        inputStream?.remove(from: RunLoop.current, forMode: .defaultRunLoopMode)
        outputStream?.remove(from: RunLoop.current, forMode: .defaultRunLoopMode)
        inputStream?.delegate = nil
        outputStream?.delegate = nil
        inputStream = nil
        outputStream = nil
    }

    func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
        print("stream event \(eventCode)")
        switch eventCode {
        case .openCompleted:
            uiPresenter?.resetUIWithConnection(status: true)
            print("Stream opened")
        case .hasBytesAvailable:
            if aStream == inputStream {
                var dataBuffer = Array<UInt8>(repeating: 0, count: 1024)
                var len: Int
                while (inputStream?.hasBytesAvailable)! {
                    len = (inputStream?.read(&dataBuffer, maxLength: 1024))!
                    if len > 0 {
                        let output = String(bytes: dataBuffer, encoding: .ascii)
                        if nil != output {
                            print("server said: \(output ?? "")")
                            messageReceived(message: output!)
                        }
                    }
                }
            }
        case .hasSpaceAvailable:
            print("Stream has space available now")
        case .errorOccurred:
            print("\(aStream.streamError?.localizedDescription ?? "")")
        case .endEncountered:
            aStream.close()
            aStream.remove(from: RunLoop.current, forMode: .defaultRunLoopMode)
            print("close stream")
            uiPresenter?.resetUIWithConnection(status: false)
        default:
            print("Unknown event")
        }
    }

    func messageReceived(message: String){

        uiPresenter?.update(message: "server said: \(message)")
        print(message)
    }

    func send(message: String){

        let response = "msg:\(message)"
        let buff = [UInt8](message.utf8)
        if let _ = response.data(using: .ascii) {
            outputStream?.write(buff, maxLength: buff.count)
        }

    }

}





class ViewController: UIViewController {

    var socketConnector:SocketDataManager!
    @IBOutlet weak var ipAddressField: UITextField!
    @IBOutlet weak var portField: UITextField!
    @IBOutlet weak var messageField: UITextField!
    @IBOutlet weak var messageHistoryView: UITextView!
    @IBOutlet weak var connectBtn: UIButton!
    @IBOutlet weak var sendBtn: UIButton!
    @IBOutlet weak var statusView: UIView!
    @IBOutlet weak var statusLabl: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        socketConnector = SocketDataManager(with: self)
        resetUIWithConnection(status: false)
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    @IBAction func connect(){
        //http://localhost:50694/
        guard let ipAddr = ipAddressField.text, let portVal = portField.text  else {
            return
        }
        let soc = DataSocket(ip: ipAddr, port: portVal)
        socketConnector.connectWith(socket: soc)

    }
    @IBAction func send(){
        guard let msg = messageField.text else {
            return
        }
        send(message: msg)
        messageField.text = ""
    }
    func send(message: String){

        socketConnector.send(message: message)
        update(message: "me:\(message)")
    }
}

extension ViewController: PresenterProtocol{

    func resetUIWithConnection(status: Bool){

        ipAddressField.isEnabled = !status
        portField.isEnabled = !status
        messageField.isEnabled = status
        connectBtn.isEnabled = !status
        sendBtn.isEnabled = status

        if (status){
            updateStatusViewWith(status: "Connected")
        }else{
            updateStatusViewWith(status: "Disconnected")
        }
    }
    func updateStatusViewWith(status: String){

        statusLabl.text = status
    }

    func update(message: String){

        if let text = messageHistoryView.text{
            let newText = """
            \(text)            
            \(message)
            """
            messageHistoryView.text = newText
        }else{
            let newText = """
            \(message)
            """
            messageHistoryView.text = newText
        }

        let myRange=NSMakeRange(messageHistoryView.text.count-1, 0);
        messageHistoryView.scrollRangeToVisible(myRange)


    }


}


struct DataSocket {

    let ipAddress: String!
    let port: Int!

    init(ip: String, port: String){        
        self.ipAddress = ip
        self.port      = Int(port)
    }
}

Sample


https://github.com/swiftsocket/SwiftSocket Swift Socket library provide you a simple interface for socket based connection. Refer to this link and below sample.

let client = TCPClient(address: "www.apple.com", port: 80)
switch client.connect(timeout: 1) {
  case .success:
    switch client.send(string: "GET / HTTP/1.0\n\n" ) {
      case .success:
        guard let data = client.read(1024*10) else { return }

        if let response = String(bytes: data, encoding: .utf8) {
          print(response)
        }
      case .failure(let error):
        print(error)
    }
  case .failure(let error):
    print(error)
}

Heading


Those who are unable to receive data at the server side:

It is perhaps due to data encoding mechanism. @Mohamad Chami's answer works fine after changing the data encoding mechanism at the sendMessage method as follows:

In his example, NSString is converted to NSData by:

 NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];

Change the NSASCIIStringEncoding to NSUTF8StringEncoding. It is because at server's side (Oracle database server in my case), the data is received in modified UTF-8 encoding.


SocketConnectionVC.h

#import <UIKit/UIKit.h>

@interface SocketConnectionVC : UIViewController<NSStreamDelegate>
{
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    NSInputStream   *inputStream;
    NSOutputStream  *outputStream;

    NSMutableArray  *messages;
}

@property (weak, nonatomic) IBOutlet UITextField *ipAddressText;
@property (weak, nonatomic) IBOutlet UITextField *portText;
@property (weak, nonatomic) IBOutlet UITextField *dataToSendText;
@property (weak, nonatomic) IBOutlet UITextView *dataRecievedTextView;
@property (weak, nonatomic) IBOutlet UILabel *connectedLabel;


@end

SocketConnectionVC.m

#import "SocketConnectionVC.h"

@interface SocketConnectionVC ()

@end

@implementation SocketConnectionVC

- (void)viewDidLoad {
    [super viewDidLoad];

    _connectedLabel.text = @"Disconnected";
}

- (IBAction) sendMessage {

    NSString *response  = [NSString stringWithFormat:@"msg:%@", _dataToSendText.text];
    NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
    [outputStream write:[data bytes] maxLength:[data length]];

}

- (void) messageReceived:(NSString *)message {

    [messages addObject:message];

    _dataRecievedTextView.text = message;
    NSLog(@"%@", message);
}

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {

    NSLog(@"stream event %lu", streamEvent);

    switch (streamEvent) {

        case NSStreamEventOpenCompleted:
            NSLog(@"Stream opened");
            _connectedLabel.text = @"Connected";
            break;
        case NSStreamEventHasBytesAvailable:

            if (theStream == inputStream)
            {
                uint8_t buffer[1024];
                NSInteger 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);
                            [self messageReceived:output];
                        }
                    }
                }
            }
            break;

        case NSStreamEventHasSpaceAvailable:
            NSLog(@"Stream has space available now");
            break;

        case NSStreamEventErrorOccurred:
             NSLog(@"%@",[theStream streamError].localizedDescription);
            break;

        case NSStreamEventEndEncountered:

            [theStream close];
            [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            _connectedLabel.text = @"Disconnected";
            NSLog(@"close stream");
            break;
        default:
            NSLog(@"Unknown event");
    }

}

- (IBAction)connectToServer:(id)sender {

    NSLog(@"Setting up connection to %@ : %i", _ipAddressText.text, [_portText.text intValue]);
    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef) _ipAddressText.text, [_portText.text intValue], &readStream, &writeStream);

    messages = [[NSMutableArray alloc] init];

    [self open];
}

- (IBAction)disconnect:(id)sender {

    [self close];
}

- (void)open {

    NSLog(@"Opening streams.");

    outputStream = (__bridge NSOutputStream *)writeStream;
    inputStream = (__bridge NSInputStream *)readStream;

    [outputStream setDelegate:self];
    [inputStream setDelegate:self];

    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    [outputStream open];
    [inputStream open];

    _connectedLabel.text = @"Connected";
}

- (void)close {
    NSLog(@"Closing streams.");
    [inputStream close];
    [outputStream close];
    [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream setDelegate:nil];
    [outputStream setDelegate:nil];
    inputStream = nil;
    outputStream = nil;

    _connectedLabel.text = @"Disconnected";
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

snapshot for ui of this SocketConnectionVC enter image description here

and Follow these steps

1- input the ip on ipAdress textfield
2- input the port on port textfield
3- press connect button
4- (make sure the ip address and port is correct and the open of stream is fine. you can show the status of stream on console of Xcode)
5- input data to send to server
6- press send button
7- you can show the received message from server on the text view above connect button

Tags:

Ios

Tcp