Connecting to Redis server with NSStream in Swift
Asked Answered
O

1

9

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)
}
Otis answered 11/8, 2015 at 21:40 Comment(0)
O
3

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**
Otis answered 15/8, 2015 at 1:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.