@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.
@@ -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
+ }