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
Related
Hello I am working on an application which can intercept the visited network traffic ip address list from iPhone (similar to Charles Proxy).
Currently I have written code to create a VPN profile using NETunnelProviderManager class.
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
guard let self = self else { return }
if self.manager != managers?.first {
self.manager = managers?.first
} else {
self.manager = self.makeManager()
}
self.manager?.saveToPreferences(completionHandler: { (error) in
if let error = error {
print("Error in saving VPN Config: \(error.localizedDescription)")
}
self.manager?.loadFromPreferences(completionHandler: { (error) in
if error == nil {
do {
try self.manager?.connection.startVPNTunnel()
} catch let vpnTunnelError {
print("Unable to start VPN Tunnel by error: \(vpnTunnelError.localizedDescription)")
}
} else {
print("Load From Preference Error")
}
})
})
}
}
private func makeManager() -> NETunnelProviderManager {
let manager = NETunnelProviderManager()
manager.localizedDescription = "NetworkTrafficVPN"
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = "com.NetworkTraffic.vpn-tunnel"
proto.serverAddress = "Network Traffic"
manager.protocolConfiguration = proto
let onDemandRule = NEOnDemandRuleConnect()
onDemandRule.interfaceTypeMatch = .any
manager.isOnDemandEnabled = true
manager.onDemandRules = [onDemandRule]
manager.isEnabled = true
return manager
}
Once I start the vpn tunnel (self.manager?.connection.startVPNTunnel()) from the created NETunnelProviderManager object
I get a callback in NEPacketTunnelProvider network extension method (startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void)).
I have created the NEPacketTunnelNetworkSettings inside startTunnel() method using the assigned IP address of the iPhone.
After calling the method self.setTunnelNetworkSettings(settings) { error in } I am getting network data callback inside
self.packetFlow.readPackets { (data, numbers) } method
class PacketTunnelProvider: NEPacketTunnelProvider {
// MARK: - Constants
let logger: Logger = Logger(subsystem: "NetworkTrafficSubsystem", category: "NetworkTrafficCategory")
let proxyServerPort: Int = 8888
let proxyServerAddress = "192.168.43.185" // Device assigned ip address
// MARK: - NEPacketTunnelProvider
override func startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void) {
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: proxyServerAddress)
settings.tunnelOverheadBytes = 80
settings.mtu = 1400
settings.ipv4Settings = NEIPv4Settings(addresses: [proxyServerAddress], subnetMasks: ["255.255.255.255"])
settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default()]
settings.ipv4Settings?.excludedRoutes = []
settings.dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "8.8.4.4"])
settings.dnsSettings?.matchDomains = []
self.setTunnelNetworkSettings(settings) { error in
if let e = error {
self.logger.info("Settings error")
completionHandler(e)
} else {
self.logger.info("Settings set without error")
self.readPacketObjects()
completionHandler(nil)
}
}
}
private func readPacketObjects() {
self.packetFlow.readPackets { (data, numbers) in
self.logger.info("Inside readPackets completionHandler")
// Way to print the ip address from the received data array
self.packetFlow.writePackets(data, withProtocols: numbers)
self.readPacketObjects()
}
}
}
But I am unable to decode the data object from self.packetFlow.readPackets { (data, numbers) in } method.
So my question is that in order to read the IP address of all the visited network traffic is this the correct approach or I need to do something else.
If this is correct approach then let me know how to decode the received Data object.
If not then please suggest the correct way to achieve this functionality.
Any help would be appreciated thanks.
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.
Im writing a iOS app which needs to send regular updates in the background after the user adds new data (Barcoder Scanner, scancodes). I can't find any ways to send the mail in background via SMTP and mailcore2 without problems and limitations.
I already tried this with:
Tried it with background fetch
https://developer.apple.com/documentation/uikit/core_app/managing_your_app_s_life_cycle/preparing_your_app_to_run_in_the_background/updating_your_app_with_background_app_refresh
But this is very irregular and it takes some time until it triggers.
In AppDelegate.swift:
func applicationDidEnterBackground(_ application: UIApplication) {
BackgroundTask.run(application: application) { [...] }
}
But then the data is only send if i close/minimize the app and if the BackgroundTask is not complete the app will freeze and I receive this error: XPC connection interrupted
I also run into problems because I need to wait for the sendOperation to return but as this is async I build a workaround to keep the Thread running and processing my "if success else ..." afterwards. More in the full code:
typealias CompletionHandler = (Error?) -> Void
/// Provides syncronous access to results returned by
/// asynchronous processes with completion handlers
class SyncMaker {
var result: Error? = nil
/// Generates a synchronous-compliant completion handler
func completion() -> CompletionHandler{
return {
(error: Error?) in
// Store result, return control
self.result = error
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
// Perform task (that must use custom completion handler) and wait
func run(_ task: #escaping () -> Void) -> Error? {
task()
CFRunLoopRun()
return result
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
BackgroundTask.run(application: application) { backgroundTask in
if (scanManager.hasDataToSend()) {
let smtpSession = MCOSMTPSession()
let settings: Settings = scanManager.getSettings()
smtpSession.hostname = settings.credMailServer
smtpSession.username = settings.credMailSource
print(settings.credMailSource)
smtpSession.password = settings.credMailPassword
smtpSession.port = UInt32(settings.credMailPort)
[…] Setting auth and connection typ
smtpSession.isCheckCertificateEnabled = false
smtpSession.timeout = 100
smtpSession.connectionLogger = {(connectionID, type, data) in
if data != nil {
if let string = NSString(data: data!, encoding: String.Encoding.utf8.rawValue){
NSLog("Connectionlogger: \(string)")
}
}
}
let builder = MCOMessageBuilder()
builder.header.to = [MCOAddress(displayName: settings.credMailDest, mailbox: settings.credMailDest)!]
builder.header.from = MCOAddress(displayName: settings.credMailSource, mailbox: settings.credMailSource)
builder.header.subject = "ScanLMS"
builder.htmlBody = ""
guard let attachment = MCOAttachment(data: scanManager.getSendData().data(using: .ascii), filename: "scans.txt") else {
print("Cant init attachment!")
backgroundTask.end()
return
}
attachment.mimeType = "text/plain"
builder.addAttachment(attachment)
let rfc822Data = builder.data()
let sendOperation = smtpSession.sendOperation(with: rfc822Data!)
var sendingError: Bool = true
print("Trying to send mail...")
if (sendOperation != nil) {
print("Starting sendOperation...")
let syncMaker = SyncMaker() //full class on top of code
let result = syncMaker.run {
sendOperation?.start(
syncMaker.completion())
}
if (result != nil) {
print("Error sending email: \(result!)")
} else {
sendingError = false
print("Successfully sent email!")
}
} else {
print("Cant init sendOperation")
sendingError = true
}
if (sendingError) {
print("Error, returning")
} else {
print("Send done")
print("Updating scanManager with send data...")
scanManager.updateSendData()
print("Done, returning")
}
} else {
print("No new send data")
}
backgroundTask.end()
}
}
I lowered the smtpSession.timeout = 100 to 3 (seconds) and now its not blocking the UI anymore. More a hack then a solution, but it works.
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)
}
})
}
}
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)
}
}