GCDAsyncSocket for synchronous requests with Swift 1.2
Background:
I have been using it GCDAsyncSocket
in a project successfully with Swift 1.2 (via bridge header).
Now the problem is that it needs some kind of queue because the system it connects to can only process and return one command at a time.
So if it calls methods back like:
getSystemInfo() getSystemStatus()
Only the returned one is getSystemInfo()
returned via the delegate callback because the system was busy processing it, however it getSystemStatus()
was sent asynchronously
successfully but was not processed by the controller. I would like it to be able to make callbacks back and have them in the queue and process as soon as the controller has processed and returned the previous response - basically making the process synchronous.
Question:
As shown below in the code example below, the didConnectToHost
delegate callback, when it connects to the controller, it calls getSystemInfo()
, then getSystemStatus()
back, it should call getSystemStatus()
after it has received the system information results.
I've looked NSCondition
, NSOperation
even GCD, but I'm not sure what is the most elegant way to approach this. I don't want to put another queue processor in the mix as there is already a queue setup for GCDAsyncSocket
. What's the best, most elegant way to handle this?
Pseudo class code:
public class SendNet: NSObject, GCDAsyncSocketDelegate {
var socket:GCDAsyncSocket! = nil
func setupConnection(){
var error : NSError?
if (socket == nil) {
socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
} else {
socket.disconnect()
}
if (!socket.connectToHost(host, onPort: port, withTimeout: 5.0, error: &error)){
println("Error: \(error)")
} else {
println("Connecting...")
}
}
public func socket(socket : GCDAsyncSocket, didConnectToHost host:String, port p:UInt16) {
println("Connected to \(host) on port \(p).")
self.socket = socket
getSystemInfo()
getSystemStatus()
}
func send(msgBytes: [UInt8]) {
var msgData = NSData(bytes: msgBytes, length: msgBytes)
socket.writeData(msgData, withTimeout: -1.0, tag: 0)
socket.readDataWithTimeout(-1.0, tag: 0)
}
func getSystemInfo() {
var sendBytes:[UInt8] = [0x0, 0x1, 0x2, 0x3]
send(sendBytes)
}
func getSystemStatus() {
var sendBytes:[UInt8] = [0x4, 0x5, 0x6, 0x7]
send(sendBytes)
}
public func socket(socket : GCDAsyncSocket!, didReadData data:NSData!, withTag tag:Int){
var msgData = NSMutableData()
msgData.setData(data)
var msgType:UInt16 = 0
msgData.getBytes(&msgType, range: NSRange(location: 2,length: 1))
println(msgType)
}
}
Any suggestions would be great - thanks!
source to share
So I decided to use NSOperation for this.
Created a class file SyncRequest.swift with the following code:
import Foundation
class SyncRequest : NSOperation {
var socket:GCDAsyncSocket! = nil
var msgData:NSData! = nil
override var concurrent: Bool {
return false
}
override var asynchronous: Bool {
return false
}
private var _executing: Bool = false
override var executing: Bool {
get {
return _executing
}
set {
if (_executing != newValue) {
self.willChangeValueForKey("isExecuting")
_executing = newValue
self.didChangeValueForKey("isExecuting")
}
}
}
private var _finished: Bool = false;
override var finished: Bool {
get {
return _finished
}
set {
if (_finished != newValue) {
self.willChangeValueForKey("isFinished")
_finished = newValue
self.didChangeValueForKey("isFinished")
}
}
}
/// Complete the operation
func completeOperation() {
executing = false
finished = true
}
override func start() {
if (cancelled) {
finished = true
return
}
executing = true
main()
}
override func main() -> (){
println("starting...")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReadData:", name: "DidReadData", object: nil)
sendData()
}
func sendData() {
socket.writeData(msgData, withTimeout: -1.0, tag: 0)
println("Sending: \(msgData)")
socket.readDataWithTimeout(-1.0, tag: 0)
}
func didReadData(notif: NSNotification) {
println("Data Received!")
NSNotificationCenter.defaultCenter().removeObserver(self, name: "DidReadData", object: nil)
completeOperation()
}
}
Then, when I need to send something to the controller, I do the following:
// sync the request to the controller
let queue = NSOperationQueue() // sync request queue
let requestOperation = SyncRequest()
requestOperation.socket = socket // pass the socket to send through
requestOperation.msgData = msgData // pass the msgData to send
queue.maxConcurrentOperationCount = 1
queue.addOperation(requestOperation)
Remember to send an NSNotification when data comes in from where you are handling the "didReadData" GCDAsyncSocket delegate.
public func socket(socket : GCDAsyncSocket!, didReadData data:NSData!, withTag tag:Int){
...
NSNotificationCenter.defaultCenter().postNotificationName("DidReadData", object: data)
...
}
source to share