@cappitolian/network-discovery 0.0.1
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/CappitolianNetworkDiscovery.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +365 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscovery.java +218 -0
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscoveryPlugin.java +116 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +264 -0
- package/dist/esm/definitions.d.ts +62 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +14 -0
- package/dist/esm/web.js +18 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +32 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +35 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscovery.swift +158 -0
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscoveryPlugin.swift +75 -0
- package/ios/Tests/NetworkDiscoveryPluginTests/NetworkDiscoveryTests.swift +15 -0
- package/package.json +86 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public class NetworkDiscovery: NSObject {
|
|
4
|
+
private var netService: NetService?
|
|
5
|
+
private var netServiceBrowser: NetServiceBrowser?
|
|
6
|
+
private var discoveredServices: [NetService] = []
|
|
7
|
+
|
|
8
|
+
weak var delegate: NetworkDiscoveryDelegate?
|
|
9
|
+
|
|
10
|
+
@objc public func startAdvertising(
|
|
11
|
+
serviceName: String,
|
|
12
|
+
serviceType: String,
|
|
13
|
+
port: Int,
|
|
14
|
+
txtRecord: [String: String]?
|
|
15
|
+
) {
|
|
16
|
+
netService = NetService(domain: "local.", type: serviceType, name: serviceName, port: Int32(port))
|
|
17
|
+
|
|
18
|
+
// Configurar TXT record
|
|
19
|
+
if let txtRecord = txtRecord, !txtRecord.isEmpty {
|
|
20
|
+
var txtData: [String: Data] = [:]
|
|
21
|
+
for (key, value) in txtRecord {
|
|
22
|
+
txtData[key] = value.data(using: .utf8)
|
|
23
|
+
}
|
|
24
|
+
let txtRecordData = NetService.data(fromTXTRecord: txtData)
|
|
25
|
+
netService?.setTXTRecord(txtRecordData)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
netService?.delegate = self
|
|
29
|
+
netService?.publish()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@objc public func stopAdvertising() {
|
|
33
|
+
netService?.stop()
|
|
34
|
+
netService = nil
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@objc public func startDiscovery(serviceType: String, domain: String = "local.") {
|
|
38
|
+
netServiceBrowser = NetServiceBrowser()
|
|
39
|
+
netServiceBrowser?.delegate = self
|
|
40
|
+
netServiceBrowser?.searchForServices(ofType: serviceType, inDomain: domain)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc public func stopDiscovery() {
|
|
44
|
+
netServiceBrowser?.stop()
|
|
45
|
+
netServiceBrowser = nil
|
|
46
|
+
discoveredServices.removeAll()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// MARK: - NetServiceDelegate
|
|
51
|
+
extension NetworkDiscovery: NetServiceDelegate {
|
|
52
|
+
public func netServiceDidPublish(_ sender: NetService) {
|
|
53
|
+
print("Service published: \(sender.name)")
|
|
54
|
+
delegate?.advertisingDidStart()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public func netService(_ sender: NetService, didNotPublish errorDict: [String : NSNumber]) {
|
|
58
|
+
print("Service did not publish: \(errorDict)")
|
|
59
|
+
let errorCode = errorDict[NetService.errorCode]?.intValue ?? -1
|
|
60
|
+
delegate?.advertisingDidFail(error: "Failed to publish service. Error code: \(errorCode)")
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// MARK: - NetServiceBrowserDelegate
|
|
65
|
+
extension NetworkDiscovery: NetServiceBrowserDelegate {
|
|
66
|
+
public func netServiceBrowserDidStopSearch(_ browser: NetServiceBrowser) {
|
|
67
|
+
print("Service discovery stopped")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public func netServiceBrowser(_ browser: NetServiceBrowser, didNotSearch errorDict: [String : NSNumber]) {
|
|
71
|
+
print("Service discovery failed: \(errorDict)")
|
|
72
|
+
let errorCode = errorDict[NetService.errorCode]?.intValue ?? -1
|
|
73
|
+
delegate?.discoveryDidFail(error: "Discovery failed. Error code: \(errorCode)")
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool) {
|
|
77
|
+
print("Service found: \(service.name)")
|
|
78
|
+
discoveredServices.append(service)
|
|
79
|
+
service.delegate = self
|
|
80
|
+
service.resolve(withTimeout: 5.0)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public func netServiceBrowser(_ browser: NetServiceBrowser, didRemove service: NetService, moreComing: Bool) {
|
|
84
|
+
print("Service lost: \(service.name)")
|
|
85
|
+
|
|
86
|
+
let serviceData: [String: Any] = [
|
|
87
|
+
"serviceName": service.name,
|
|
88
|
+
"serviceType": service.type
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
delegate?.serviceLost(serviceData: serviceData)
|
|
92
|
+
|
|
93
|
+
if let index = discoveredServices.firstIndex(of: service) {
|
|
94
|
+
discoveredServices.remove(at: index)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public func netServiceDidResolveAddress(_ sender: NetService) {
|
|
99
|
+
print("Service resolved: \(sender.name)")
|
|
100
|
+
|
|
101
|
+
var addresses: [String] = []
|
|
102
|
+
|
|
103
|
+
if let addressesData = sender.addresses {
|
|
104
|
+
for addressData in addressesData {
|
|
105
|
+
let address = addressData.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> String? in
|
|
106
|
+
guard let baseAddress = pointer.baseAddress else { return nil }
|
|
107
|
+
let data = baseAddress.assumingMemoryBound(to: sockaddr.self)
|
|
108
|
+
|
|
109
|
+
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
110
|
+
if getnameinfo(data, socklen_t(addressData.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
|
|
111
|
+
return String(cString: hostname)
|
|
112
|
+
}
|
|
113
|
+
return nil
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if let addr = address {
|
|
117
|
+
addresses.append(addr)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var serviceData: [String: Any] = [
|
|
123
|
+
"serviceName": sender.name,
|
|
124
|
+
"serviceType": sender.type,
|
|
125
|
+
"domain": sender.domain,
|
|
126
|
+
"hostName": sender.hostName ?? "",
|
|
127
|
+
"port": sender.port,
|
|
128
|
+
"addresses": addresses
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
// Agregar TXT record
|
|
132
|
+
if let txtData = sender.txtRecordData() {
|
|
133
|
+
let txtRecord = NetService.dictionary(fromTXTRecord: txtData)
|
|
134
|
+
var txtRecordDict: [String: String] = [:]
|
|
135
|
+
for (key, value) in txtRecord {
|
|
136
|
+
if let strValue = String(data: value, encoding: .utf8) {
|
|
137
|
+
txtRecordDict[key] = strValue
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
serviceData["txtRecord"] = txtRecordDict
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
delegate?.serviceFound(serviceData: serviceData)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber]) {
|
|
147
|
+
print("Service did not resolve: \(errorDict)")
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// MARK: - Delegate Protocol
|
|
152
|
+
@objc public protocol NetworkDiscoveryDelegate: AnyObject {
|
|
153
|
+
func advertisingDidStart()
|
|
154
|
+
func advertisingDidFail(error: String)
|
|
155
|
+
func serviceFound(serviceData: [String: Any])
|
|
156
|
+
func serviceLost(serviceData: [String: Any])
|
|
157
|
+
func discoveryDidFail(error: String)
|
|
158
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
@objc(NetworkDiscoveryPlugin)
|
|
5
|
+
public class NetworkDiscoveryPlugin: CAPPlugin, NetworkDiscoveryDelegate {
|
|
6
|
+
private var implementation: NetworkDiscovery?
|
|
7
|
+
|
|
8
|
+
override public func load() {
|
|
9
|
+
implementation = NetworkDiscovery()
|
|
10
|
+
implementation?.delegate = self
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@objc func startAdvertising(_ call: CAPPluginCall) {
|
|
14
|
+
guard let serviceName = call.getString("serviceName"),
|
|
15
|
+
let serviceType = call.getString("serviceType"),
|
|
16
|
+
let port = call.getInt("port") else {
|
|
17
|
+
call.reject("Missing required parameters")
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let txtRecord = call.getObject("txtRecord") as? [String: String]
|
|
22
|
+
|
|
23
|
+
implementation?.startAdvertising(
|
|
24
|
+
serviceName: serviceName,
|
|
25
|
+
serviceType: serviceType,
|
|
26
|
+
port: port,
|
|
27
|
+
txtRecord: txtRecord
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
call.resolve(["success": true])
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@objc func stopAdvertising(_ call: CAPPluginCall) {
|
|
34
|
+
implementation?.stopAdvertising()
|
|
35
|
+
call.resolve(["success": true])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@objc func startDiscovery(_ call: CAPPluginCall) {
|
|
39
|
+
guard let serviceType = call.getString("serviceType") else {
|
|
40
|
+
call.reject("Missing serviceType parameter")
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let domain = call.getString("domain") ?? "local."
|
|
45
|
+
|
|
46
|
+
implementation?.startDiscovery(serviceType: serviceType, domain: domain)
|
|
47
|
+
call.resolve()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@objc func stopDiscovery(_ call: CAPPluginCall) {
|
|
51
|
+
implementation?.stopDiscovery()
|
|
52
|
+
call.resolve(["success": true])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// MARK: - NetworkDiscoveryDelegate
|
|
56
|
+
public func advertisingDidStart() {
|
|
57
|
+
print("Advertising started successfully")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public func advertisingDidFail(error: String) {
|
|
61
|
+
print("Advertising failed: \(error)")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public func serviceFound(serviceData: [String : Any]) {
|
|
65
|
+
notifyListeners("serviceFound", data: serviceData)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public func serviceLost(serviceData: [String : Any]) {
|
|
69
|
+
notifyListeners("serviceLost", data: serviceData)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public func discoveryDidFail(error: String) {
|
|
73
|
+
print("Discovery failed: \(error)")
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import NetworkDiscoveryPlugin
|
|
3
|
+
|
|
4
|
+
class NetworkDiscoveryTests: XCTestCase {
|
|
5
|
+
func testEcho() {
|
|
6
|
+
// This is an example of a functional test case for a plugin.
|
|
7
|
+
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
|
8
|
+
|
|
9
|
+
let implementation = NetworkDiscovery()
|
|
10
|
+
let value = "Hello, World!"
|
|
11
|
+
let result = implementation.echo(value)
|
|
12
|
+
|
|
13
|
+
XCTAssertEqual(value, result)
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cappitolian/network-discovery",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A Capacitor plugin for network service discovery using mDNS/Bonjour. Allows automatic server-client connection without manual IP configuration.",
|
|
5
|
+
"main": "dist/plugin.cjs.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/esm/index.d.ts",
|
|
8
|
+
"unpkg": "dist/plugin.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"android/src/main/",
|
|
11
|
+
"android/build.gradle",
|
|
12
|
+
"dist/",
|
|
13
|
+
"ios/Sources",
|
|
14
|
+
"ios/Tests",
|
|
15
|
+
"Package.swift",
|
|
16
|
+
"CappitolianNetworkDiscovery.podspec"
|
|
17
|
+
],
|
|
18
|
+
"author": "Cappitolian",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/alessandrycruz1987/network-discovery.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/alessandrycruz1987/network-discovery.git/issues"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"capacitor",
|
|
29
|
+
"plugin",
|
|
30
|
+
"native",
|
|
31
|
+
"network",
|
|
32
|
+
"discovery",
|
|
33
|
+
"mdns",
|
|
34
|
+
"bonjour",
|
|
35
|
+
"nsd",
|
|
36
|
+
"zeroconf"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
40
|
+
"verify:ios": "xcodebuild -scheme CappitolianNetworkDiscovery -destination generic/platform=iOS",
|
|
41
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
42
|
+
"verify:web": "npm run build",
|
|
43
|
+
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
44
|
+
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
|
|
45
|
+
"eslint": "eslint . --ext ts",
|
|
46
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
47
|
+
"swiftlint": "node-swiftlint",
|
|
48
|
+
"docgen": "docgen --api NetworkDiscoveryPlugin --output-readme README.md --output-json dist/docs.json",
|
|
49
|
+
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
50
|
+
"clean": "rimraf ./dist",
|
|
51
|
+
"watch": "tsc --watch",
|
|
52
|
+
"prepublishOnly": "npm run build"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@capacitor/android": "^7.0.0",
|
|
56
|
+
"@capacitor/core": "^7.0.0",
|
|
57
|
+
"@capacitor/docgen": "^0.3.1",
|
|
58
|
+
"@capacitor/ios": "^7.0.0",
|
|
59
|
+
"@ionic/eslint-config": "^0.4.0",
|
|
60
|
+
"@ionic/prettier-config": "^4.0.0",
|
|
61
|
+
"@ionic/swiftlint-config": "^2.0.0",
|
|
62
|
+
"eslint": "^8.57.1",
|
|
63
|
+
"prettier": "^3.6.2",
|
|
64
|
+
"prettier-plugin-java": "^2.7.7",
|
|
65
|
+
"rimraf": "^6.1.0",
|
|
66
|
+
"rollup": "^4.53.2",
|
|
67
|
+
"swiftlint": "^2.0.0",
|
|
68
|
+
"typescript": "^5.9.3"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"@capacitor/core": "^7.0.0"
|
|
72
|
+
},
|
|
73
|
+
"prettier": "@ionic/prettier-config",
|
|
74
|
+
"swiftlint": "@ionic/swiftlint-config",
|
|
75
|
+
"eslintConfig": {
|
|
76
|
+
"extends": "@ionic/eslint-config/recommended"
|
|
77
|
+
},
|
|
78
|
+
"capacitor": {
|
|
79
|
+
"ios": {
|
|
80
|
+
"src": "ios"
|
|
81
|
+
},
|
|
82
|
+
"android": {
|
|
83
|
+
"src": "android"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|