Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
}
like image 255
Brian Nezhad Avatar asked Aug 11 '15 21:08

Brian Nezhad


1 Answers

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**
like image 95
Brian Nezhad Avatar answered Nov 14 '22 15:11

Brian Nezhad