How to perform UI actions from Socket.io On Function - ios

I am new to using socket.io. I have made a singleton class for socket works and I am using an Instance of it in my app.
My question is: How to perform any action after receiving the data in the On function when I am on a different view controller?
import Foundation
import SocketIO
import UIKit
protocol testUI {
func fareUpdate(amount:NSNumber)
}
var delegate:testUI?
class SocketHelper {
static let shared = SocketHelper()
var socket: SocketIOClient!
let manager = SocketManager(socketURL: URL(string: "http://13.59.81.136:3355/")!, config: [.log(true), .compress, .connectParams(["token":user.shared.token])] )
private init() {
socket = manager.defaultSocket
}
func connectSocket(completion: #escaping(Bool) -> () ) {
disconnectSocket()
// socket.on(clientEvent: .connect) {[weak self] (data, ack) in
// print("socket connected")
// self?.socket.removeAllHandlers()
// completion(true)
// }
socket.connect()
}
func disconnectSocket() {
socket.removeAllHandlers()
socket.disconnect()
print("socket Disconnected")
}
func testStatus(){
print(socket.status)
}
func checkConnection() -> Bool {
if socket.manager?.status == .connected {
return true
}
return false
}
enum Events {
case search
func getFareEstimate(params:NSDictionary){
SocketHelper.shared.socket.emit("fareEstimate" , params)
}
func listen(completion: #escaping (Any) -> Void) {
SocketHelper.shared.socket!.on("rideFares") { (response, emitter) in
print(response)
let dic = response as! NSArray
let data = dic[0] as! NSDictionary
let fare = data["fare"] as! NSNumber
DispatchQueue.main.async {
delegate?.fareUpdate(amount: fare)
}
}
// func off() {
// SocketHelper.shared.socket.off(listnerName)
// }
}
}
}
I have tried using a delegate but it doesn't seem to work. I could be on multiple view controllers when the socket receives the data in the On function.

Related

Network Extension didn't called

I am trying to connect VPN using OpenVPNAdapter but the PacketTunnelProvider isn't called from the controller. What am i missing here?
Controller.swift
import NetworkExtension
var providerManager: NETunnelProviderManager!
var provider = PacketTunnelProvider()
override func viewDidLoad() {
super.viewDidLoad()
self.loadProviderManager {
self.configureVPN(response: self.arrResponse[0], serverAddress: self.arrResponse[0].iP, username: "vpn", password: "vpn")
}
}
func loadProviderManager(completion:#escaping () -> Void) {
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
if error == nil {
self.providerManager = managers?.first ?? NETunnelProviderManager()
completion()
}
}
}
func configureVPN(response:Response,serverAddress: String, username: String, password: String) {
let data = Data(base64Encoded:response.openVPNConfigDataBase64, options: .ignoreUnknownCharacters)
print(data!)
let decodedString = String(data: data!, encoding: .utf8)!
print(decodedString)
self.providerManager?.loadFromPreferences { error in
if error == nil {
let tunnelProtocol = NETunnelProviderProtocol()
tunnelProtocol.username = username
tunnelProtocol.serverAddress = serverAddress
tunnelProtocol.providerBundleIdentifier = "***.*****.********.***********.********"
tunnelProtocol.providerConfiguration = ["ovpn": data!, "username": username, "password": password]
tunnelProtocol.disconnectOnSleep = false
self.providerManager.protocolConfiguration = tunnelProtocol
self.providerManager.localizedDescription = "SMVPN"
self.providerManager.isEnabled = true
self.providerManager.saveToPreferences(completionHandler: { (error) in
if error == nil {
self.providerManager.loadFromPreferences(completionHandler: { (error) in
if error == nil {
self.provider.startTunnel(options: nil) { error in //this called here not in network extension
if error != nil {
print(error!)
}else {
}
}
}else {
print(error!.localizedDescription)
}
})
}
})
}
}
}
Project Entitlement
PacketTunnelProvider.swift
import NetworkExtension
import OpenVPNAdapter
class PacketTunnelProvider: NEPacketTunnelProvider {
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
var vpnReachability = OpenVPNReachability()
var configuration: OpenVPNConfiguration!
var properties: OpenVPNConfigurationEvaluation!
var UDPSession: NWUDPSession!
var TCPConnection: NWTCPConnection!
lazy var vpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}()
override func startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void) {
// Add code here to start the process of connecting the tunnel.
guard
let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration
else {
fatalError()
}
guard let ovpnFileContent: Data = providerConfiguration["ovpn"] as? Data else { return }
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnFileContent
do {
properties = try vpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
configuration.tunPersist = true
if !properties.autologin {
if let username: String = providerConfiguration["username"] as? String, let password: String = providerConfiguration["password"] as? String {
let credentials = OpenVPNCredentials()
credentials.username = username
credentials.password = password
do {
try vpnAdapter.provide(credentials: credentials)
} catch {
completionHandler(error)
return
}
}
}
vpnReachability.startTracking { [weak self] status in
guard status != .notReachable else { return }
self?.vpnAdapter.reconnect(afterTimeInterval: 5)
}
startHandler = completionHandler
vpnAdapter.connect(using: self)
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
// Add code here to start the process of stopping the tunnel.
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
vpnAdapter.disconnect()
completionHandler()
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
// Add code here to handle the message.
if let handler = completionHandler {
handler(messageData)
}
}
override func sleep(completionHandler: #escaping () -> Void) {
// Add code here to get ready to sleep.
completionHandler()
}
override func wake() {
// Add code here to wake up.
}
}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: #escaping (Error?) -> Void) {
setTunnelNetworkSettings(networkSettings) { (error) in
completionHandler(error == nil ? self.packetFlow as? Error : nil)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: #escaping (OpenVPNAdapterPacketFlow?) -> Void) {
setTunnelNetworkSettings(networkSettings) { (error) in
completionHandler(error == nil ? self.packetFlow : nil)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?) {
switch event {
case .connected:
if reasserting {
reasserting = false
}
guard let startHandler = startHandler else { return }
startHandler(nil)
self.startHandler = nil
case .disconnected:
guard let stopHandler = stopHandler else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
stopHandler()
self.stopHandler = nil
case .reconnecting:
reasserting = true
default:
break
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, fatal == true else {
return
}
NSLog("Error: \(error.localizedDescription)")
NSLog("Connection Info: \(vpnAdapter.connectionInformation.debugDescription)")
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
if let startHandler = startHandler {
startHandler(error)
self.startHandler = nil
} else {
cancelTunnelWithError(error)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
NSLog("Log: \(logMessage)")
}
}
extension PacketTunnelProvider: OpenVPNAdapterPacketFlow {
func readPackets(completionHandler: #escaping ([Data], [NSNumber]) -> Void) {
packetFlow.readPackets(completionHandler: completionHandler)
}
func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool {
return packetFlow.writePackets(packets, withProtocols: protocols)
}
}
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
Extension Entitlement
self.provider.startTunnel(options: nil) { error in // This is called here, not in network extension
This method is called from controller but didn't get called in network extension.
I posted all my code, so if I missed something, then please let me know. Any help is appreciated.
I found this question but I haven't figured it out yet.
PacketTunnelProvider network extension not called Swift 3
You haven't mentioned anything about how you are testing network extension. You should attach a Network extension process before running your project for debugging. Then only network extension methods will trigger for debug.
Finally i found my mistake it's i tried to called network extension but it will never called. so i called NETunnelProviderManager directly and it will work.
self.providerManager.loadFromPreferences(completionHandler: { (error) in
if error == nil {
do {
try self.providerManager.connection.startVPNTunnel()
} catch let error {
print(error.localizedDescription)
}
}else {
print(error!.localizedDescription)
}
})

connect socketIO with options in swift

I am trying to use socketIO for one of my projects.
I need to use following options while connecting to my socketIO server.
let socketConfig = SocketIOClientOption.self
socketConfig.forceNew(true)
socketConfig.reconnects(true)
socketConfig.reconnectAttempts(10)
socketConfig.reconnectWait(6000)
According to socketIO docs (https://github.com/socketio/socket.io-client-swift) I am not able to find a way to pass SocketIOOptions while connecting.
I have been trying to use below code to connect but it fails due to the absence of these options
let manager = SocketManager(socketURL: URL(string: "myurl:9476")!, config: [.log(true), .connectParams(["key":"value"])])
let socket = manager.defaultSocket
SocketIO version used:- 'Socket.IO-Client-Swift', '~> 13.3.0'
I figured out the solution.
The way to include options has been revised in the latest version.
I did the following and it worked:-
manager = SocketManager(socketURL: URL(string:"myurl:123")!, config: [.log(true), .forceNew(true), .reconnectAttempts(10), .reconnectWait(6000), .connectParams(["key":"value"]), .forceWebsockets(true), .compress])
socket = manager?.defaultSocket
Socket singleton class in Swift 4
import Foundation
import UIKit
import SwiftyJSON
import Alamofire
import SocketIO
import ObjectMapper
typealias OrderEventResponseBlock = (_ response : Any? , _ type : OrderEventType) -> ()
typealias TrackResponseBlock = (_ response : Any?) -> ()
class SocketIOManager: NSObject {
static let shared = SocketIOManager()
private var manager: SocketManager?
var socket: SocketIOClient?
override init() {
super.init()
let token = UDSingleton.shared.userData?.userDetails?.accessToken
guard let URL = URL(string: APIBasePath.basePath) else {return}
manager = SocketManager(socketURL: URL , config: [.log(true), .connectParams(["access_token" : /token])])
socket = manager?.defaultSocket
setupListeners()
}
//Server Methods
func establishConnection() {
let token = UDSingleton.shared.userData?.userDetails?.accessToken
if (self.socket?.status == .disconnected || self.socket?.status == .notConnected ) {
if (token != nil || token != "") {
socket?.connect()
}
}
else {
debugPrint("======= Socket already connected =======")
}
}
func closeConnection() {
debugPrint("=======***** SocketClientEvent.disconnect called ****=======")
socket?.disconnect()
}
func setupListeners() {
socket?.on(SocketClientEvent.disconnect.rawValue) { [weak self] (array, emitter) in
debugPrint("======= SocketClientEvent.disconnect listener=======")
self?.establishConnection()
}
socket?.on(SocketClientEvent.error.rawValue) {[weak self] (array, emitter) in
debugPrint("======= SocketClientEvent.error =======")
self?.establishConnection()
}
socket?.on(SocketClientEvent.connect.rawValue) { (array, emitter) in
if self.socket?.status == .connected {
debugPrint("======= userauth after connected =======")
}
}
}
func getStatus() -> SocketIOStatus? {
guard let status = self.socket?.status else{ return nil }
return status
}
//MARK:- Listening Events
//MARK:-
func listenOrderEventConnected(_ completionHandler: #escaping OrderEventResponseBlock) {
socket?.on(SocketEvents.OrderEvent.rawValue) {(arrData, socketAck) in
guard let item = JSON(arrData[0]).dictionaryObject else {return}
guard let type = item["type"] as? String else{return}
guard let typeSocket : OrderEventType = OrderEventType(rawValue: type) else {return}
}
}
//MARK:- EmitterWithAcknowledge Events
func emitMapLocation(_ userData : [String: Any] , _ completionHandler: #escaping TrackResponseBlock) {
socket?.emitWithAck(SocketEvents.CommonEvent.rawValue , userData).timingOut(after: 4.0, callback: { (response) in
guard let item = JSON(response[0]).dictionaryObject else{return}
let json = JSON(item)
if json[APIConstants.statusCode.rawValue].stringValue == Validate.successCode.rawValue {
let objDriver = Mapper<ApiSucessData<DriverList>>().map(JSONObject: item)
completionHandler( objDriver?.object)
}
})
}
func getParticularOrder(_ userData : [String: Any] , _ completionHandler: #escaping TrackResponseBlock) {
socket?.emitWithAck(SocketEvents.CommonEvent.rawValue, userData).timingOut(after: 2.0, callback: { (response) in
let item = JSON(response[0]).dictionaryObject
let json = JSON(item)
if json[APIConstants.statusCode.rawValue].stringValue == Validate.successCode.rawValue {
let objOrder = Mapper<ApiSucessData<Order>>().map(JSONObject: item)
completionHandler( objOrder?.object)
}
})
}
}

Socket.io Swift client handler doesn't trigger

I have issue with soket.io client, and as I read here, you should define socket manager as a global variable so it's not released by ARC.
My code with that in mind:
let manager = SocketManager(socketURL: URL(string: Api.SOCKET)!, config: [.log(true), .path(Api.SOCKET_PATH)])
let socket: SocketIOClient?
....
override func viewDidLoad() {
super.viewDidLoad()
subscribeOnTransaction() { }
}
func subscribeOnTransaction() {
if self.socket == nil {
socket = manager.socket(forNamespace: Api.SOCKET_PATH)
subscribeToChangesChannel { }
socket?.on(clientEvent: .connect) { data, ack in
self.socket?.emit("transaction", self.sellOrder.id!)
}
self.socket?.on(clientEvent: .statusChange) {data, ack in
print(data)
}
self.socket?.connect()
}
}
func subscribeToChangesChannel(completion: #escaping () -> Void) {
if let socket = self.socket {
socket.on("transaction/\(self.sellOrder.id!)") { data, ack in
print(data, ack)
if data.count > 0 {
let rawResult = data[0]
if let result = rawResult as? [String: Any] {
let newRawValue = result["new_val"]
if let transaction = self.parseSocketResponse(newRawValue) {
self.openTransactionTrackingScreen(transaction)
completion()
}
}
}
}
}
}
But it doesn't call the handlers when I set breakpoints. Connection is established, everything's fine, with log turned on I get
Got polling response
LOG SocketEnginePolling: Got poll message:
What might be the problem? Any ideas? Will appreciate any help, thanks

How to use Namespace in SocketIOClient in swift

I am using latest version of SocketIOClient (13.1.1). How to emit or listen using namespace.
This should be enough :
class SocketIOManager: NSObject {
static let sharedInstance = SocketIOManager()
let manager = SocketManager(socketURL: URL(string: "")!, config: [.log(false), .compress, .forcePolling(false)])
var avaialableCallBack:(([Any]) -> Void)?
override init(){
super.init()
}
func establishConnection() {
let socket = manager.socket(forNamespace: "/consumer")
socket.on("connect") { (data, ack) -> Void in
print("socket connected",data,ack)
}
socket.on(clientEvent: .disconnect){data, ack in
print("socket disconnected")
}
socket.on("session-available") { (dataArr, ack) -> Void in
ack.with(true)
if let sessionAvailableCB = self.avaialableCallBack {
sessionAvailableCB(dataArr)
}
}
socket.connect()
}
func closeConnection() {
let socket = manager.socket(forNamespace: "/consumer")
socket.disconnect()
}
func emitMessage(message:String,data:[String:Any]){
let socket = manager.socket(forNamespace: "/consumer")
socket.emit(message,data)
}
func emitMessageWithAck(message:String,data:[String:Any]) -> OnAckCallback{
let socket = manager.socket(forNamespace: "/consumer")
return socket.emitWithAck(message, data)
}
}

Completion Handlers in swift 4

I have a problem I'm trying to wrap my head around relating to the use of completion handlers. I have 3 layers in my iOS program, the ViewController->Service->Networking. I need to load some data through API call from the view controller.
I have defined functions(completionHandlers) in the ViewController that should execute once the data request is complete and am comfortable when in implementing completion handlers when only two layers exists, but confused when in the following scenario:
DashboardViewController.swift
import UIKit
#IBDesignable class DashboardViewController: UIViewController {
#IBOutlet weak var stepCountController: ExpandedCardView!
var articles:[Article]?
let requestHandler = RequestHandler()
let dashboardService = DashboardService()
override func viewDidLoad() {
super.viewDidLoad()
dashboardService.getDashboardData(completionHandler: getDashboardDataCompletionHandler)
}
func getDashboardDataCompletionHandler(withData: DashboardDataRequest) {
print(withData)
}
}
DashboardService.swift
import Foundation
class DashboardService: GeneralService {
var requestHandler: DashboardRequestHandler
override init() {
requestHandler = DashboardRequestHandler()
super.init()
}
//this function should execute requestHandler.requestDashboardData(), and then execute convertDashboardData() with the result of previous get request
func getDashboardData(completionHandler: #escaping (DashboardDataRequest) -> Void) {
//on network call return
guard let url = URL(string: apiResourceList?.value(forKey: "GetDashboard") as! String) else { return }
requestHandler.requestDashboardData(url: url, completionHandler: convertDashboardData(completionHandler: completionHandler))
}
func convertDashboardData(completionHandler: (DashboardDataRequest) -> Void) {
//convert object to format acceptable by view
}
}
DashboardRequestHandler.swift
import Foundation
class DashboardRequestHandler: RequestHandler {
var dataTask: URLSessionDataTask?
func requestDashboardData(url: URL, completionHandler: #escaping (DashboardDataRequest) -> Void) {
dataTask?.cancel()
defaultSession.dataTask(with: url, completionHandler: {(data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else {
return
}
do {
let decodedJson = try JSONDecoder().decode(DashboardDataRequest.self, from: data)
completionHandler(decodedJson)
} catch let jsonError {
print(jsonError)
}
}).resume()
}
}
If you look at the comment in DashboardService.swift, my problem is quite obvious. I pass a completion handler from ViewController to Service and Service has its own completion handler that it passes to RequestHandler where the view controller completion handler (getDashboardDataCompletionHandler) should be executed after the Service completion handler (convertDashboardData())
Please help me in clarifying how to implement this. Am I making a design mistake by trying to chain completionHandlers like this or am I missing something obvious.
Thank you
--EDIT--
My Request Handler implementation is as follows:
import Foundation
class RequestHandler {
// let defaultSession = URLSession(configuration: .default)
var defaultSession: URLSession!
init() {
guard let path = Bundle.main.path(forResource: "Api", ofType: "plist") else {
print("Api.plist not found")
return
}
let apiResourceList = NSDictionary(contentsOfFile: path)
let config = URLSessionConfiguration.default
if let authToken = apiResourceList?.value(forKey: "AuthToken") {
config.httpAdditionalHeaders = ["Authorization": authToken]
}
defaultSession = URLSession(configuration: config)
}
}
In this case is more clear to use delegation, for example like this:
protocol DashboardServiceDelegate {
func didLoaded(_ viewModel: DashboardViewModel)
}
class DashboardViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
dashboardService.delegate = self
dashboardService.getDashboardData()
}
}
extension DashboardViewController: DashboardServiceDelegate {
func didLoaded(_ viewModel: DashboardViewModel) {
///
}
}
protocol DashboardRequestHandlerDelegate() {
func didLoaded(request: DashboardDataRequest)
}
class DashboardService: GeneralService {
private lazy var requestHandler: DashboardRequestHandler = { reqHandler in
reqHandler.delegate = self
return reqHandler
}(DashboardRequestHandler())
func getDashboardData() {
guard let url = ...
requestHandler.requestDashboardData(url: url)
}
}
extension DashboardService: DashboardRequestHandlerDelegate {
func didLoaded(request: DashboardDataRequest) {
let viewModel = convert(request)
delegate.didLoaded(viewModel)
}
}

Resources