diff --git a/ios/Sources/SocketsPlugin/Socket.swift b/ios/Sources/SocketsPlugin/Socket.swift new file mode 100644 index 0000000..34302af --- /dev/null +++ b/ios/Sources/SocketsPlugin/Socket.swift @@ -0,0 +1,91 @@ +import Foundation +import Network + +protocol SocketDelegate: AnyObject { + func didChangeState(socket: String, state: SocketState) + func didReceiveMessage(socket: String, message: String) +} + +public class Socket: NSObject { + + var id: String + var host: String + var port: Int + var useTLS: Bool + var acceptInvalidCertificates: Bool + + var connection: NWConnection? + + weak var delegate: SocketDelegate? + + public init(id: String, host: String, port: Int, useTLS: Bool, acceptInvalidCertificates: Bool) { + self.id = id + self.host = host + self.port = port + + self.useTLS = useTLS + self.acceptInvalidCertificates = acceptInvalidCertificates + } + + public func connect() { + let connection = NWConnection(host: NWEndpoint.Host(self.host), port: NWEndpoint.Port(String(self.port))!, using: .tcp) + connection.stateUpdateHandler = self.stateDidChange(to:) + self.receive(on: connection) + connection.start(queue: .main) + self.connection = connection + } + + public func write(_ message: String) { + if let data = message.data(using: .utf8) { + connection?.send(content: data, completion: .idempotent) + } + } + + public func disconnect() { + connection?.forceCancel() + } + + func stateDidChange(to state: NWConnection.State) { + switch state { + case .setup: + print("connection setup") + break + + case .waiting(let error): + print("connection waiting: \(error)") + break + + case .preparing: + self.delegate?.didChangeState(socket: self.id, state: .connecting) + break + + case .ready: + self.delegate?.didChangeState(socket: self.id, state: .connected) + break + + case .failed(let error): + print("connection failed: \(error)") + break + + case .cancelled: + self.delegate?.didChangeState(socket: self.id, state: .disconnected) + break + + default: + print("other") + break + } + } + + func receive(on connection: NWConnection) { + connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { (data, contentContext, isComplete, error) in + if let data = data, !data.isEmpty { + if let message = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) { + self.delegate?.didReceiveMessage(socket: self.id, message: message) + } + } + self.receive(on: connection) + } + } + +} diff --git a/ios/Sources/SocketsPlugin/Sockets.swift b/ios/Sources/SocketsPlugin/Sockets.swift index 750ade3..51f8efb 100644 --- a/ios/Sources/SocketsPlugin/Sockets.swift +++ b/ios/Sources/SocketsPlugin/Sockets.swift @@ -1,8 +1,65 @@ import Foundation -@objc public class Sockets: NSObject { - @objc public func echo(_ value: String) -> String { - print(value) - return value +enum SocketState:String { + case connecting = "connecting" + case connected = "connected" + case disconnected = "disconnected" +} + +@objc public class Sockets: NSObject, SocketDelegate { + + private var sockets: [Socket] = [] + + private weak var plugin: SocketsPlugin? + @objc public init(plugin: SocketsPlugin? = nil) { + self.plugin = plugin + } + + func didChangeState(socket: String, state: SocketState) { + self.plugin?.notifyListeners("state", data: [ + "id": socket, + "state": state.rawValue + ]) + } + + func didReceiveMessage(socket: String, message: String) { + self.plugin?.notifyListeners("message", data: [ + "id": socket, + "message": message + ]) + } + + @objc public func create(id: String, host: String, port: Int, useTLS: Bool = false, acceptInvalidCertificates: Bool = false) -> Socket { + let socket = Socket(id: id, host: host, port: port, useTLS: useTLS, acceptInvalidCertificates: acceptInvalidCertificates) + socket.delegate = self + sockets.append(socket) + return socket + } + + @objc public func connect(id: String) { + if let socket = self.socket(with: id) { + socket.connect() + } + } + + @objc public func send(id: String, message: String) { + if let socket = self.socket(with: id) { + socket.write(message) + } + } + + @objc public func disconnect(id: String) { + if let socket = self.socket(with: id) { + socket.disconnect() + } + } + + @objc func socket(with id: String) -> Socket? { + for socket in self.sockets { + if (socket.id == id) { + return socket + } + } + return nil } } diff --git a/ios/Sources/SocketsPlugin/SocketsPlugin.swift b/ios/Sources/SocketsPlugin/SocketsPlugin.swift index ecff892..9251e3f 100644 --- a/ios/Sources/SocketsPlugin/SocketsPlugin.swift +++ b/ios/Sources/SocketsPlugin/SocketsPlugin.swift @@ -1,23 +1,50 @@ import Foundation import Capacitor -/** - * Please read the Capacitor iOS Plugin Development Guide - * here: https://capacitorjs.com/docs/plugins/ios - */ @objc(SocketsPlugin) public class SocketsPlugin: CAPPlugin, CAPBridgedPlugin { + + private lazy var implementation: Sockets = { + return Sockets(plugin: self) + }() + public let identifier = "SocketsPlugin" public let jsName = "Sockets" public let pluginMethods: [CAPPluginMethod] = [ - CAPPluginMethod(name: "echo", returnType: CAPPluginReturnPromise) + CAPPluginMethod(name: "create", returnType: CAPPluginReturnPromise), + CAPPluginMethod(name: "connect", returnType: CAPPluginReturnPromise), + CAPPluginMethod(name: "send", returnType: CAPPluginReturnPromise), + CAPPluginMethod(name: "disconnect", returnType: CAPPluginReturnPromise) ] - private let implementation = Sockets() - @objc func echo(_ call: CAPPluginCall) { - let value = call.getString("value") ?? "" - call.resolve([ - "value": implementation.echo(value) - ]) + @objc func create(_ call: CAPPluginCall) { + let id = call.getString("id") ?? UUID().uuidString + let host = call.getString("host") ?? "" + let port = call.getInt("port") ?? 0 + + let useTLS = call.getBool("useTLS") ?? false + let acceptInvalidCertificates = call.getBool("acceptInvalidCertificates") ?? false + + let socket = implementation.create(id: id, host: host, port: port, useTLS: useTLS, acceptInvalidCertificates: acceptInvalidCertificates) + call.resolve() } + + @objc func connect(_ call: CAPPluginCall) { + let id = call.getString("id") ?? "" + implementation.connect(id: id) + call.resolve() + } + + @objc func send(_ call: CAPPluginCall) { + let id = call.getString("id") ?? "" + let message = call.getString("message") ?? "" + implementation.send(id: id, message: message) + call.resolve() + } + + @objc func disconnect(_ call: CAPPluginCall) { + let id = call.getString("id") ?? "" + implementation.disconnect(id: id) + call.resolve() + } }