Sign in

Connecting to Socket using Swift 5

First, we need to create a Socket class:

class SocketServer: NSObject{

static private let HOST = "000.000.000.000"
static private let PORT: UInt32 = 3000

//Server action code
static public let SOME_ACTION = 100

private var inputStream: InputStream!
private var outputStream: OutputStream!

public var isOpen: Bool = false
weak var delegate: SocketDelegate?

override init(){
super.init()
}

//1
public func connect(){
//set up uninitialized socket streams
without automatic memory management
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?

//bind read and write socket streams
together and connect them to the socket of the host
CFStreamCreatePairWithSocketToHost(
kCFAllocatorDefault,
SocketServer.HOST as CFString,
SocketServer.PORT,
&readStream,
&writeStream)


//store retained references
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()

inputStream.delegate = self
//run a loop
inputStream.schedule(in: .current, forMode: .common)
outputStream.schedule(in: .current, forMode: .common)

//open flood gates
inputStream.open()
outputStream.open()

isOpen = true
}

//2
private func closeNetworkConnection(){
inputStream.close()
outputStream.close()
isOpen = false
}

//3
private func serverRequest(serverActionCode: UInt8, int: UInt8){
writeToOutputStream(int: serverActionCode)
writeToOutputStream(int: int)
}

//4
public func someAction(someParam: UInt8!){
serverRequest(serverActionCode: SocketServer.SOME_ACTION, int: someParam)
}
}

Actions in this file explained:

Second, write an extension to implement StreamDelegate:

extension SocketServer: StreamDelegate{

//Delegate method override
func stream(_ aStream: Stream, handle eventCode: Stream.Event){
switch eventCode{
case .hasBytesAvailable:
//inputStream has something to pass
let s = self.readStringFrom(stream: aStream as! InputStream)
self.closeNetworkConnection()
if let s = s{
self.delegate?.socketDataReceived(result: Data(s.utf8))
}else{
self.delegate?.receivedNil()
}
case .endEncountered:
print("end of inputStream")
case .errorOccurred:
print("error occured")
case .hasSpaceAvailable:
print("has space available")
case .openCompleted:
isOpen = true
print("open completed")
default:
print("StreamDelegate event")
}
}

//1
private func getBufferFrom(stream: InputStream, size: Int) -> UnsafeMutablePointer<UInt8> {
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: size)

while (stream.hasBytesAvailable) {
let numberOfBytesRead = self.inputStream.read(buffer, maxLength: size)
if numberOfBytesRead < 0, let error = stream.streamError {
print(error)
break
}
if numberOfBytesRead == 0{
//EOF
break
}
}
return buffer
}

//2
private func readDataFrom(stream: InputStream, size: Int) -> Data?{
let buffer = getBufferFrom(stream: stream, size: size)
return Data(bytes: buffer, count: size)
}

//3
private func readStringFrom(stream: InputStream, withSize: Int) -> String?{
let d = readDataFrom(stream: stream, size: withSize)!
return String(data: d, encoding: .utf8)
}

//4
private func readStringFrom(stream: InputStream) -> String?{
let len: Int = Int(Int32(readIntFrom(stream: inputStream)!))
return readStringFrom(stream: stream, withSize: len)
}

//5
private func readIntFrom(stream: InputStream) -> UInt32?{
let buffer = getBufferFrom(stream: stream, size: 4)
var int: UInt32 = 0
let data = NSData(bytes: buffer, length: 4)
data.getBytes(&int, length: 4)
int = UInt32(bigEndian: int)
buffer.deallocate()
return int
}

//6
private func writeToOutputStream(string: String){
let data = string.data(using: .utf8)!
data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
else {
print("Error joining chat")
return
}
outputStream.write(pointer, maxLength: data.count)
}
}

//7
private func writeToOutputStream(int: UInt8){
let data = int.data
data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
else {
print("Error joining chat")
return
}
outputStream.write(pointer, maxLength: data.count)
}
}
}

Methods:

Third, create a protocol:

protocol SocketDelegate: class{
//1
func socketDataReceived(result: Data?)

//2
func receivedNil()
}

Methods explanation:

Lastly, using SocketServer is as simple as:

class SomeClass: UIViewController{
var result: SomeObj
func viewDidLoad(){
let socket: SocketServer = SocketServer() socket.delegate = self
socket.connect()
socket.someAction(someParam: 5)
}
}
extension SomeClass: SocketDelegate{
func socketDataReceived(result: Data?){
if result == nil {return}
let result: SomeObj = processData(result) if let result: SomeObj = result{
self.result = result
}
}
func receivedNil(){}
}

That’s it, have fun programming, and feel free to contact me if anything goes wrong :)

Mobile Developer, Just a Christian-Atheist-Palestinian-Israeli-Arab-Gay kinda guy, just the type you could meet anytime. PierreJanineh.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store