// // PusherService.swift // pusher_client // // Created by Romario Chinloy on 10/26/20. // import Flutter import PusherSwiftWithEncryption class PusherService: MChannel { static let CHANNEL_NAME = "com.github.chinloyal/pusher_client" static let EVENT_STREAM = "com.github.chinloyal/pusher_client_stream" static let LOG_TAG = "PusherClientPlugin" static let PRIVATE_PREFIX = "private-" static let PRIVATE_ENCRYPTED_PREFIX = "private-encrypted-" static let PRESENCE_PREFIX = "presence-" private var _pusherInstance: Pusher! private var bindedEvents = Dictionary() struct Utils { static var enableLogging = true static func debugLog(msg: String) { if(enableLogging) { debugPrint("D/\(PusherService.LOG_TAG): \(msg)") } } static func errorLog(msg: String) { if(enableLogging) { debugPrint("E/\(PusherService.LOG_TAG): \(msg)") } } } func register(messenger: FlutterBinaryMessenger) { let methodChannel = FlutterMethodChannel(name: PusherService.CHANNEL_NAME, binaryMessenger: messenger) methodChannel.setMethodCallHandler { (_ call:FlutterMethodCall, result:@escaping FlutterResult) in switch call.method { case "init": self.`init`(call, result: result) case "connect": self.connect(result: result) case "disconnect": self.disconnect(result: result) case "getSocketId": self.getSocketId(result: result) case "subscribe": self.subscribe(call, result: result) case "unsubscribe": self.unsubscribe(call, result: result) case "bind": self.bind(call, result: result) case "unbind": self.unbind(call, result: result) case "trigger": self.trigger(call, result: result) default: result(FlutterMethodNotImplemented) } } let eventChannel = FlutterEventChannel(name: PusherService.EVENT_STREAM, binaryMessenger: messenger) eventChannel.setStreamHandler(StreamHandler.default) } func `init`(_ call:FlutterMethodCall, result:@escaping FlutterResult) { do { let json = call.arguments as! String let pusherArgs: PusherArgs = try JSONDecoder().decode(PusherArgs.self, from: json.data(using: .utf8)!) Utils.enableLogging = pusherArgs.initArgs.enableLogging if(_pusherInstance == nil) { let pusherOptions = PusherClientOptions( authMethod: pusherArgs.pusherOptions.auth == nil ? .noMethod : AuthMethod.authRequestBuilder(authRequestBuilder: AuthRequestBuilder(pusherAuth: pusherArgs.pusherOptions.auth!)), host: pusherArgs.pusherOptions.cluster != nil ? .cluster(pusherArgs.pusherOptions.cluster!) : .host(pusherArgs.pusherOptions.host), port: pusherArgs.pusherOptions.encrypted ? pusherArgs.pusherOptions.wssPort : pusherArgs.pusherOptions.wsPort, useTLS: pusherArgs.pusherOptions.encrypted, activityTimeout: Double(pusherArgs.pusherOptions.activityTimeout) / 1000 ) _pusherInstance = Pusher(key: pusherArgs.appKey, options: pusherOptions) _pusherInstance.connection.reconnectAttemptsMax = pusherArgs.pusherOptions.maxReconnectionAttempts _pusherInstance.connection.maxReconnectGapInSeconds = Double(pusherArgs.pusherOptions.maxReconnectGapInSeconds) _pusherInstance.connection.pongResponseTimeoutInterval = Double(pusherArgs.pusherOptions.pongTimeout) / 1000 _pusherInstance.connection.delegate = ConnectionListener.default Utils.debugLog(msg: "Pusher initialized") result(nil) } } catch let err { Utils.errorLog(msg: err.localizedDescription) result(FlutterError(code: "INIT_ERROR", message: err.localizedDescription, details: err)) } } func connect(result:@escaping FlutterResult) { _pusherInstance.connect() result(nil) } func disconnect(result:@escaping FlutterResult) { _pusherInstance.disconnect() Utils.debugLog(msg: "Disconnect") result(nil) } func getSocketId(result:@escaping FlutterResult) { result(_pusherInstance.connection.socketId) } func subscribe(_ call:FlutterMethodCall, result:@escaping FlutterResult) { let channelMap = call.arguments as! [String: String] let channelName: String = channelMap["channelName"]! var channel: PusherChannel if(!channelName.starts(with: PusherService.PRESENCE_PREFIX)) { channel = _pusherInstance.subscribe(channelName) } else { channel = _pusherInstance.subscribeToPresenceChannel(channelName: channelName) for pEvent in Constants.PresenceEvents.allCases { channel.bind(eventName: pEvent.rawValue, eventCallback: ChannelEventListener.default.onEvent) } } for pEvent in Constants.Events.allCases { channel.bind(eventName: pEvent.rawValue, eventCallback: ChannelEventListener.default.onEvent) } Utils.debugLog(msg: "Subscribed: \(channelName)") result(nil) } func unsubscribe(_ call:FlutterMethodCall, result:@escaping FlutterResult) { let channelMap = call.arguments as! [String: String] let channelName: String = channelMap["channelName"]! _pusherInstance.unsubscribe(channelName) Utils.debugLog(msg: "Unsubscribed: \(channelName)") result(nil) } /** * Note binding can happen before the channel has been subscribed to */ func bind(_ call:FlutterMethodCall, result:@escaping FlutterResult) { let map = call.arguments as! [String: String] let channelName: String = map["channelName"]! let eventName: String = map["eventName"]! var channel: PusherChannel if(!channelName.starts(with: PusherService.PRESENCE_PREFIX)) { channel = _pusherInstance.connection.channels.find(name: channelName)! bindedEvents[channelName + eventName] = channel.bind(eventName: eventName, eventCallback: ChannelEventListener.default.onEvent) } else { channel = _pusherInstance.connection.channels.findPresence(name: channelName)! bindedEvents[channelName + eventName] = channel.bind(eventName: eventName, eventCallback: ChannelEventListener.default.onEvent) } Utils.debugLog(msg: "[BIND] \(eventName)") result(nil) } func unbind(_ call:FlutterMethodCall, result:@escaping FlutterResult) { let map = call.arguments as! [String: String] let channelName: String = map["channelName"]! let eventName: String = map["eventName"]! var channel: PusherChannel let callBackId = bindedEvents[channelName + eventName] if(callBackId != nil) { if(!channelName.starts(with: PusherService.PRESENCE_PREFIX)) { channel = _pusherInstance.connection.channels.find(name: channelName)! channel.unbind(eventName: eventName, callbackId: callBackId!) } else { channel = _pusherInstance.connection.channels.findPresence(name: channelName)! channel.unbind(eventName: eventName, callbackId: callBackId!) } } Utils.debugLog(msg: "[UNBIND] \(eventName)") result(nil) } func trigger(_ call:FlutterMethodCall, result:@escaping FlutterResult) { do { let json = call.arguments as! String let clientEvent: ClientEvent = try JSONDecoder().decode(ClientEvent.self, from: json.data(using: .utf8)!) let channelName = clientEvent.channelName let eventName = clientEvent.eventName let data: String = clientEvent.data ?? "" let errorMessage = "Trigger can only be called on private and presence channels." switch clientEvent.channelName { case _ where channelName.starts(with: PusherService.PRIVATE_ENCRYPTED_PREFIX): result(FlutterError(code: "TRIGGER_ERROR", message: errorMessage, details: nil)) case _ where channelName.starts(with: PusherService.PRIVATE_PREFIX): let channel: PusherChannel = _pusherInstance.connection.channels.find(name: channelName)! channel.trigger(eventName: eventName, data: data) case _ where channelName.starts(with: PusherService.PRESENCE_PREFIX): let channel: PusherPresenceChannel = _pusherInstance.connection.channels.findPresence(name: channelName)! channel.trigger(eventName: eventName, data: data) default: result(FlutterError(code: "TRIGGER_ERROR", message: errorMessage, details: nil)) } Utils.debugLog(msg: "[TRIGGER] \(eventName)") result(nil) } catch let err { Utils.errorLog(msg: err.localizedDescription) result(FlutterError(code: "TRIGGER_ERROR", message: err.localizedDescription, details: err)) } } }