1

I am hoping you can help me. I'm exceptionally new to Xcode and Swift but am trying to skill up fast. I am mixing the learning of the basic language with some real examples. I'm getting there but i'm struggling with a little project.

Basically, i'm writing a little sockets client that is making a connection to a sockets based server. After scouring the web, I finally found what I believe is the right approach - to make use of the NSStream functionality.

In my attached view controller code (which is attached to a basic view with a couple of labels), the code works, in that it connects and can receive data from the server; the println() call in the stream function works.

The problem I am having is that as soon as my application receives any data, it seems to block the UI. I do see the data in the All Output window, but my interface is no longer working. For example, I have a button (as part of the navigation controller), that works until I receive data...

I am using the following to create the server on my OS X machine:

nc -k -l 2444

... and here is my view controller code

import UIKit

class VCMainView: UIViewController, NSStreamDelegate{

    let serverAddress: String = "127.0.0.1"
    let serverPort: Int = 2444

    var inputStream: NSInputStream?
    var outputStream: NSOutputStream?

    @IBOutlet weak var lblScreenLabel1: UILabel!
    @IBOutlet weak var lblScreenLabel2: UILabel!

    override func viewDidLoad() {

        super.viewDidLoad()

        self.lblScreenLabel1.text = "Connecting"
        self.lblScreenLabel2.text = "Please Wait..."

        self.connectSocket()

    } // func

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    } // func

    func connectSocket() {

        NSStream.getStreamsToHostWithName(self.serverAddress, port: self.serverPort, inputStream: &self.inputStream, outputStream: &self.outputStream)

        if inputStream != nil && outputStream != nil {

            inputStream!.delegate = self
            outputStream!.delegate = self

            inputStream!.scheduleInRunLoop(.mainRunLoop(), forMode: NSDefaultRunLoopMode)
            outputStream!.scheduleInRunLoop(.mainRunLoop(), forMode: NSDefaultRunLoopMode)

            inputStream!.open()
            outputStream!.open()

        } // if

    } // func

    func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {

        switch (eventCode) {

        case NSStreamEvent.ErrorOccurred:
            break

        case NSStreamEvent.EndEncountered:
            break

        case NSStreamEvent.None:
            break

        case NSStreamEvent.HasBytesAvailable:

            var buffer = [UInt8](count: 4096, repeatedValue: 0)

            if ( aStream == inputStream) {

                let hasBytes = self.inputStream!.hasBytesAvailable

                while ( hasBytes ) {

                    var len = self.inputStream!.read(&buffer, maxLength: buffer.count)
                    if(len > 0){
                        var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
                        if ("" != output){

                            println(output)


                        } // if
                    } // if

                } // while

            } // if

            break
        case NSStreamEvent.allZeros:
            break

        case NSStreamEvent.OpenCompleted:
            break

        case NSStreamEvent.HasSpaceAvailable:
            break

        default:
            break

        } // switch

    } // func

} // class

If somebody could help point me in the right direction, I would really appreciate that...

Thanks in advance! Jon

Jon
  • 41
  • 4

1 Answers1

1

So it would appear that a few hours after posting my question, even though I searched prior to posting for hours that I figured out what I was doing wrong. I found my solution in the answer by Greg Parker in another post - Is calling read:maxLength: once for every NSStreamEventHasBytesAvailable correct?

My issue was that I was calling checking for HasBytesAvailable in a loop, this was causing the blocking. It turns out that if you don't read all of the available data (so have a buffer shorter than the data that is available), then the delegate will just get another event anyway. Removing this loop fixed my blocking issue. Here is my updated view controller class:

import UIKit

class VCMainView: UIViewController, NSStreamDelegate{

    let serverAddress: String = "127.0.0.1"
    let serverPort: Int = 2444

    var inputStream: NSInputStream?
    var outputStream: NSOutputStream?

    @IBOutlet weak var lblScreenLabel1: UILabel!
    @IBOutlet weak var lblScreenLabel2: UILabel!

    override func viewDidLoad() {

        super.viewDidLoad()

        self.lblScreenLabel1.text = "Connecting"
        self.lblScreenLabel2.text = "Please Wait..."

        self.connectSocket()

    } // func

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    } // func

    func connectSocket() {

        NSStream.getStreamsToHostWithName(self.serverAddress, port: self.serverPort, inputStream: &self.inputStream, outputStream: &self.outputStream)

        if inputStream != nil && outputStream != nil {

            inputStream!.delegate = self
            outputStream!.delegate = self

            inputStream!.scheduleInRunLoop(.mainRunLoop(), forMode: NSDefaultRunLoopMode)
            outputStream!.scheduleInRunLoop(.mainRunLoop(), forMode: NSDefaultRunLoopMode)

            inputStream!.open()
            outputStream!.open()

        } // if

    } // func

    func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {

        switch (eventCode) {

        case NSStreamEvent.ErrorOccurred:
            break

        case NSStreamEvent.EndEncountered:
            break

        case NSStreamEvent.None:
            break

        case NSStreamEvent.HasBytesAvailable:

            var buffer = [UInt8](count: 4096, repeatedValue: 0)

            if ( aStream == inputStream) {

                if ( self.inputStream!.hasBytesAvailable ) {

                    var bytesRead = self.inputStream!.read(&buffer, maxLength: 4096)
                    var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)

                    println(output)

                } // if

            } // if

            break
        case NSStreamEvent.allZeros:
            break

        case NSStreamEvent.OpenCompleted:
            break

        case NSStreamEvent.HasSpaceAvailable:
            break

        default:
            break

        } // switch

    } // func

} // class

Now, this isn't saying that the rest of my code is good, as I stated in my initial question, i'm learning Swift and am a total beginner. If anyone does see anything wrong, I would certainly welcome any feedback.

Thanks, Jon

Community
  • 1
  • 1
Jon
  • 41
  • 4