how to Socket io with swift language - swift

Here is my socket io swift language code
and main.js file code also here
I have not properly idea why is not with socket io server.
let manager = SocketManager(socketURL: URL(string: "http://localhost:3000")!, config: [.log(true), .compress])
self.socket = manager.socket(forNamespace: "/")
establishSocketConnection()
func establishSocketConnection() {
self.socket.connect()
self.socket.on("connection") { ( dataArray, ack) -> Void in
print("connected to external server")
}
}
func cloesSocketConnection() {
self.socket.disconnect()
}
log:
2019-03-27 23:38:36.228040+0530 scoket[1693:38288] LOG SocketIOClient{/}: Handling event: statusChange with data: [connecting, 2]
2019-03-27 23:38:36.228241+0530 scoket[1693:38288] LOG SocketIOClient{/}: Joining namespace /
2019-03-27 23:38:36.228353+0530 scoket[1693:38288] LOG SocketManager: Tried connecting socket when engine isn't open. Connecting
2019-03-27 23:38:36.228437+0530 scoket[1693:38288] LOG SocketManager: Adding engine
2019-03-27 23:38:36.229083+0530 scoket[1693:38288] LOG SocketIOClient{/}: Adding handler for event: connection
2019-03-27 23:38:36.229115+0530 scoket[1693:38343] LOG SocketEngine: Starting engine. Server: http://localhost:3000
2019-03-27 23:38:36.229203+0530 scoket[1693:38343] LOG SocketEngine: Handshaking
2019-03-27 23:38:36.229239+0530 scoket[1693:38288] LOG SocketManager: Manager is being released
2019-03-27 23:38:36.232066+0530 scoket[1693:38343] LOG SocketEnginePolling: Doing polling GET http://localhost:3000/socket.io/?transport=polling&b64=1
2019-03-27 23:38:36.331457+0530 scoket[1693:38346] LOG SocketEnginePolling: Got polling response
2019-03-27 23:38:36.331618+0530 scoket[1693:38346] LOG SocketEnginePolling: Got poll message: 96:0{"sid":"yaMqmjKGkhok6TuqAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}2:40
2019-03-27 23:38:36.331884+0530 scoket[1693:38346] LOG SocketEngine: Got message: 0{"sid":"yaMqmjKGkhok6TuqAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}
2019-03-27 23:38:36.341490+0530 scoket[1693:38346] LOG SocketEngine: Got message: 40
2019-03-27 23:38:36.341726+0530 scoket[1693:38346] LOG SocketEngine: Writing poll: has data: false
2019-03-27 23:38:36.342051+0530 scoket[1693:38346] LOG SocketEnginePolling: Sending poll: as type: 2
2019-03-27 23:38:36.342505+0530 scoket[1693:38346] LOG SocketEnginePolling: Created POST string: 1:2
2019-03-27 23:38:36.342910+0530 scoket[1693:38346] LOG SocketEnginePolling: POSTing
2019-03-27 23:38:36.343202+0530 scoket[1693:38346] LOG SocketEngine: Engine is being released

your socket property it isn't true
your connection method must be like this:
func establishSocketConnection() {
let socket = manager.defaultSocket
self.socket.on("connection") { ( dataArray, ack) -> Void in
print("connected to external server")
}
}
and every time you want to create handler in socket (like socket.on or socket.emit)
you must create socket variable again.
hope to this help you.

Here is the latest swift 4.2 code
Connect with socket io server and get data in xcode
func ConnectToSocket() {
manager = SocketManager(socketURL: URL(string: "http://localhost:3000")!, config: [.log(true), .compress])
socketIOClient = manager.defaultSocket
socketIOClient.on(clientEvent: .connect) {data, ack in
print(data)
print("socket connected")
self.socketIOClient.emit("GetData", "dad")
}
socketIOClient.on("newChatMessage") { (dataArray, socketAck) -> Void in
print(dataArray)
}
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()
}

Use this SocketIOManger for communicate with socket and make changes according to your need.
class SocketIOManager: NSObject {
static let sharedInstance = SocketIOManager()
var manager : SocketManager?
var socket : SocketIOClient?
override init() {
super.init()
}
/**
Create socket connection.
- parameter completionHandler : Called when complition will occure.
- parameter connected : True when socket connected.
- returns: nil
*/
func createSocketConnection (completionHandler: #escaping (_ connected: Bool) -> Void) {
let token = UserDefaults.standard.string(forKey: ConstantsBroadcast.Access_Token)!
manager = SocketManager(socketURL: URL(string: ConstantsBroadcast.socketBaseUrl)!
,config: [.log(true), .connectParams(["token": token]) ])
socket = manager?.defaultSocket
socket?.on(clientEvent: .connect) { data, ack in
print("socket connected.")
completionHandler(true)
}
socket?.connect()
}
/**
Disconnect socket.
*/
func closeConnection() {
socket?.disconnect()
}
/**
Add new user to socket.
- parameter username: current user name.
- parameter userid: current user id.
- parameter image: current user profile image url.
- returns: nil
*/
func addUserToServer(username: String, userid: String, image: String) {
let dict = ["username" : username, "userId": userid, "profileImage":image] as [String : Any]
self.socket?.emit("adduser", dict) //adduser
}
/**
Add handler for total connected user.
- parameter completionHandler : Called when complition will occure.
- parameter userCount : Total number of connected users.
- returns: nil
*/
func userConnectionHandlers(completionHandler: #escaping (_ userCount: String) -> Void) {
socket?.on("updateconnecteduser") { data,ack in //updateconnecteduser
debugPrint("connected user : ",data)
if let snippet = data[0] as? NSDictionary {
completionHandler("\(String(describing: snippet.value(forKey: "viewers")!))")
}
}
}
/**
Send new message.
- parameter message: user text message.
- returns: nil
*/
func sendMessage(message :String){
if socket?.status == .connected {
self.socket?.emit("sendchat", ["message": message]) //sendchat
}
else{
print("socket not connected")
}
}
/**
Add handler for get new messages.
- parameter completionHandler : Called when complition will occure.
- parameter messageInfo : New message data.
- returns: nil
*/
func newMessageHandlers(completionHandler: #escaping (_ messageInfo: ChatItemModel) -> Void) {
socket?.on("updatechat") { data,ack in //updatechat
print(data)
var msgData = ChatItemModel()
if let chatData = data[0] as? NSDictionary {
if let mId = chatData.value(forKey: "chatCommentId") as? String{
msgData.ChatCommentId = mId
}
if let userId = chatData.value(forKey: "userId") as? String {
msgData.UserID = userId
}
if let username = chatData.value(forKey: "username") as? String{
msgData.UserName = username
}
if let image = chatData.value(forKey: "profileImage") as? String{
msgData.ProfileImage = image
}
if let message = chatData.value(forKey: "message") as? String{
msgData.Message = message
}
if let createdAt = chatData.value(forKey: "createdAt") as? String{
msgData.CreatedAT = createdAt
}
completionHandler(msgData)
}
}
}
/**
Block user.
- parameter userId: user id.
- returns: nil
*/
func blockUser(userId : String){
if socket?.status == .connected {
self.socket?.emit("blockuser", ["userId" : userId]) //blockuser
print("blockuser emmited : ", userId)
}
else{
print("socket not connected")
}
}
/**
Add handler for block user.
- parameter completionHandler : Called when complition will occure.
- parameter userId : Blocked user id.
- returns: nil
*/
func blockUserHandler(completionHandler: #escaping (_ status : Int, _ userId: String) -> Void) {
socket?.on("updateblockeduser") { data,ack in //updateblockeduser
print("blcked userId : ",data)
if let blockedItem = data[0] as? NSDictionary {
if let status = blockedItem.value(forKey: "status") as? Int{
if let userId = blockedItem.value(forKey: "userId") as? String{
completionHandler(status, userId)
}
}
}
}
}
}

Related

Swift UDP Connection Refused

func send(_ payload: Data) {
connection!.send(content: payload, completion: .contentProcessed({ sendError in
if let error = sendError {
NSLog("Unable to process and send the data: \(error)")
} else {
NSLog("Data has been sent")
connection!.receiveMessage { (data, context, isComplete, error) in
if(isComplete){
guard let myData = data else { return }
NSLog("Received message: " + String(decoding: myData, as: UTF8.self))
}else{
NSLog("XYITA")
}
}
}
}))
}
So, I got this send function, connection works fine, connection.send works fine, but connection.receiveMessage throws strange error, which I cannot find how to resolve.
Error:
[connection] nw_socket_get_input_frames [C1:1] recvmsg(fd 17, 9216 bytes) [61: Connection refused]
Any suggestions?

How to create a chat app using TCP in swift

I simply wants to pass a string from one phone to another (vice versa) using TCP connection. Hence, I have a few questions as I'm a bit confused.
Can this be achieved using two different simulators in two different mac machines ?
Can this be achieved using two different simulators in the same mac machine ?
If this can be done on simulators would replacing the IP of the mac would work with 8080 port ?
My partially completed code as bellow;
class NWTCPConnection: NSObject {
var connection: NWConnection!
func connectToTcp() {
let PORT: NWEndpoint.Port = 8080
let ipAddress :NWEndpoint.Host = "192.168.8.133" //Machines IP
let queue = DispatchQueue(label: "TCP Client Queue")
let tcp = NWProtocolTCP.Options.init()
tcp.noDelay = true
let params = NWParameters.init(tls: nil, tcp: tcp)
connection = NWConnection(to: NWEndpoint.hostPort(host: ipAddress, port: PORT), using: params)
connection.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
print("Socket State: Ready")
UserDefaults.standard.set(true, forKey: "isConnected")
self.sendMSG()
self.receive()
default:
UserDefaults.standard.set(false, forKey: "isConnected")
break
}
}
connection.start(queue: queue)
}
func sendMSG() {
print("send data")
let message1 = "hello world"
let content: Data = message1.data(using: .utf8)!
connection.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to TCP destination ")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
})))
}
// How to trigger this ???
func receive() {
connection.receiveMessage { (data, context, isComplete, error) in
if (isComplete) {
print("Receive is complete, count bytes: \(data!.count)")
if (data != nil) {
print(data!)
} else {
print("Data == nil")
}
}
}
}
the moment I run the simulator and call the connectToTcp() I get the following error
nw_socket_handle_socket_event [C1:1] Socket SO_ERROR [61: Connection refused]

Swift NWConnection with Discord IPC (protocol error)

I try to connect with Discord using IPC, but whatever i try, the error message stays the same Optional("\u{02}\0\0\0(\0\0\0{"code":1003,"message":"protocol error"}")
I started with the encode function, but that is UInt8 because Swift apparently does not support an UInt32 data object.
Then i tried to create an UInt32 (memory) array, to convert that to Data, that failed as well.
can someone point me into the direction where I make a mistake.
import Foundation
import Cocoa
import Network
class Discord {
enum opcode: UInt32 {
case handshake = 0
case frame = 1
case close = 2
case ping = 3
case pong = 4
}
var appID: String
private var connection: NWConnection?
private let endpoint: String = NSTemporaryDirectory() + "discord-ipc-0"
init(appID: String) {
self.appID = appID
}
func connect() {
print("Connecting to \(endpoint)")
connection = NWConnection(
to: NWEndpoint.unix(path: endpoint),
using: .tcp
)
connection?.stateUpdateHandler = { state in
switch state {
case .setup:
print("Setting up...")
case .preparing:
print("Prepairing...")
case .waiting(let error):
print("Waiting: \(error)")
case .ready:
print("Ready...")
case .failed(let error):
print("Failed: \(error)")
case .cancelled:
print("Cancelled :'(")
default:
break
}
}
connection?.receiveMessage { completeContent, contentContext, isComplete, error in
print(
String(data: completeContent ?? Data(), encoding: .utf8),
error
)
}
connection?.start(queue: .global())
}
func uint32encode(opcode: opcode, message string: String) -> Data {
let payload = string.data(using: .utf8)!
var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 8 + payload.count, alignment: 0)
defer { buffer.deallocate() }
buffer.copyBytes(from: payload)
buffer[8...] = buffer[..<payload.count]
buffer.storeBytes(of: opcode.rawValue, as: UInt32.self)
buffer.storeBytes(of: UInt32(payload.count), toByteOffset: 4, as: UInt32.self)
let uIntData = Data(bytes: &buffer, count: 8 + payload.count)
return uIntData
}
func encode(opcode: opcode, message string: String) -> Data {
let jsondata = string.data(using: .utf8)!
var data = Data()
data.append(UInt8(opcode.rawValue))
data.append(UInt8(jsondata.count))
data.append(contentsOf: [UInt8](jsondata))
/*
uint32 opcode (0 or 1)
uint32 length (length)
byte[length] jsonData (??)
*/
return data
}
func handshake() {
connect()
// We should say "hello", with opcode handshake
let hello = encode(opcode: .handshake, message: "{\"v\":1,\"client_id\":\"\(appID)\"}")
print("Sending \(String.init(data: hello, encoding: .utf8))")
connection?.send(
content: hello,
completion: .contentProcessed({ error in
print("Error:", error?.localizedDescription)
})
)
}
func handshakev2() {
connect()
// We should say "hello", with opcode handshake
let hello = uint32encode(opcode: .handshake, message: "{\"v\":1,\"client_id\":\"\(appID)\"}")
print("Sending (V2) \(String.init(data: hello, encoding: .utf8))")
connection?.send(
content: hello,
completion: .contentProcessed({ error in
print("Error (V2):", error?.localizedDescription)
})
)
}
}

getting error message from server during API call

I have an app where I used RxSwift for my networking by extending ObservableType this works well but the issue I am having now is when I make an API request and there is an error, I am unable to show the particular error message sent from the server. Now how can I get the particular error response sent from the server
extension ObservableType {
func convert<T: EVObject>(to observableType: T.Type) -> Observable<T> where E: DataRequest {
return self.flatMap({(request) -> Observable<T> in
let disposable = Disposables.create {
request.cancel()
}
return Observable<T>.create({observer -> Disposable in
request.validate().responseObject { (response: DataResponse<T>) in
switch response.result {
case .success(let value):
if !disposable.isDisposed {
observer.onNext(value)
observer.onCompleted()
}
case .failure(let error):
if !disposable.isDisposed {
observer.onError(NetworkingError(httpResponse: response.response,
networkData: response.data, baseError: error))
observer.onCompleted()
}
}
}
return disposable
})
})
}
}
let networkRetryPredicate: RetryPredicate = { error in
if let err = error as? NetworkingError, let response = err.httpResponse {
let code = response.statusCode
if code >= 400 && code < 600 {
return false
}
}
return true
}
// Use this struct to pass the response and data along with
// the error as alamofire does not do this automatically
public struct NetworkingError: Error {
let httpResponse: HTTPURLResponse?
let networkData: Data?
let baseError: Error
}
response from the server could be
{
"status" : "error",
"message" : " INSUFFICIENT_FUNDS"
}
or
{
"status" : "success",
"data" : " gghfgdgchf"
}
my response is handled like this
class MaxResponse<T: NSObject>: MaxResponseBase, EVGenericsKVC {
var data: T?
public func setGenericValue(_ value: AnyObject!, forUndefinedKey key: String) {
switch key {
case "data":
data = value as? T
default:
print("---> setGenericValue '\(value)' forUndefinedKey '\(key)' should be handled.")
}
}
public func getGenericType() -> NSObject {
return T()
}
}
the error is
return ApiClient.session.rx.request(urlRequest: MaxApiRouter.topupWall(userId: getUser()!.id!, data: body))
.convert(to: MaxResponse<Wall>.self)
In the official Alamofire docs it is mentioned that validate(), without any parameters:
Automatically validates status code within 200..<300 range, and that
the Content-Type header of the response matches the Accept header of
the request, if one is provided.
So if you do not include Alamofire's validate() you are saying that no matter the status code, if the request did get through, you will consider it successful, so that's why it shows nothing in the failure block.
However if you prefer to use it, yes, it will give you an ResponseValidationFailureReason error, but you still have access to the response.data. Try printing it, you should see the expected error response from the server:
if let responseData = response.data {
print(String(data: responseData, encoding: .utf8))
}

RxSwift renew Authentication Token

Hy I'm trying to come up with a solution using Moya and RxSwift that renews an Authentication token and retries the requests.
The problem is I have multiple requests going on at the same time, so lets say 10 requests fire while the Authentication token has expired, I will try to renew the token on all of them, and as soon as the first one renews the other ones will fail because they use a wrong token to renew.
What I would like to do is just build a queue (maybe) of requests and then retry those. Not sure if this is the best scenario for this.
This is what I have so far:
final class NetworkOnlineProvider {
fileprivate let database = DatabaseClient(database: DatabaseRealm()).database
fileprivate let provider: MoyaProvider<NetworkAPI>
init(endpointClosure: #escaping MoyaProvider<NetworkAPI>.EndpointClosure = MoyaProvider<NetworkAPI>.defaultEndpointMapping,
requestClosure: #escaping MoyaProvider<NetworkAPI>.RequestClosure = MoyaProvider<NetworkAPI>.defaultRequestMapping,
stubClosure: #escaping MoyaProvider<NetworkAPI>.StubClosure = MoyaProvider.neverStub,
manager: Manager = MoyaProvider<NetworkAPI>.defaultAlamofireManager(),
plugins: [PluginType] = [],
trackInflights: Bool = false) {
self.provider = MoyaProvider(endpointClosure: endpointClosure, requestClosure: requestClosure, stubClosure: stubClosure, manager: manager, plugins: plugins, trackInflights: trackInflights)
}
fileprivate func getJWTRenewRequest() -> Single<Response>? {
if let token = JWTManager.sharedInstance.token {
return provider.rx.request(.renew(token: token))
}
return nil
}
func tokenRequest() -> Single<String> {
let errorSingle = Single<String>.create { single in
single(.error(APIError.failure))
return Disposables.create()
}
let emptyJWTSingle = Single<String>.create { single in
single(.success(""))
return Disposables.create()
}
// Return if no token found
guard let appToken = JWTManager.sharedInstance.getJWT() else {
return refreshToken() ?? emptyJWTSingle
}
// If we have a valid token, just return it
if !appToken.hasTokenExpired {
return Single<String>.create { single in
single(.success(appToken.token))
return Disposables.create()
}
}
// Token has expired
let newTokenRequest = refreshToken()
return newTokenRequest ?? errorSingle
}
func refreshToken() -> Single<String>? {
return getJWTRenewRequest()?
.debug("Renewing JWT")
.filterSuccessfulStatusCodes()
.map { (response: Response) -> (token: String, expiration: Double) in
guard let json = try? JSON(data: response.data) else { throw RxError.unknown }
let success = json["success"]
guard
let jwt = success["jwt"].string,
let jwt_expiration = success["jwt_expiration"].double,
let valid_login = success["valid_login"].bool, valid_login
else { throw RxError.unknown }
return (token: jwt, expiration: jwt_expiration)
}
.do(onSuccess: { (token: String, expiration: Double) in
JWTManager.sharedInstance.save(token: JWT(token: token, expiration: String(expiration)))
})
.map { (token: String, expiration: Double) in
return token
}
.catchError { e -> Single<String> in
print("Failed to Renew JWT")
JWTManager.sharedInstance.delete()
UIApplication.shared.appDelegate.cleanPreviousContext(jwt: true)
let loginVC = UIStoryboard(storyboard: .login).instantiateViewController(vc: LoginViewController.self)
UIApplication.shared.appDelegate.window?.setRootViewController(UINavigationController(rootViewController: loginVC))
throw e
}
}
func request(_ target: NetworkAPI) -> Single<Response> {
let actualRequest = provider.rx.request(target)
if target.isAuthenticatedCall {
return tokenRequest().flatMap { _ in
actualRequest
}
}
return actualRequest
}
}
The solution is here: RxSwift and Retrying a Network Request Despite Having an Invalid Token
The key is to use flatMapFirst so you only make one request for the first 401 and ignore other 401s while that request is in flight.
The gist associated with the article includes unit tests proving it works.