How to Use Socket in Swift (Connect, Send and Get Message)

how to use socket in swift (connect, send and get message)

I use the SwiftSocket https://github.com/swiftsocket/SwiftSocket for TCP connection. It is easy to use. For example for using it (i add comments to it):

I have my wrapper for work with this lib and this is the method how to send request and get response:

    private func blockingRequest(data: String, client: TCPClient?) -> (data: String?, error: ConnectionError?) {
// It use ufter connection
if client != nil {
// Send data
var (isSuccess, errorMessage) = client!.send(str: data)
if isSuccess {
// Read response data
var data = client!.read(1024*10)
if let d = data {
// Create String from response data
if let str = NSString(bytes: d, length: d.count, encoding: NSUTF8StringEncoding) as? String {
return (data: str, error: nil)
} else {
return (data: nil, error: ConnectionError.BadResponseData)
}
} else {
return (data: nil, error: ConnectionError.BadResponseData)
}
} else {
println(errorMessage)
return (data: nil, error: ConnectionError.RequestError)
}
} else {
return (data: nil, error: ConnectionError.RequestError)
}
}

I want to send a message to a particular client with socket.io in swift (IOS)?

You should configure it on server and on client.

Here my question from long ago but I'll explain it here too.

Each of your user connected you should gave it ID. Store it on database or whatever map could be use to. Remember ID must be unique like emails, userid or whatever unique.

Server.js

const sessionsMap = {}; // I save user ID here

io.on('connection', (socket) => {
socket.emit('askForUserId');

socket.on('userIdReceived', (userId) => {
sessionsMap[userId] = socket.id; // save the users on database or Object up to you
});

socket.on('send', (message) => {
const receiverId = sessionsMap[message.receiverId];
const messageData = message.data;
socket.broadcast.to(receiverId).emit('mymessage', messageData);
});
});

Client

Sorry I don't know swift but its same (I hope you get the point), servers handle the message send to who and came to who.

const userId = 'julia@mail.me'; // this should be unique

io.on('askForUserId', () => {
io.emit(userId); // your connected user ID save it.
});

io.on('mymessage', (message) => {
console.log('Yoho had message from', message.senderId)
console.log(message.text)
});

// send message to other people
io.emit('send', { text: 'Hellow', receiverId: 'john@doe.net', senderId: 'julia@mail.me' })

Swift 5: Socket IO integration and send data

Join Room specific press on emit data

self.socketIOClient.emit("join_id", with: "154") /*Like Random id generate*/
let jsonDic = ["room":driver_id,"userId":"\(userID)",
"quoteId":quote_id,"isRequest":"1"] /* Json Data send to server */
self.socketIOClient.emit("sendRequestToDriver",jsonDic)

Get json Respone

let manager:SocketManager!
let socketIOClient: SocketIOClient!
manager = SocketManager(socketURL: URL(string: ServerAPI.SocketURL)!,
config: [.log(true), .compress])
socketIOClient = manager.defaultSocket
self.socketIOClient?.on("orderRequestUpdateByDriver") { ( dataArray, ack) -> Void in
let dict = dataArray[0] as? NSDictionary ?? [:]
Print(dict)
}
socketIOClient.on(clientEvent: .error) { (data, eck) in
print(data)
print("socket error")
}

socketIOClient.on(clientEvent: .disconnect) { (data, eck) in
print(data)
print("socket disconnect")
}

socketIOClient.on(clientEvent: SocketClientEvent.reconnect) { (data, eck) in
print(data)
print("socket reconnect")
}
socketIOClient.connect()

how connect to socket in swift to read data and write it

You can use the SocketRocket ObjC framework bridged to Swift

https://github.com/square/SocketRocket

Then you just create an instance of the class and use it in Swift, i.e.:

var socket = SRWebSocket(...)
socket.open()
socket.send()
socket.close()

SocketRocket sources

As the user requested I'm attaching the screenshot of the bridging header prompt:
Sample Image

Also remember to link your app against libcucore.dylib:
Sample Image

Send message through Socket Outputstream from Swift Client to Java Server

In your Java code, you expect the first 4 bytes to be the length of the message, but in the Swift code, you didn't send the length of the message first. You sent the message itself directly. So now the Java code is left confused as to why the message is shorter than it expected, and waiting for more data to read.

You can try sending the number of bytes in data first:

let byteCountAsByteArray = withUnsafeBytes(of: Int32(data.count).bigEndian, Array.init)
out!.write(byteCountAsByteArray, maxLength: 4)
out!.write(pointer, maxLength: data.count)

I suspect that you are using a DataInputStream in your Java code, hence .bigEndian. But if you are reading this in a little endian way, use .littleEndian instead.

Get Response from Socket in Swift

Please check if you using any authentication in the socket or not. If you are using authentication you need to set the configuration in your swift code.
Here AUTHTOKEN which is user token you received from your login response from your server. AUTH_USER_ID which you received from your login response from your server.


class SocketIOManager: NSObject {

static let sharedInstance = SocketIOManager()

override init() {
super.init()
initialize()
}

func initialize() {

let socketUrl = "https://chatsapi.dummyUrl.io"

let specs: SocketIOClientConfiguration = [
.log(true),
.connectParams(getConnectionParam())]
socketManager = SocketManager(socketURL: URL(string: socketUrl)!, config: specs)
socket = socketManager?.defaultSocket
}

func getConnectionParam() -> [String: Any] {
return ["auth": "AUTHTOKEN", "user_id": "AUTH_USER_ID", "timeZone": "\(Date().localTimeZoneName)"]
}

func addHandlers() {
socket.on("get_all_user_list") { (data, ack) in
print("KK get_all_user_list", data,ack) // this line never gets called. I tried putting breakpoints.
}
}

func establishConnection() {
socket.connect()
}

func closeConnection() {
socket.disconnect()
}

}

How to connect Socket in Swift 4

You should make a shared property in your SocketIOManager class like this:

static let shared = SocketIOManager()

and then create init() like this:

    var socket: SocketIOClient!

// defaultNamespaceSocket and swiftSocket both share a single connection to the server
let manager = SocketManager(socketURL: URL(string: "http://localhost:3000")!, config: [.log(true), .compress])

override init() {
super.init()

socket = manager.defaultSocket
}

and finally write your methods like this:

func connectSocket() {
let token = UserDefaults.standard.getAccessToken()

self.manager.config = SocketIOClientConfiguration(
arrayLiteral: .connectParams(["token": token]), .secure(true)
)
socket.connect()
}

func receiveMsg() {
socket.on("new message here") { (dataArray, ack) in

print(dataArray.count)

}
}

and call your method like this:

SocketIOManager.shared.connectSocket()

The point is that you should make a strong reference to manager property in your viewController and static let shared = SocketIOManager() you do this for you!

Wait for message using socket.io and Swift 3

Result:

Sample Image

iOS Source Code:

Once you are connected to the server.

socket.on("connect") { data, ack in
self.socket.emit("get-notified-water", "")
}

You tell the server that you want to get notified about the water tank level.

self.socket.emit("get-notified-water", "")

And then you listen the notification here:

socket.on("notification-water") { data in
let levelTank = data.0.first as! Int
self.updateViewWithLevelTank(levelTank)
}

NOTE: Intentionally I didn't include updateViewWithLevelTank function implementation since I want you to be focus on the important part that is connecting with the server and being notified with the water tank level, besides I don't think it is complicated to mimic the UIView animation.



import UIKit
import SocketIO

class ViewController: UIViewController {
@IBOutlet weak var tankView: UIView!
let socket = SocketIOClient(socketURL: URL(string: "http://localhost:5000")!)

override func viewDidLoad() {
super.viewDidLoad()

socket.on("connect") { data, ack in
self.socket.emit("get-notified-water", "")
}

socket.on("notification-water") { data in
let levelTank = data.0.first as! Int
self.updateViewWithLevelTank(levelTank)
}

socket.connect()
}

}


Server Source code:

setInterval will call the anonymous function until levelTank is 100.

In which the the anonymous function, notifies the client (iOS socket) the levelTank every time is called.

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

var port = 5000;

io.on('connection', function(socket) {

socket.on('get-notified-water', function(data) {
var levelTank = 0;
var intervalTime = 500;

var intervalId = setInterval(function() {
levelTank += 10
socket.emit('notification-water', levelTank);

if (levelTank === 100) {
clearInterval(intervalId)
}
}, intervalTime);
});

});

http.listen(port, function() {
console.log('server up and running at %s port', port);
});

So let me know if this works for your case or you need any more clarification of my example, probably I've missed something who knows? ;)

SWIFT How to Send Data back to the ViewController from a SocketAPI Class?

Make your MessagingViewController a listener for the ChatServiceAPI. You can implement that like this:

Create a protocol like the following:

protocol MessageListener: AnyObject {
func messageReceived(text: String)
}

and make you controller conform to it:

extension MessagingViewController: MessageListener {
func messageReceived(text: String) {
// do whatever you need with the message
}
}

Then, in the ChatServiceAPI you can create a var named listeners:

private var listeners: [MessageListener] = []

and methods for adding and removing listeners:

func add(listener: MessageListener) {
self.listeners.append(listener)
}

func remove(listener: MessageListener) {
self.listeners.remove(listener)
}

For the last part, in your ChatServiceAPI, when the "msg" event is received, you need to send the message to all of your registered listeners. So, something like this:

socket!.on("msg") { (data, emitter) in
...
for listener in self.listeners {
listener.messageReceived(text: ...)
}
}

Now you also need to register your viewController as a listener. So you would call ChatServiceAPI.shared.add(listener: self) in your viewDidLoad.
Don't forget to also call ChatServiceAPI.shared.remove(listener: self) to prevent memory leaks.



Related Topics



Leave a reply



Submit