Search code examples
iosswiftsocketsredisnsstream

Connecting to Redis server with NSStream in Swift


Hi everyone as the title mention I'm trying to send and receive data from my Redis server in swift language. I have done a lot of research and I can't come across a good answer about this topic, closest I have come to is NSStream or a few Github projects (Most of them with broken code), I been trying to create a solution for 3 days now, Please someone help.

Connection Requirement for Redis on Port 6379:

  • TCP
  • Telnet (My Favorite)

Problems:

  1. App Delegate Crash Thread 1: EXC_BAD_ACCESS(code=1, address=XXXXXXXX) SOMETIMES
  2. No data return

Class with Initialization (Redis): The Closest I could get to a level where I understand the procedure with NSStream, but again this doesn't print anything for return in my dialog and I can't figure out what's wrong.

class Redis: NSObject, NSStreamDelegate {

     //Intilizing Stream & Requirement
     var endPoint: CFString?
     var onPort: UInt32?
     var inputStream: NSInputStream?
     var outputStream: NSOutputStream?

Server Connection Function:

    func serverConnection(endPoint: CFString, onPort: UInt32){

        //Streams Init
        let Host: CFString = endPoint
        let Port: UInt32 = onPort
        var readStream: Unmanaged<CFReadStream>?
        var writeStream: Unmanaged<CFWriteStream>?

        //Bind Streams to Host and Port
        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, Host, Port, &readStream, &writeStream)

        //Cast CFStream to NSStreams
        inputStream = readStream!.takeRetainedValue()
        outputStream = writeStream!.takeRetainedValue()

        //Assign Delegate
        inputStream!.delegate = self
        outputStream!.delegate = self

        //Schadule Run-loop
        inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
        outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)

        //Open Connection
        inputStream!.open()
        outputStream!.open()


    }

Stream: Once the app lunch I get an App delegate error SOMETIMES

Thread 1: EXC_BAD_ACCESS(code=1, address=XXXXXXXX)

    func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
        if aStream === inputStream {
            switch eventCode {
            case NSStreamEvent.ErrorOccurred:
                //Print Available Errors
                print("Error: \(aStream.streamError?.description)")
                break
            case NSStreamEvent.OpenCompleted:
                //Connection Succeed
                print("Connection Complete \(aStream.description)")
                break
            case NSStreamEvent.HasBytesAvailable:
                //Server Respond
                var buffer = [UInt8](count: 8, repeatedValue: 0)
                while inputStream?.hasBytesAvailable != nil {
                    let result: Int = (inputStream?.read(&buffer, maxLength: buffer.count))!
                    print(result)
                    print(buffer)
                }
                break
            default:
                break

            }
        }

        if aStream === outputStream {
            switch eventCode {
            case NSStreamEvent.ErrorOccurred:
                //Print Available Errors
                print("Error: \(aStream.streamError?.description)")
                break
            case NSStreamEvent.OpenCompleted:
                //Connection Succeed
                print("Connection Complete \(aStream.description)")
                break
            case NSStreamEvent.HasSpaceAvailable:
                //Ready to Send more Dat
                print("HasSpaceAvailable \(aStream.description)")
                break
            default:
                break

            }
        }

    }

Server Test with Ping: the return should be PONG

func Ping(){
    let Command: NSString = NSString(format: "Ping /n", String(endPoint))
    let data: NSData = NSData(data: Command.dataUsingEncoding(NSUTF8StringEncoding)!)
    outputStream!.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
}

Solution

  • Thanks to GCDAsyncSocket, I was able to create a solution for Redis connection with Swift 2. I'm also working on Full set Framework on Github for Redis if anyone interested in downloading.

    Redis Class: You must include GCDAsyncSocketDelegate.

    import Foundation
    class Redis: NSObject,  GCDAsyncSocketDelegate {
    
        //Alloc GCDAsyncSocket
        var Socket: GCDAsyncSocket?
    
        /*============================================================
        // Server Open Connection
        ============================================================*/
        func server(endPoint: String, onPort: UInt16){
    
            //Check For Socket Condition
            if !(Socket != nil) {
    
                //Assign Delegeate to Self Queue
                Socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
    
            }
    
            var err: NSError?
    
            /*============================================================
            GCDAsyncSocket ConnectToHost Throw Error so you must handle 
            this with Try [Try!], do, Catch.
            ============================================================*/
    
            do{
                //Assign Function Constants
                try Socket!.connectToHost(endPoint, onPort: onPort)
            }catch {
                //Error
                print(err)
            }
    
            //Read Send Data
            Socket?.readDataWithTimeout(2, tag: 1)
        }
    
    
        //Server Confirmation
        func socket(sock: GCDAsyncSocket!, didConnectToHost host: String!, port: UInt16) {
            print("Connected to Redis!")
        }
    
        /*============================================================
        // Read Data From Redis Server [NSUTF8StringEncoding]
        ============================================================*/
    
        func socket(sock: GCDAsyncSocket!, didReadData data: NSData!, withTag tag: Int) {
            let Recieved: NSString = NSString(data: data, encoding: NSUTF8StringEncoding)!
            print(Recieved)
        }
    
        /*===============================================================
        // Send Command [I Will create Full SET and Upload it to Github]
        =================================================================*/
    
        func Command(Command: String){
            let request: String = Command + "\r\n"
            let data: NSData = request.dataUsingEncoding(NSUTF8StringEncoding)!
            Socket!.writeData(data, withTimeout: 1.0, tag: 0)
    
        }
    
    }
    

    Call the methods by creating a constant of class Redis.

     let redisServer = Redis()
     redisServer.server("XX.XX.XXX.XXX", onPort: 6379)
     redisServer.Command("Ping") //Return Should be **PONG**