munim-wifi 0.1.0 → 0.1.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/README.md +132 -8
- package/ios/HybridMunimWifi.swift +178 -145
- 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,73 +6,29 @@
|
|
|
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
12
|
|
|
13
13
|
class HybridMunimWifi: HybridMunimWifiSpec {
|
|
14
|
-
private let wifiClient = CWWiFiClient.shared()
|
|
15
14
|
private var locationManager: CLLocationManager?
|
|
16
|
-
private var scanResults: [
|
|
15
|
+
private var scanResults: [WifiNetwork] = []
|
|
17
16
|
private var isScanning = false
|
|
18
17
|
|
|
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
|
-
}
|
|
18
|
+
// Note: iOS has very limited Wi-Fi scanning capabilities
|
|
19
|
+
// CoreWLAN is macOS only - not available on iOS
|
|
20
|
+
// iOS can only get SSID/BSSID, not RSSI, channel, or frequency
|
|
69
21
|
|
|
70
22
|
func isWifiEnabled() throws -> Bool {
|
|
71
|
-
|
|
23
|
+
// On iOS, we can't directly check if Wi-Fi is enabled
|
|
24
|
+
// We can infer it by checking if we can get current network
|
|
25
|
+
if let _ = try? getCurrentNetworkSync() {
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
return false
|
|
72
29
|
}
|
|
73
30
|
|
|
74
31
|
func requestWifiPermission() throws -> Bool {
|
|
75
|
-
// On iOS, Wi-Fi scanning requires location permission
|
|
76
32
|
let locationManager = CLLocationManager()
|
|
77
33
|
self.locationManager = locationManager
|
|
78
34
|
|
|
@@ -80,67 +36,68 @@ class HybridMunimWifi: HybridMunimWifiSpec {
|
|
|
80
36
|
return status == .authorizedWhenInUse || status == .authorizedAlways
|
|
81
37
|
}
|
|
82
38
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
39
|
+
// Helper to get current network synchronously
|
|
40
|
+
private func getCurrentNetworkSync() -> CurrentNetworkInfo? {
|
|
41
|
+
var result: CurrentNetworkInfo? = nil
|
|
42
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
87
43
|
|
|
88
|
-
|
|
89
|
-
|
|
44
|
+
NEHotspotNetwork.fetchCurrent { network in
|
|
45
|
+
if let network = network {
|
|
46
|
+
result = CurrentNetworkInfo(
|
|
47
|
+
ssid: network.ssid,
|
|
48
|
+
bssid: network.bssid ?? ""
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
semaphore.signal()
|
|
90
52
|
}
|
|
91
53
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
54
|
+
_ = semaphore.wait(timeout: .now() + 5)
|
|
55
|
+
return result
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func scanNetworks(maxResults: Double?, timeout: Double?) throws -> [WifiNetwork] {
|
|
59
|
+
// iOS limitation: Cannot scan for networks directly
|
|
60
|
+
// Only can get current connected network
|
|
61
|
+
// For scanning, we can only return the current network if connected
|
|
62
|
+
// This is a major iOS limitation
|
|
98
63
|
|
|
99
|
-
|
|
100
|
-
let
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
64
|
+
if let current = try? getCurrentNetworkSync() {
|
|
65
|
+
let network = WifiNetwork(
|
|
66
|
+
ssid: current.ssid,
|
|
67
|
+
bssid: current.bssid,
|
|
68
|
+
rssi: nil, // Not available on iOS
|
|
69
|
+
frequency: nil, // Not available on iOS
|
|
70
|
+
channel: nil, // Not available on iOS
|
|
71
|
+
capabilities: nil,
|
|
72
|
+
isSecure: nil,
|
|
73
|
+
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
74
|
+
)
|
|
75
|
+
scanResults = [network]
|
|
76
|
+
return [network]
|
|
108
77
|
}
|
|
78
|
+
|
|
79
|
+
// Return empty array if not connected
|
|
80
|
+
return []
|
|
109
81
|
}
|
|
110
82
|
|
|
111
83
|
func startScan(maxResults: Double?, timeout: Double?) throws {
|
|
112
84
|
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
|
-
}
|
|
85
|
+
// iOS limitation: Cannot continuously scan
|
|
86
|
+
// Just get current network once
|
|
87
|
+
if let current = try? getCurrentNetworkSync() {
|
|
88
|
+
let network = WifiNetwork(
|
|
89
|
+
ssid: current.ssid,
|
|
90
|
+
bssid: current.bssid,
|
|
91
|
+
rssi: nil,
|
|
92
|
+
frequency: nil,
|
|
93
|
+
channel: nil,
|
|
94
|
+
capabilities: nil,
|
|
95
|
+
isSecure: nil,
|
|
96
|
+
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
97
|
+
)
|
|
98
|
+
scanResults = [network]
|
|
143
99
|
}
|
|
100
|
+
isScanning = false
|
|
144
101
|
}
|
|
145
102
|
|
|
146
103
|
func stopScan() throws {
|
|
@@ -148,75 +105,151 @@ class HybridMunimWifi: HybridMunimWifiSpec {
|
|
|
148
105
|
}
|
|
149
106
|
|
|
150
107
|
func getSSIDs() throws -> [String] {
|
|
151
|
-
|
|
152
|
-
|
|
108
|
+
// iOS limitation: Can only get current network SSID
|
|
109
|
+
if let current = try? getCurrentNetworkSync() {
|
|
110
|
+
return [current.ssid]
|
|
111
|
+
}
|
|
112
|
+
return []
|
|
153
113
|
}
|
|
154
114
|
|
|
155
115
|
func getWifiFingerprint() throws -> WifiFingerprint {
|
|
156
|
-
|
|
157
|
-
let
|
|
116
|
+
// iOS limitation: Can only get current network, no RSSI/channel/frequency
|
|
117
|
+
let networks = scanResults.isEmpty ? (try? scanNetworks(maxResults: nil, timeout: nil)) ?? [] : scanResults
|
|
158
118
|
|
|
159
119
|
return WifiFingerprint(
|
|
160
|
-
networks:
|
|
120
|
+
networks: networks,
|
|
161
121
|
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
162
122
|
)
|
|
163
123
|
}
|
|
164
124
|
|
|
165
125
|
func getRSSI(ssid: String) throws -> Double? {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return network.map { Double($0.rssiValue) }
|
|
126
|
+
// iOS limitation: RSSI not available for scanned networks
|
|
127
|
+
return nil
|
|
169
128
|
}
|
|
170
129
|
|
|
171
130
|
func getBSSID(ssid: String) throws -> String? {
|
|
172
|
-
let
|
|
173
|
-
|
|
174
|
-
|
|
131
|
+
if let current = try? getCurrentNetworkSync(), current.ssid == ssid {
|
|
132
|
+
return current.bssid
|
|
133
|
+
}
|
|
134
|
+
return nil
|
|
175
135
|
}
|
|
176
136
|
|
|
177
137
|
func getChannelInfo(ssid: String) throws -> ChannelInfo? {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
138
|
+
// iOS limitation: Channel and frequency not available
|
|
139
|
+
return nil
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
func getNetworkInfo(ssid: String) throws -> WifiNetwork? {
|
|
143
|
+
if let current = try? getCurrentNetworkSync(), current.ssid == ssid {
|
|
144
|
+
return WifiNetwork(
|
|
145
|
+
ssid: current.ssid,
|
|
146
|
+
bssid: current.bssid,
|
|
147
|
+
rssi: nil, // Not available on iOS
|
|
148
|
+
frequency: nil, // Not available on iOS
|
|
149
|
+
channel: nil, // Not available on iOS
|
|
150
|
+
capabilities: nil,
|
|
151
|
+
isSecure: nil,
|
|
152
|
+
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
return nil
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
func getCurrentNetwork() throws -> CurrentNetworkInfo? {
|
|
159
|
+
var result: CurrentNetworkInfo? = nil
|
|
160
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
161
|
+
|
|
162
|
+
NEHotspotNetwork.fetchCurrent { network in
|
|
163
|
+
if let network = network {
|
|
164
|
+
result = CurrentNetworkInfo(
|
|
165
|
+
ssid: network.ssid,
|
|
166
|
+
bssid: network.bssid ?? ""
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
semaphore.signal()
|
|
182
170
|
}
|
|
183
171
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
172
|
+
_ = semaphore.wait(timeout: .now() + 5)
|
|
173
|
+
return result
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
func connectToNetwork(options: ConnectionOptions) throws {
|
|
177
|
+
let configuration: NEHotspotConfiguration
|
|
178
|
+
|
|
179
|
+
if let password = options.password {
|
|
180
|
+
if options.isWEP == true {
|
|
181
|
+
configuration = NEHotspotConfiguration(ssid: options.ssid, passphrase: password, isWEP: true)
|
|
182
|
+
} else {
|
|
183
|
+
configuration = NEHotspotConfiguration(ssid: options.ssid, passphrase: password, isWEP: false)
|
|
184
|
+
}
|
|
188
185
|
} else {
|
|
189
|
-
|
|
186
|
+
configuration = NEHotspotConfiguration(ssid: options.ssid)
|
|
190
187
|
}
|
|
191
188
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
189
|
+
configuration.joinOnce = false
|
|
190
|
+
|
|
191
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
192
|
+
var error: Error?
|
|
193
|
+
|
|
194
|
+
NEHotspotConfigurationManager.shared.apply(configuration) { applyError in
|
|
195
|
+
error = applyError
|
|
196
|
+
semaphore.signal()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_ = semaphore.wait(timeout: .now() + 30)
|
|
200
|
+
|
|
201
|
+
if let error = error {
|
|
202
|
+
throw error
|
|
203
|
+
}
|
|
196
204
|
}
|
|
197
205
|
|
|
198
|
-
func
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
206
|
+
func disconnect() throws {
|
|
207
|
+
// On iOS, we can't directly disconnect from Wi-Fi
|
|
208
|
+
// We can only remove saved configurations
|
|
209
|
+
// This is an iOS limitation
|
|
210
|
+
// Note: This will remove the network from saved networks, not disconnect immediately
|
|
211
|
+
if let current = try? getCurrentNetworkSync() {
|
|
212
|
+
NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: current.ssid)
|
|
202
213
|
}
|
|
203
|
-
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
func getIPAddress() throws -> String? {
|
|
217
|
+
var address: String?
|
|
218
|
+
var ifaddr: UnsafeMutablePointer<ifaddrs>?
|
|
219
|
+
|
|
220
|
+
guard getifaddrs(&ifaddr) == 0 else { return nil }
|
|
221
|
+
defer { freeifaddrs(ifaddr) }
|
|
222
|
+
|
|
223
|
+
guard var ptr = ifaddr else { return nil }
|
|
224
|
+
|
|
225
|
+
while ptr != nil {
|
|
226
|
+
defer { ptr = ptr.pointee.ifa_next }
|
|
227
|
+
|
|
228
|
+
let interface = ptr.pointee
|
|
229
|
+
let addrFamily = interface.ifa_addr.pointee.sa_family
|
|
230
|
+
|
|
231
|
+
if addrFamily == UInt8(AF_INET) {
|
|
232
|
+
let name = String(cString: interface.ifa_name)
|
|
233
|
+
if name == "en0" || name == "en1" {
|
|
234
|
+
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
235
|
+
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
|
|
236
|
+
&hostname, socklen_t(hostname.count),
|
|
237
|
+
nil, socklen_t(0), NI_NUMERICHOST)
|
|
238
|
+
address = String(cString: hostname)
|
|
239
|
+
break
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return address
|
|
204
245
|
}
|
|
205
246
|
|
|
206
247
|
func addListener(eventName: String) throws {
|
|
207
|
-
// Event listener implementation can be added here if needed
|
|
208
248
|
print("[MunimWifi] Adding listener for event: \(eventName)")
|
|
249
|
+
// Note: iOS has limited event support for Wi-Fi changes
|
|
209
250
|
}
|
|
210
251
|
|
|
211
252
|
func removeListeners(count: Double) throws {
|
|
212
|
-
// Event listener removal implementation can be added here if needed
|
|
213
253
|
print("[MunimWifi] Removing \(count) listeners")
|
|
214
254
|
}
|
|
215
255
|
}
|
|
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
|
-
}
|