@cappitolian/network-discovery 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -240
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscovery.java +123 -17
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscoveryPlugin.java +24 -13
- package/dist/docs.json +44 -30
- package/dist/esm/definitions.d.ts +47 -9
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +3 -3
- package/dist/esm/web.js +6 -5
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +7 -6
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +7 -6
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscovery.swift +138 -25
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscoveryPlugin.swift +10 -32
- package/package.json +1 -1
|
@@ -4,55 +4,168 @@ import Network
|
|
|
4
4
|
@objc public class NetworkDiscovery: NSObject {
|
|
5
5
|
private var listener: NWListener?
|
|
6
6
|
private var browser: NWBrowser?
|
|
7
|
+
private var activeConnections: [NWConnection] = []
|
|
7
8
|
|
|
8
9
|
@objc public func startPublishing(name: String, type: String, port: Int, metadata: [String: String]) throws {
|
|
10
|
+
stopServer()
|
|
9
11
|
let nwPort = NWEndpoint.Port(integerLiteral: UInt16(port))
|
|
10
|
-
// Limpieza de tipo para Bonjour: _ssspos._tcp
|
|
11
12
|
let cleanType = type.contains("_") ? type : "_\(type)._tcp"
|
|
12
13
|
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
for (key, value) in metadata { txt[key] = value }
|
|
16
|
-
service.txtRecord = txt
|
|
14
|
+
let ipForName = metadata["ip"] ?? ""
|
|
15
|
+
let displayName = ipForName.isEmpty ? name : "\(name)-\(ipForName)"
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
let mappedMetadata = metadata.mapValues { $0.data(using: .utf8)! }
|
|
18
|
+
let txtData = NetService.data(fromTXTRecord: mappedMetadata)
|
|
19
|
+
|
|
20
|
+
// --- CONFIGURACIÓN MEJORADA PARA COMPATIBILIDAD CROSS-PLATFORM ---
|
|
21
|
+
let params = NWParameters.tcp
|
|
22
|
+
params.includePeerToPeer = true
|
|
23
|
+
params.allowLocalEndpointReuse = true // Permite reutilización del puerto
|
|
24
|
+
params.acceptLocalOnly = false // Acepta conexiones de toda la red local
|
|
25
|
+
|
|
26
|
+
// Configuración adicional para que Android pueda resolver
|
|
27
|
+
if let tcpOptions = params.defaultProtocolStack.internetProtocol as? NWProtocolIP.Options {
|
|
28
|
+
tcpOptions.version = .any // IPv4 e IPv6
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let service = NWListener.Service(name: displayName, type: cleanType, domain: nil, txtRecord: txtData)
|
|
32
|
+
|
|
33
|
+
listener = try NWListener(using: params, on: nwPort)
|
|
19
34
|
listener?.service = service
|
|
35
|
+
|
|
36
|
+
// --- HANDLER CRÍTICO: Aceptar conexiones entrantes ---
|
|
37
|
+
listener?.newConnectionHandler = { [weak self] connection in
|
|
38
|
+
print("NSD_LOG: iOS recibió conexión de: \(connection.endpoint)")
|
|
39
|
+
|
|
40
|
+
// Iniciar la conexión para que Android pueda resolver el servicio
|
|
41
|
+
connection.start(queue: .main)
|
|
42
|
+
self?.activeConnections.append(connection)
|
|
43
|
+
|
|
44
|
+
// Configurar handlers básicos
|
|
45
|
+
connection.stateUpdateHandler = { state in
|
|
46
|
+
if case .ready = state {
|
|
47
|
+
print("NSD_LOG: Conexión establecida con Android")
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Limpieza automática cuando la conexión termine
|
|
52
|
+
connection.receiveMessage { _, _, _, error in
|
|
53
|
+
if error != nil {
|
|
54
|
+
connection.cancel()
|
|
55
|
+
self?.activeConnections.removeAll { $0 === connection }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
listener?.stateUpdateHandler = { state in
|
|
61
|
+
switch state {
|
|
62
|
+
case .ready:
|
|
63
|
+
if let port = self.listener?.port {
|
|
64
|
+
print("NSD_LOG: ✅ iOS Servidor LISTO en puerto \(port): \(displayName)")
|
|
65
|
+
}
|
|
66
|
+
case .failed(let error):
|
|
67
|
+
print("NSD_LOG: ❌ iOS Servidor falló: \(error)")
|
|
68
|
+
case .waiting(let error):
|
|
69
|
+
print("NSD_LOG: ⏳ iOS Servidor esperando: \(error)")
|
|
70
|
+
default:
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
20
75
|
listener?.start(queue: .main)
|
|
76
|
+
|
|
77
|
+
// Pequeño delay para asegurar que el servicio esté completamente publicado
|
|
78
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
79
|
+
print("NSD_LOG: Servicio iOS completamente inicializado")
|
|
80
|
+
}
|
|
21
81
|
}
|
|
22
82
|
|
|
23
83
|
@objc public func findService(name: String, type: String, timeout: TimeInterval, completion: @escaping ([String: Any]?) -> Void) {
|
|
84
|
+
stopDiscovery()
|
|
24
85
|
let cleanType = type.contains("_") ? type : "_\(type)._tcp"
|
|
25
|
-
let parameters = NWParameters()
|
|
26
|
-
parameters.includePeerToPeer = true
|
|
27
86
|
|
|
28
|
-
|
|
87
|
+
// Configuración mejorada para el browser
|
|
88
|
+
let params = NWParameters()
|
|
89
|
+
params.includePeerToPeer = true
|
|
29
90
|
|
|
30
|
-
browser
|
|
91
|
+
let browser = NWBrowser(for: .bonjour(type: cleanType, domain: nil), using: params)
|
|
92
|
+
self.browser = browser
|
|
93
|
+
var finished = false
|
|
94
|
+
|
|
95
|
+
browser.browseResultsChangedHandler = { [weak self] results, _ in
|
|
31
96
|
for result in results {
|
|
32
|
-
if case let .service(foundName, _, _, _) = result.endpoint, foundName
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
97
|
+
if case let .service(foundName, _, _, _) = result.endpoint, foundName.contains(name) {
|
|
98
|
+
if !finished {
|
|
99
|
+
finished = true
|
|
100
|
+
|
|
101
|
+
print("NSD_LOG: iOS Cliente encontró servicio: \(foundName)")
|
|
102
|
+
|
|
103
|
+
var meta: [String: String] = [:]
|
|
104
|
+
if case let .bonjour(txt) = result.metadata {
|
|
105
|
+
for (k, v) in txt.dictionary {
|
|
106
|
+
if let dataValue = v as? Data {
|
|
107
|
+
meta[k] = String(data: dataValue, encoding: .utf8) ?? ""
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// --- LÓGICA DE EXTRACCIÓN ROBUSTA ---
|
|
113
|
+
var ip = meta["ip"] ?? ""
|
|
114
|
+
|
|
115
|
+
if (ip.isEmpty || ip == "0.0.0.0") && foundName.contains("-") {
|
|
116
|
+
ip = foundName.components(separatedBy: "-").last ?? ""
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if ip.isEmpty || ip == "0.0.0.0" {
|
|
120
|
+
if case let .hostPort(host, _) = result.endpoint {
|
|
121
|
+
ip = "\(host)".components(separatedBy: "%").first ?? "\(host)"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
print("NSD_LOG: IP extraída: \(ip)")
|
|
126
|
+
|
|
127
|
+
self?.stopDiscovery()
|
|
128
|
+
completion(["ip": ip, "port": 8081, "metadata": meta])
|
|
129
|
+
}
|
|
37
130
|
return
|
|
38
131
|
}
|
|
39
132
|
}
|
|
40
133
|
}
|
|
41
134
|
|
|
42
|
-
browser
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
135
|
+
browser.stateUpdateHandler = { state in
|
|
136
|
+
switch state {
|
|
137
|
+
case .ready:
|
|
138
|
+
print("NSD_LOG: iOS Browser listo para buscar")
|
|
139
|
+
case .failed(let error):
|
|
140
|
+
print("NSD_LOG: iOS Browser falló: \(error)")
|
|
141
|
+
default:
|
|
142
|
+
break
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
browser.start(queue: .main)
|
|
147
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { [weak self] in
|
|
148
|
+
if !finished {
|
|
149
|
+
finished = true
|
|
150
|
+
print("NSD_LOG: iOS Búsqueda timeout")
|
|
151
|
+
self?.stopDiscovery()
|
|
152
|
+
completion(nil)
|
|
153
|
+
}
|
|
46
154
|
}
|
|
47
155
|
}
|
|
48
156
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@objc public func stopPublishing() {
|
|
157
|
+
@objc public func stopServer() {
|
|
158
|
+
// Limpiar todas las conexiones activas
|
|
159
|
+
activeConnections.forEach { $0.cancel() }
|
|
160
|
+
activeConnections.removeAll()
|
|
161
|
+
|
|
55
162
|
listener?.cancel()
|
|
56
163
|
listener = nil
|
|
164
|
+
print("NSD_LOG: iOS Servidor detenido")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@objc public func stopDiscovery() {
|
|
168
|
+
browser?.cancel()
|
|
169
|
+
browser = nil
|
|
57
170
|
}
|
|
58
171
|
}
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import Capacitor
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Registro moderno de Capacitor para Swift.
|
|
6
|
-
* 'NetworkDiscovery' es el nombre que usas en registerPlugin en TS.
|
|
7
|
-
*/
|
|
8
4
|
@objc(NetworkDiscoveryPlugin)
|
|
9
5
|
public class NetworkDiscoveryPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
10
|
-
public let identifier = "NetworkDiscoveryPlugin"
|
|
11
|
-
public let jsName = "NetworkDiscovery"
|
|
12
|
-
|
|
13
|
-
// Mapeo de métodos para el Bridge
|
|
6
|
+
public let identifier = "NetworkDiscoveryPlugin"
|
|
7
|
+
public let jsName = "NetworkDiscovery"
|
|
14
8
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
15
9
|
CAPPluginMethod(name: "startServer", returnType: CAPPluginReturnPromise),
|
|
16
10
|
CAPPluginMethod(name: "stopServer", returnType: CAPPluginReturnPromise),
|
|
@@ -22,44 +16,28 @@ public class NetworkDiscoveryPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
22
16
|
@objc func startServer(_ call: CAPPluginCall) {
|
|
23
17
|
guard let name = call.getString("serviceName"),
|
|
24
18
|
let type = call.getString("serviceType"),
|
|
25
|
-
let port = call.getInt("port") else {
|
|
26
|
-
call.reject("Faltan parámetros obligatorios")
|
|
27
|
-
return
|
|
28
|
-
}
|
|
19
|
+
let port = call.getInt("port") else { return call.reject("Faltan parámetros") }
|
|
29
20
|
|
|
30
21
|
let metadata = call.getObject("metadata") as? [String: String] ?? [:]
|
|
31
|
-
|
|
32
22
|
do {
|
|
33
23
|
try implementation.startPublishing(name: name, type: type, port: port, metadata: metadata)
|
|
34
24
|
call.resolve()
|
|
35
|
-
} catch {
|
|
36
|
-
call.reject("Error iniciando servidor mDNS: \(error.localizedDescription)")
|
|
37
|
-
}
|
|
25
|
+
} catch { call.reject(error.localizedDescription) }
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
@objc func stopServer(_ call: CAPPluginCall) {
|
|
41
|
-
implementation.
|
|
29
|
+
implementation.stopServer()
|
|
42
30
|
call.resolve()
|
|
43
31
|
}
|
|
44
32
|
|
|
45
33
|
@objc func findServer(_ call: CAPPluginCall) {
|
|
46
34
|
guard let name = call.getString("serviceName"),
|
|
47
|
-
let type = call.getString("serviceType") else {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
let timeout = Double(call.getInt("timeout") ?? 5000) / 1000.0
|
|
52
|
-
|
|
35
|
+
let type = call.getString("serviceType") else { return call.reject("Faltan parámetros") }
|
|
36
|
+
|
|
37
|
+
let timeout = Double(call.getInt("timeout") ?? 10000) / 1000.0
|
|
53
38
|
implementation.findService(name: name, type: type, timeout: timeout) { result in
|
|
54
|
-
if let
|
|
55
|
-
|
|
56
|
-
"ip": result.ip,
|
|
57
|
-
"port": result.port,
|
|
58
|
-
"metadata": result.metadata
|
|
59
|
-
])
|
|
60
|
-
} else {
|
|
61
|
-
call.reject("No se encontró el servidor tras \(timeout) segundos")
|
|
62
|
-
}
|
|
39
|
+
if let data = result { call.resolve(data) }
|
|
40
|
+
else { call.reject("TIMEOUT_ERROR") }
|
|
63
41
|
}
|
|
64
42
|
}
|
|
65
43
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cappitolian/network-discovery",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "A Capacitor plugin for network service discovery using mDNS/Bonjour. Allows automatic server-client connection without manual IP configuration.",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|