munim-wifi 0.1.0 → 0.1.2
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 +132 -8
- package/ios/HybridMunimWifi.swift +248 -155
- package/lib/typescript/src/specs/munim-wifi.nitro.d.ts +47 -5
- package/lib/typescript/src/specs/munim-wifi.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/MunimWifi+autolinking.cmake +1 -0
- package/nitrogen/generated/android/c++/JConnectionOptions.hpp +66 -0
- package/nitrogen/generated/android/c++/JCurrentNetworkInfo.hpp +97 -0
- package/nitrogen/generated/android/c++/JHybridMunimWifiSpec.cpp +72 -0
- package/nitrogen/generated/android/c++/JHybridMunimWifiSpec.hpp +4 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_CurrentNetworkInfo.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_CurrentNetworkInfo.hpp +74 -0
- package/nitrogen/generated/android/c++/JWifiNetwork.hpp +9 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/ConnectionOptions.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/CurrentNetworkInfo.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/HybridMunimWifiSpec.kt +16 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/Variant_NullType_CurrentNetworkInfo.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/WifiNetwork.kt +3 -3
- package/nitrogen/generated/ios/MunimWifi-Swift-Cxx-Bridge.cpp +16 -0
- package/nitrogen/generated/ios/MunimWifi-Swift-Cxx-Bridge.hpp +133 -0
- package/nitrogen/generated/ios/MunimWifi-Swift-Cxx-Umbrella.hpp +6 -0
- package/nitrogen/generated/ios/c++/HybridMunimWifiSpecSwift.hpp +38 -0
- package/nitrogen/generated/ios/swift/ConnectionOptions.swift +66 -0
- package/nitrogen/generated/ios/swift/CurrentNetworkInfo.swift +113 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__CurrentNetworkInfo_.swift +59 -0
- package/nitrogen/generated/ios/swift/HybridMunimWifiSpec.swift +4 -0
- package/nitrogen/generated/ios/swift/HybridMunimWifiSpec_cxx.swift +90 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_CurrentNetworkInfo.swift +18 -0
- package/nitrogen/generated/ios/swift/WifiNetwork.swift +18 -6
- package/nitrogen/generated/shared/c++/ConnectionOptions.hpp +92 -0
- package/nitrogen/generated/shared/c++/CurrentNetworkInfo.hpp +105 -0
- package/nitrogen/generated/shared/c++/HybridMunimWifiSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridMunimWifiSpec.hpp +10 -0
- package/nitrogen/generated/shared/c++/WifiNetwork.hpp +9 -9
- package/package.json +2 -2
- package/src/index.ts +45 -0
- package/src/specs/munim-wifi.nitro.ts +56 -6
package/README.md
CHANGED
|
@@ -79,20 +79,87 @@
|
|
|
79
79
|
### Wi-Fi Scanning
|
|
80
80
|
|
|
81
81
|
- 📡 **Network Scanning**: Scan for nearby Wi-Fi networks with detailed information
|
|
82
|
-
- 📶 **Signal Strength**: Get RSSI (signal strength) values for all networks
|
|
83
|
-
- 🔍 **Network Details**: Retrieve SSIDs, BSSIDs (MAC addresses), channels, and frequencies
|
|
82
|
+
- 📶 **Signal Strength**: Get RSSI (signal strength) values for all networks (Android only)
|
|
83
|
+
- 🔍 **Network Details**: Retrieve SSIDs, BSSIDs (MAC addresses), channels, and frequencies (Android only)
|
|
84
84
|
- 📊 **Wi-Fi Fingerprinting**: Create comprehensive Wi-Fi fingerprints for location services
|
|
85
|
-
- 🔄 **Continuous Scanning**: Support for continuous scanning with event-based updates
|
|
85
|
+
- 🔄 **Continuous Scanning**: Support for continuous scanning with event-based updates (Android only)
|
|
86
86
|
- 📱 **Cross-platform**: Works on both iOS and Android
|
|
87
87
|
|
|
88
|
+
### Network Management
|
|
89
|
+
|
|
90
|
+
- 🔌 **Connect to Networks**: Programmatically connect to Wi-Fi networks
|
|
91
|
+
- 🔌 **Disconnect**: Disconnect from current Wi-Fi network
|
|
92
|
+
- 📱 **Current Network Info**: Get information about currently connected network
|
|
93
|
+
- 🌐 **IP Address**: Retrieve IP address information for current connection
|
|
94
|
+
|
|
88
95
|
### Additional Features
|
|
89
96
|
|
|
90
|
-
- 📱 **Cross-platform**: Works on both iOS and Android
|
|
97
|
+
- 📱 **Cross-platform**: Works on both iOS and Android (with platform-specific limitations)
|
|
91
98
|
- 🎯 **TypeScript Support**: Full TypeScript definitions included
|
|
92
99
|
- ⚡ **High Performance**: Built with React Native's Nitro modules architecture
|
|
93
100
|
- 🚀 **Expo Compatible**: Works seamlessly with Expo managed and bare workflows
|
|
94
101
|
- 🔐 **Permission Handling**: Built-in permission request helpers
|
|
95
102
|
|
|
103
|
+
### ⚠️ Platform Limitations
|
|
104
|
+
|
|
105
|
+
#### iOS Limitations
|
|
106
|
+
|
|
107
|
+
**Critical: CoreWLAN is macOS Only**
|
|
108
|
+
CoreWLAN framework is NOT available on iOS - it only works on macOS. The iOS implementation uses `NEHotspotNetwork` and `NEHotspotConfiguration` APIs.
|
|
109
|
+
|
|
110
|
+
**iOS Wi-Fi Scanning Capabilities:**
|
|
111
|
+
iOS has very limited Wi-Fi scanning capabilities:
|
|
112
|
+
|
|
113
|
+
✅ **Available on iOS:**
|
|
114
|
+
- Get SSID (network name) - via `NEHotspotNetwork.fetchCurrent()`
|
|
115
|
+
- Get BSSID (MAC address) - via `NEHotspotNetwork.fetchCurrent()`
|
|
116
|
+
- Connect to Wi-Fi networks - via `NEHotspotConfiguration`
|
|
117
|
+
- Disconnect from Wi-Fi - via `NEHotspotConfiguration`
|
|
118
|
+
- Get current connected network info
|
|
119
|
+
|
|
120
|
+
❌ **NOT Available on iOS:**
|
|
121
|
+
- RSSI (signal strength) - Cannot be retrieved for scanned networks
|
|
122
|
+
- Channel information - Not available
|
|
123
|
+
- Frequency information - Not available
|
|
124
|
+
- General network scanning - Only works for hotspot networks via NEHotspotHelper (requires special entitlement)
|
|
125
|
+
|
|
126
|
+
**iOS Requirements:**
|
|
127
|
+
- Location permission (precise location) required
|
|
128
|
+
- "Access Wi-Fi Information" entitlement in Xcode
|
|
129
|
+
- "Hotspot Configuration" capability for connecting to networks
|
|
130
|
+
- iOS 13+ requires location permission
|
|
131
|
+
|
|
132
|
+
#### Android Limitations
|
|
133
|
+
|
|
134
|
+
**Scanning Restrictions:**
|
|
135
|
+
- `WifiManager.startScan()` is deprecated in Android P (API 28) but still works
|
|
136
|
+
- **Throttling limits:**
|
|
137
|
+
- Foreground apps: 4 scans every 2 minutes
|
|
138
|
+
- Background apps: More restrictive
|
|
139
|
+
- Requires location permission (Android 6.0+)
|
|
140
|
+
- Passive listening available on Android 10+ (API 29)
|
|
141
|
+
|
|
142
|
+
✅ **Available on Android:**
|
|
143
|
+
- Full network scanning with SSID, BSSID, RSSI, channel, frequency
|
|
144
|
+
- Connect/disconnect to networks
|
|
145
|
+
- Get current network info
|
|
146
|
+
- All features work, but with throttling limits
|
|
147
|
+
|
|
148
|
+
#### Feature Support Matrix
|
|
149
|
+
|
|
150
|
+
| Feature | iOS | Android | Notes |
|
|
151
|
+
|---------|-----|---------|-------|
|
|
152
|
+
| Scan networks | ⚠️ Limited | ✅ Full | iOS: SSID/BSSID only, no RSSI/channel/frequency |
|
|
153
|
+
| Get SSID | ✅ | ✅ | Both platforms |
|
|
154
|
+
| Get BSSID | ✅ | ✅ | Both platforms |
|
|
155
|
+
| Get RSSI | ❌ | ✅ | iOS: Not available for scanned networks |
|
|
156
|
+
| Get Channel | ❌ | ✅ | iOS: Not available |
|
|
157
|
+
| Get Frequency | ❌ | ✅ | iOS: Not available |
|
|
158
|
+
| Connect to network | ✅ | ✅ | Both platforms (requires entitlements/capabilities) |
|
|
159
|
+
| Disconnect | ✅ | ✅ | Both platforms |
|
|
160
|
+
| Get current network | ✅ | ✅ | Both platforms |
|
|
161
|
+
| Wi-Fi fingerprinting | ⚠️ Limited | ✅ Full | iOS: Limited to SSID/BSSID only |
|
|
162
|
+
|
|
96
163
|
## 📦 Installation
|
|
97
164
|
|
|
98
165
|
### React Native CLI
|
|
@@ -344,16 +411,18 @@ Gets BSSID (MAC address) for a specific network by SSID.
|
|
|
344
411
|
#### `getChannelInfo(ssid)`
|
|
345
412
|
|
|
346
413
|
Gets channel and frequency information for a specific network by SSID.
|
|
414
|
+
**Note: Not available on iOS - returns null.**
|
|
347
415
|
|
|
348
416
|
**Parameters:**
|
|
349
417
|
|
|
350
418
|
- `ssid` (string): The SSID of the network
|
|
351
419
|
|
|
352
|
-
**Returns:** Promise<
|
|
420
|
+
**Returns:** Promise<ChannelInfo | null>
|
|
353
421
|
|
|
354
422
|
#### `getNetworkInfo(ssid)`
|
|
355
423
|
|
|
356
424
|
Gets all available information for a specific network by SSID.
|
|
425
|
+
**Note: On iOS, RSSI, channel, and frequency will be undefined.**
|
|
357
426
|
|
|
358
427
|
**Parameters:**
|
|
359
428
|
|
|
@@ -361,6 +430,38 @@ Gets all available information for a specific network by SSID.
|
|
|
361
430
|
|
|
362
431
|
**Returns:** Promise<WifiNetwork | null>
|
|
363
432
|
|
|
433
|
+
#### `getCurrentNetwork()`
|
|
434
|
+
|
|
435
|
+
Gets information about the currently connected Wi-Fi network.
|
|
436
|
+
|
|
437
|
+
**Returns:** Promise<CurrentNetworkInfo | null>
|
|
438
|
+
|
|
439
|
+
#### `connectToNetwork(options)`
|
|
440
|
+
|
|
441
|
+
Connects to a Wi-Fi network.
|
|
442
|
+
**Note: Requires appropriate permissions and capabilities on both platforms.**
|
|
443
|
+
|
|
444
|
+
**Parameters:**
|
|
445
|
+
|
|
446
|
+
- `options` (ConnectionOptions):
|
|
447
|
+
- `ssid` (string): The SSID of the network
|
|
448
|
+
- `password?` (string): Optional password for secured networks
|
|
449
|
+
- `isWEP?` (boolean): Whether the network uses WEP encryption
|
|
450
|
+
|
|
451
|
+
**Returns:** Promise<void>
|
|
452
|
+
|
|
453
|
+
#### `disconnect()`
|
|
454
|
+
|
|
455
|
+
Disconnects from the current Wi-Fi network.
|
|
456
|
+
|
|
457
|
+
**Returns:** Promise<void>
|
|
458
|
+
|
|
459
|
+
#### `getIPAddress()`
|
|
460
|
+
|
|
461
|
+
Gets IP address information for the current Wi-Fi connection.
|
|
462
|
+
|
|
463
|
+
**Returns:** Promise<string | null>
|
|
464
|
+
|
|
364
465
|
### Event Management
|
|
365
466
|
|
|
366
467
|
#### `addNetworkFoundListener(callback)`
|
|
@@ -392,15 +493,38 @@ Adds an event listener.
|
|
|
392
493
|
interface WifiNetwork {
|
|
393
494
|
ssid: string
|
|
394
495
|
bssid: string
|
|
395
|
-
rssi
|
|
396
|
-
frequency
|
|
397
|
-
channel?: number
|
|
496
|
+
rssi?: number // Not available on iOS
|
|
497
|
+
frequency?: number // Not available on iOS
|
|
498
|
+
channel?: number // Not available on iOS
|
|
398
499
|
capabilities?: string
|
|
399
500
|
isSecure?: boolean
|
|
400
501
|
timestamp?: number
|
|
401
502
|
}
|
|
402
503
|
```
|
|
403
504
|
|
|
505
|
+
#### `CurrentNetworkInfo`
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
interface CurrentNetworkInfo {
|
|
509
|
+
ssid: string
|
|
510
|
+
bssid: string
|
|
511
|
+
ipAddress?: string
|
|
512
|
+
subnetMask?: string
|
|
513
|
+
gateway?: string
|
|
514
|
+
dnsServers?: string[]
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
#### `ConnectionOptions`
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
interface ConnectionOptions {
|
|
522
|
+
ssid: string
|
|
523
|
+
password?: string
|
|
524
|
+
isWEP?: boolean
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
404
528
|
#### `WifiFingerprint`
|
|
405
529
|
|
|
406
530
|
```typescript
|
|
@@ -6,217 +6,310 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
|
-
import CoreWLAN
|
|
10
|
-
import CoreLocation
|
|
11
9
|
import NetworkExtension
|
|
10
|
+
import CoreLocation
|
|
11
|
+
import SystemConfiguration.CaptiveNetwork
|
|
12
|
+
import NitroModules
|
|
12
13
|
|
|
13
14
|
class HybridMunimWifi: HybridMunimWifiSpec {
|
|
14
|
-
private let wifiClient = CWWiFiClient.shared()
|
|
15
15
|
private var locationManager: CLLocationManager?
|
|
16
|
-
private var scanResults: [
|
|
16
|
+
private var scanResults: [WifiNetwork] = []
|
|
17
17
|
private var isScanning = false
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// 2.4 GHz band
|
|
23
|
-
return ((frequency - 2412) / 5) + 1
|
|
24
|
-
} else if frequency >= 5170 && frequency <= 5825 {
|
|
25
|
-
// 5 GHz band
|
|
26
|
-
return ((frequency - 5170) / 5) + 36
|
|
27
|
-
}
|
|
28
|
-
return 0
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
private func convertNetwork(_ network: CWNetwork) -> WifiNetwork {
|
|
32
|
-
guard let channel = network.wlanChannel else {
|
|
33
|
-
return WifiNetwork(
|
|
34
|
-
ssid: network.ssid ?? "",
|
|
35
|
-
bssid: network.bssid ?? "",
|
|
36
|
-
rssi: Int(network.rssiValue),
|
|
37
|
-
frequency: 0,
|
|
38
|
-
channel: 0,
|
|
39
|
-
capabilities: network.ies?.description ?? "",
|
|
40
|
-
isSecure: false,
|
|
41
|
-
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let channelNumber = Int(channel.channelNumber)
|
|
46
|
-
let frequency: Int
|
|
47
|
-
if channel.channelBand == .band2GHz {
|
|
48
|
-
frequency = 2412 + (channelNumber - 1) * 5
|
|
49
|
-
} else {
|
|
50
|
-
frequency = 5170 + (channelNumber - 36) * 5
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let isSecure = network.supportsSecurity(.WPA2Personal) ||
|
|
54
|
-
network.supportsSecurity(.WPA3Personal) ||
|
|
55
|
-
network.supportsSecurity(.WPAEnterprise) ||
|
|
56
|
-
network.supportsSecurity(.WPA3Enterprise)
|
|
57
|
-
|
|
58
|
-
return WifiNetwork(
|
|
59
|
-
ssid: network.ssid ?? "",
|
|
60
|
-
bssid: network.bssid ?? "",
|
|
61
|
-
rssi: Int(network.rssiValue),
|
|
62
|
-
frequency: frequency,
|
|
63
|
-
channel: channelNumber,
|
|
64
|
-
capabilities: network.ies?.description ?? "",
|
|
65
|
-
isSecure: isSecure,
|
|
66
|
-
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
67
|
-
)
|
|
68
|
-
}
|
|
19
|
+
// Note: iOS has very limited Wi-Fi scanning capabilities
|
|
20
|
+
// CoreWLAN is macOS only - not available on iOS
|
|
21
|
+
// iOS can only get SSID/BSSID, not RSSI, channel, or frequency
|
|
69
22
|
|
|
70
|
-
func isWifiEnabled() throws -> Bool {
|
|
71
|
-
|
|
23
|
+
func isWifiEnabled() throws -> Promise<Bool> {
|
|
24
|
+
let promise = Promise<Bool>()
|
|
25
|
+
// On iOS, we can't directly check if Wi-Fi is enabled
|
|
26
|
+
// We can infer it by checking if we can get current network
|
|
27
|
+
if let _ = try? getCurrentNetworkSync() {
|
|
28
|
+
promise.resolve(withResult: true)
|
|
29
|
+
} else {
|
|
30
|
+
promise.resolve(withResult: false)
|
|
31
|
+
}
|
|
32
|
+
return promise
|
|
72
33
|
}
|
|
73
34
|
|
|
74
|
-
func requestWifiPermission() throws -> Bool {
|
|
75
|
-
|
|
35
|
+
func requestWifiPermission() throws -> Promise<Bool> {
|
|
36
|
+
let promise = Promise<Bool>()
|
|
76
37
|
let locationManager = CLLocationManager()
|
|
77
38
|
self.locationManager = locationManager
|
|
78
39
|
|
|
79
40
|
let status = locationManager.authorizationStatus
|
|
80
|
-
|
|
41
|
+
let hasPermission = status == .authorizedWhenInUse || status == .authorizedAlways
|
|
42
|
+
promise.resolve(withResult: hasPermission)
|
|
43
|
+
return promise
|
|
81
44
|
}
|
|
82
45
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
46
|
+
// Helper to get current network synchronously
|
|
47
|
+
private func getCurrentNetworkSync() -> CurrentNetworkInfo? {
|
|
48
|
+
var result: CurrentNetworkInfo? = nil
|
|
49
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
87
50
|
|
|
88
|
-
|
|
89
|
-
|
|
51
|
+
NEHotspotNetwork.fetchCurrent { network in
|
|
52
|
+
if let network = network {
|
|
53
|
+
result = CurrentNetworkInfo(
|
|
54
|
+
ssid: network.ssid,
|
|
55
|
+
bssid: network.bssid ?? ""
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
semaphore.signal()
|
|
90
59
|
}
|
|
91
60
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
61
|
+
_ = semaphore.wait(timeout: .now() + 5)
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func scanNetworks(options: ScanOptions?) throws -> Promise<[WifiNetwork]> {
|
|
66
|
+
let promise = Promise<[WifiNetwork]>()
|
|
67
|
+
// iOS limitation: Cannot scan for networks directly
|
|
68
|
+
// Only can get current connected network
|
|
69
|
+
// For scanning, we can only return the current network if connected
|
|
70
|
+
// This is a major iOS limitation
|
|
98
71
|
|
|
99
|
-
|
|
100
|
-
let
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
72
|
+
if let current = try? getCurrentNetworkSync() {
|
|
73
|
+
let network = WifiNetwork(
|
|
74
|
+
ssid: current.ssid,
|
|
75
|
+
bssid: current.bssid,
|
|
76
|
+
rssi: nil, // Not available on iOS
|
|
77
|
+
frequency: nil, // Not available on iOS
|
|
78
|
+
channel: nil, // Not available on iOS
|
|
79
|
+
capabilities: nil,
|
|
80
|
+
isSecure: nil,
|
|
81
|
+
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
82
|
+
)
|
|
83
|
+
scanResults = [network]
|
|
84
|
+
promise.resolve(withResult: [network])
|
|
85
|
+
} else {
|
|
86
|
+
// Return empty array if not connected
|
|
87
|
+
promise.resolve(withResult: [])
|
|
108
88
|
}
|
|
89
|
+
return promise
|
|
109
90
|
}
|
|
110
91
|
|
|
111
|
-
func startScan(
|
|
92
|
+
func startScan(options: ScanOptions?) throws {
|
|
112
93
|
isScanning = true
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
guard status == .authorizedWhenInUse || status == .authorizedAlways else {
|
|
128
|
-
isScanning = false
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Start scanning in background
|
|
133
|
-
DispatchQueue.global(qos: .background).async { [weak self] in
|
|
134
|
-
guard let self = self else { return }
|
|
135
|
-
|
|
136
|
-
do {
|
|
137
|
-
let networks = try interface.scanForNetworks(withName: nil)
|
|
138
|
-
self.scanResults = networks
|
|
139
|
-
self.isScanning = false
|
|
140
|
-
} catch {
|
|
141
|
-
self.isScanning = false
|
|
142
|
-
}
|
|
94
|
+
// iOS limitation: Cannot continuously scan
|
|
95
|
+
// Just get current network once
|
|
96
|
+
if let current = try? getCurrentNetworkSync() {
|
|
97
|
+
let network = WifiNetwork(
|
|
98
|
+
ssid: current.ssid,
|
|
99
|
+
bssid: current.bssid,
|
|
100
|
+
rssi: nil,
|
|
101
|
+
frequency: nil,
|
|
102
|
+
channel: nil,
|
|
103
|
+
capabilities: nil,
|
|
104
|
+
isSecure: nil,
|
|
105
|
+
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
106
|
+
)
|
|
107
|
+
scanResults = [network]
|
|
143
108
|
}
|
|
109
|
+
isScanning = false
|
|
144
110
|
}
|
|
145
111
|
|
|
146
112
|
func stopScan() throws {
|
|
147
113
|
isScanning = false
|
|
148
114
|
}
|
|
149
115
|
|
|
150
|
-
func getSSIDs() throws -> [String] {
|
|
151
|
-
let
|
|
152
|
-
|
|
116
|
+
func getSSIDs() throws -> Promise<[String]> {
|
|
117
|
+
let promise = Promise<[String]>()
|
|
118
|
+
// iOS limitation: Can only get current network SSID
|
|
119
|
+
if let current = try? getCurrentNetworkSync() {
|
|
120
|
+
promise.resolve(withResult: [current.ssid])
|
|
121
|
+
} else {
|
|
122
|
+
promise.resolve(withResult: [])
|
|
123
|
+
}
|
|
124
|
+
return promise
|
|
153
125
|
}
|
|
154
126
|
|
|
155
|
-
func getWifiFingerprint() throws -> WifiFingerprint {
|
|
156
|
-
let
|
|
157
|
-
|
|
127
|
+
func getWifiFingerprint() throws -> Promise<WifiFingerprint> {
|
|
128
|
+
let promise = Promise<WifiFingerprint>()
|
|
129
|
+
// iOS limitation: Can only get current network, no RSSI/channel/frequency
|
|
130
|
+
var networks: [WifiNetwork] = scanResults
|
|
131
|
+
if networks.isEmpty {
|
|
132
|
+
if let current = try? getCurrentNetworkSync() {
|
|
133
|
+
networks = [WifiNetwork(
|
|
134
|
+
ssid: current.ssid,
|
|
135
|
+
bssid: current.bssid,
|
|
136
|
+
rssi: nil,
|
|
137
|
+
frequency: nil,
|
|
138
|
+
channel: nil,
|
|
139
|
+
capabilities: nil,
|
|
140
|
+
isSecure: nil,
|
|
141
|
+
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
142
|
+
)]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
158
145
|
|
|
159
|
-
|
|
160
|
-
networks:
|
|
146
|
+
let fingerprint = WifiFingerprint(
|
|
147
|
+
networks: networks,
|
|
161
148
|
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
162
149
|
)
|
|
150
|
+
promise.resolve(withResult: fingerprint)
|
|
151
|
+
return promise
|
|
163
152
|
}
|
|
164
153
|
|
|
165
|
-
func getRSSI(ssid: String) throws ->
|
|
166
|
-
let
|
|
167
|
-
|
|
168
|
-
|
|
154
|
+
func getRSSI(ssid: String) throws -> Promise<Variant_NullType_Double> {
|
|
155
|
+
let promise = Promise<Variant_NullType_Double>()
|
|
156
|
+
// iOS limitation: RSSI not available for scanned networks
|
|
157
|
+
promise.resolve(withResult: .first(NullType()))
|
|
158
|
+
return promise
|
|
169
159
|
}
|
|
170
160
|
|
|
171
|
-
func getBSSID(ssid: String) throws ->
|
|
172
|
-
let
|
|
173
|
-
let
|
|
174
|
-
|
|
161
|
+
func getBSSID(ssid: String) throws -> Promise<Variant_NullType_String> {
|
|
162
|
+
let promise = Promise<Variant_NullType_String>()
|
|
163
|
+
if let current = try? getCurrentNetworkSync(), current.ssid == ssid {
|
|
164
|
+
promise.resolve(withResult: .second(current.bssid))
|
|
165
|
+
} else {
|
|
166
|
+
promise.resolve(withResult: .first(NullType()))
|
|
167
|
+
}
|
|
168
|
+
return promise
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
func getChannelInfo(ssid: String) throws -> Promise<Variant_NullType_ChannelInfo> {
|
|
172
|
+
let promise = Promise<Variant_NullType_ChannelInfo>()
|
|
173
|
+
// iOS limitation: Channel and frequency not available
|
|
174
|
+
promise.resolve(withResult: .first(NullType()))
|
|
175
|
+
return promise
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
func
|
|
178
|
-
let
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
func getNetworkInfo(ssid: String) throws -> Promise<Variant_NullType_WifiNetwork> {
|
|
179
|
+
let promise = Promise<Variant_NullType_WifiNetwork>()
|
|
180
|
+
if let current = try? getCurrentNetworkSync(), current.ssid == ssid {
|
|
181
|
+
let network = WifiNetwork(
|
|
182
|
+
ssid: current.ssid,
|
|
183
|
+
bssid: current.bssid,
|
|
184
|
+
rssi: nil, // Not available on iOS
|
|
185
|
+
frequency: nil, // Not available on iOS
|
|
186
|
+
channel: nil, // Not available on iOS
|
|
187
|
+
capabilities: nil,
|
|
188
|
+
isSecure: nil,
|
|
189
|
+
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
190
|
+
)
|
|
191
|
+
promise.resolve(withResult: .second(network))
|
|
192
|
+
} else {
|
|
193
|
+
promise.resolve(withResult: .first(NullType()))
|
|
194
|
+
}
|
|
195
|
+
return promise
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
func getCurrentNetwork() throws -> Promise<Variant_NullType_CurrentNetworkInfo> {
|
|
199
|
+
let promise = Promise<Variant_NullType_CurrentNetworkInfo>()
|
|
200
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
201
|
+
var result: CurrentNetworkInfo? = nil
|
|
202
|
+
|
|
203
|
+
NEHotspotNetwork.fetchCurrent { network in
|
|
204
|
+
if let network = network {
|
|
205
|
+
result = CurrentNetworkInfo(
|
|
206
|
+
ssid: network.ssid,
|
|
207
|
+
bssid: network.bssid ?? ""
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
semaphore.signal()
|
|
182
211
|
}
|
|
183
212
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if
|
|
187
|
-
|
|
213
|
+
_ = semaphore.wait(timeout: .now() + 5)
|
|
214
|
+
|
|
215
|
+
if let result = result {
|
|
216
|
+
promise.resolve(withResult: .second(result))
|
|
188
217
|
} else {
|
|
189
|
-
|
|
218
|
+
promise.resolve(withResult: .first(NullType()))
|
|
190
219
|
}
|
|
220
|
+
return promise
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
func connectToNetwork(options: ConnectionOptions) throws -> Promise<Void> {
|
|
224
|
+
let promise = Promise<Void>()
|
|
225
|
+
let configuration: NEHotspotConfiguration
|
|
191
226
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
if let password = options.password {
|
|
228
|
+
if options.isWEP == true {
|
|
229
|
+
configuration = NEHotspotConfiguration(ssid: options.ssid, passphrase: password, isWEP: true)
|
|
230
|
+
} else {
|
|
231
|
+
configuration = NEHotspotConfiguration(ssid: options.ssid, passphrase: password, isWEP: false)
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
configuration = NEHotspotConfiguration(ssid: options.ssid)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
configuration.joinOnce = false
|
|
238
|
+
|
|
239
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
240
|
+
var error: Error?
|
|
241
|
+
|
|
242
|
+
NEHotspotConfigurationManager.shared.apply(configuration) { applyError in
|
|
243
|
+
error = applyError
|
|
244
|
+
semaphore.signal()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_ = semaphore.wait(timeout: .now() + 30)
|
|
248
|
+
|
|
249
|
+
if let error = error {
|
|
250
|
+
promise.reject(withError: error)
|
|
251
|
+
} else {
|
|
252
|
+
promise.resolve()
|
|
253
|
+
}
|
|
254
|
+
return promise
|
|
196
255
|
}
|
|
197
256
|
|
|
198
|
-
func
|
|
199
|
-
let
|
|
200
|
-
|
|
201
|
-
|
|
257
|
+
func disconnect() throws -> Promise<Void> {
|
|
258
|
+
let promise = Promise<Void>()
|
|
259
|
+
// On iOS, we can't directly disconnect from Wi-Fi
|
|
260
|
+
// We can only remove saved configurations
|
|
261
|
+
// This is an iOS limitation
|
|
262
|
+
// Note: This will remove the network from saved networks, not disconnect immediately
|
|
263
|
+
if let current = try? getCurrentNetworkSync() {
|
|
264
|
+
NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: current.ssid)
|
|
265
|
+
}
|
|
266
|
+
promise.resolve()
|
|
267
|
+
return promise
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
func getIPAddress() throws -> Promise<Variant_NullType_String> {
|
|
271
|
+
let promise = Promise<Variant_NullType_String>()
|
|
272
|
+
var address: String?
|
|
273
|
+
var ifaddr: UnsafeMutablePointer<ifaddrs>?
|
|
274
|
+
|
|
275
|
+
guard getifaddrs(&ifaddr) == 0 else { return nil }
|
|
276
|
+
defer { freeifaddrs(ifaddr) }
|
|
277
|
+
|
|
278
|
+
guard var ptr = ifaddr else { return nil }
|
|
279
|
+
|
|
280
|
+
while ptr != nil {
|
|
281
|
+
defer { ptr = ptr.pointee.ifa_next }
|
|
282
|
+
|
|
283
|
+
let interface = ptr.pointee
|
|
284
|
+
let addrFamily = interface.ifa_addr.pointee.sa_family
|
|
285
|
+
|
|
286
|
+
if addrFamily == UInt8(AF_INET) {
|
|
287
|
+
let name = String(cString: interface.ifa_name)
|
|
288
|
+
if name == "en0" || name == "en1" {
|
|
289
|
+
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
290
|
+
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
|
|
291
|
+
&hostname, socklen_t(hostname.count),
|
|
292
|
+
nil, socklen_t(0), NI_NUMERICHOST)
|
|
293
|
+
address = String(cString: hostname)
|
|
294
|
+
break
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if let address = address {
|
|
300
|
+
promise.resolve(withResult: .second(address))
|
|
301
|
+
} else {
|
|
302
|
+
promise.resolve(withResult: .first(NullType()))
|
|
202
303
|
}
|
|
203
|
-
return
|
|
304
|
+
return promise
|
|
204
305
|
}
|
|
205
306
|
|
|
206
307
|
func addListener(eventName: String) throws {
|
|
207
|
-
// Event listener implementation can be added here if needed
|
|
208
308
|
print("[MunimWifi] Adding listener for event: \(eventName)")
|
|
309
|
+
// Note: iOS has limited event support for Wi-Fi changes
|
|
209
310
|
}
|
|
210
311
|
|
|
211
312
|
func removeListeners(count: Double) throws {
|
|
212
|
-
// Event listener removal implementation can be added here if needed
|
|
213
313
|
print("[MunimWifi] Removing \(count) listeners")
|
|
214
314
|
}
|
|
215
315
|
}
|
|
216
|
-
|
|
217
|
-
extension Array where Element: Hashable {
|
|
218
|
-
func unique() -> [Element] {
|
|
219
|
-
var seen = Set<Element>()
|
|
220
|
-
return filter { seen.insert($0).inserted }
|
|
221
|
-
}
|
|
222
|
-
}
|