WujoFefer WujoFefer - 6 months ago 48
Swift Question

TCP Stream and dispatch in iOS

I have to build an application which also will be able to receive and send data.
I need to create at least two threads. One to continuously listen and to respond to an interface and sending data.

var a = connectionClass()

@IBOutlet weak var textField: UITextField!
@IBAction func myButton(sender: AnyObject) {
a.sendData()
}

override func viewDidLoad() {
super.viewDidLoad()
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0), { ()->() in
print("gcd hello")
dispatch_async(dispatch_get_main_queue(), {
self.a.initNetworkCommunication()
print("hello from UI thread executed as dispatch")
})
})
print("hello from UI thread")
}


This code locks the user interface, however, it is receiving data.
I am looking for solutions, or example, where I could see the solution to my problem.

To create stream I used code from here: https://github.com/troligtvis/SwiftChatClient/blob/master/ChatClientSwift/ViewController.swift?

=========================================================================

Ok it's working. I can open connection and send data.
But now i don't know how to use function from other class to read stream.
How to create self.a.stream
stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent)
from when i don't know haw to use event and how to init NSString for aString.
I know, it's stupid question but for me it is complicated.

ViewControler:

import UIKit

class ViewController: UIViewController, NSStreamDelegate{

var a = connectionClass()

@IBOutlet weak var textField: UITextField!
@IBAction func myButton(sender: AnyObject) {
a.sendData()
}
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), { ()->() in
print("Inicjalizowanie")
self.a.initNetworkCommunication()

dispatch_async(dispatch_get_main_queue(), {
print("hello from UI thread executed as dispatch")
})
})
print("hello from UI thread")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}


}


connection class:

import Foundation

class connectionClass: NSObject, NSStreamDelegate {

override init(){

}

//deklaracja podstawowych zmiennych
private let serverAddress: CFString = "someIp"
private let serverPort: UInt32 = 10001

private var inputStream: NSInputStream!
private var outputStream: NSOutputStream!
var i = 0

//Inicjalizacja połączenia
func initNetworkCommunication() {

//zmienne potrzebne do pobierania i wysyłania danych
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?

//tworzy stream
autoreleasepool {
CFStreamCreatePairWithSocketToHost(nil, self.serverAddress, self.serverPort, &readStream, &writeStream)
}
self.inputStream = readStream!.takeRetainedValue()
self.outputStream = writeStream!.takeRetainedValue()

//delegaty w SWIFT - doczytać
self.inputStream.delegate = self
self.outputStream.delegate = self

//tworzy pętlę dla połączenia - doczytać
self.inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

self.inputStream.open()
self.outputStream.open()
print("Zainicjalizowane")

}

//co ta funkcja robi i gdzie jest używana. Czy ona oczekuje na wywołanie połączenia z serwerem?
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
print("a tu jest stream")
switch eventCode{
case NSStreamEvent.OpenCompleted:
print("Otwarto połączenie")
break
case NSStreamEvent.HasSpaceAvailable:
if outputStream == aStream{
print("Połączenie jest gotowe")
break
}
case NSStreamEvent.HasBytesAvailable:
print("Są ramki")
if aStream == inputStream{
var buffer: UInt8 = 0
var len: Int!

while (inputStream?.hasBytesAvailable != nil) {
len = inputStream?.read(&buffer, maxLength: 32)
if len > 0 {
let output = buffer

print("Server odpowiedział: \(output)")

}
}
}
break
case NSStreamEvent.ErrorOccurred:
print("Nie można połączyć się z serwerem!")
break
case NSStreamEvent.EndEncountered:
outputStream.close()
outputStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputStream = nil
default:
print("Nieznane działanie ")
}
}

func sendData() {

let data = NSData(bytes: [0x0F, 0x00, 0x10, 0x00, 0x00, 0x80, 0x55, 0x00, 0x55, 0x04, 0x00, 0x01, 0x00, 0x00, 0xB2, 0x04 ] as [UInt8], length: 16)

outputStream?.write(UnsafePointer<UInt8>(data.bytes) , maxLength: data.length)

print("wysłano: \(data)")

}


}

hnh hnh
Answer

First: You don't need threads to do async IO. The IO can be scheduled via GCD on the runloop. This is a good introduction: What's New in GCD.

Second: I'm not sure what you try to accomplish with your dispatch_async code section. It just runs the print in a secondary thread. Then you put your self.a.initNetworkCommunication back on the main thread ...

Third: Looking at the code from that ChatClientSwift - the a.sendData() seems to do a blocking write. Which, well, blocks ...

There are quite a few Swift GCD socket libraries available. This is one: SwiftSockets, there are more. You probably want to look at such.