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.
Files changed (37) hide show
  1. package/README.md +132 -8
  2. package/ios/HybridMunimWifi.swift +248 -155
  3. package/lib/typescript/src/specs/munim-wifi.nitro.d.ts +47 -5
  4. package/lib/typescript/src/specs/munim-wifi.nitro.d.ts.map +1 -1
  5. package/nitrogen/generated/android/MunimWifi+autolinking.cmake +1 -0
  6. package/nitrogen/generated/android/c++/JConnectionOptions.hpp +66 -0
  7. package/nitrogen/generated/android/c++/JCurrentNetworkInfo.hpp +97 -0
  8. package/nitrogen/generated/android/c++/JHybridMunimWifiSpec.cpp +72 -0
  9. package/nitrogen/generated/android/c++/JHybridMunimWifiSpec.hpp +4 -0
  10. package/nitrogen/generated/android/c++/JVariant_NullType_CurrentNetworkInfo.cpp +26 -0
  11. package/nitrogen/generated/android/c++/JVariant_NullType_CurrentNetworkInfo.hpp +74 -0
  12. package/nitrogen/generated/android/c++/JWifiNetwork.hpp +9 -9
  13. package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/ConnectionOptions.kt +44 -0
  14. package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/CurrentNetworkInfo.kt +53 -0
  15. package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/HybridMunimWifiSpec.kt +16 -0
  16. package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/Variant_NullType_CurrentNetworkInfo.kt +59 -0
  17. package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimwifi/WifiNetwork.kt +3 -3
  18. package/nitrogen/generated/ios/MunimWifi-Swift-Cxx-Bridge.cpp +16 -0
  19. package/nitrogen/generated/ios/MunimWifi-Swift-Cxx-Bridge.hpp +133 -0
  20. package/nitrogen/generated/ios/MunimWifi-Swift-Cxx-Umbrella.hpp +6 -0
  21. package/nitrogen/generated/ios/c++/HybridMunimWifiSpecSwift.hpp +38 -0
  22. package/nitrogen/generated/ios/swift/ConnectionOptions.swift +66 -0
  23. package/nitrogen/generated/ios/swift/CurrentNetworkInfo.swift +113 -0
  24. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  25. package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__CurrentNetworkInfo_.swift +59 -0
  26. package/nitrogen/generated/ios/swift/HybridMunimWifiSpec.swift +4 -0
  27. package/nitrogen/generated/ios/swift/HybridMunimWifiSpec_cxx.swift +90 -0
  28. package/nitrogen/generated/ios/swift/Variant_NullType_CurrentNetworkInfo.swift +18 -0
  29. package/nitrogen/generated/ios/swift/WifiNetwork.swift +18 -6
  30. package/nitrogen/generated/shared/c++/ConnectionOptions.hpp +92 -0
  31. package/nitrogen/generated/shared/c++/CurrentNetworkInfo.hpp +105 -0
  32. package/nitrogen/generated/shared/c++/HybridMunimWifiSpec.cpp +4 -0
  33. package/nitrogen/generated/shared/c++/HybridMunimWifiSpec.hpp +10 -0
  34. package/nitrogen/generated/shared/c++/WifiNetwork.hpp +9 -9
  35. package/package.json +2 -2
  36. package/src/index.ts +45 -0
  37. 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<{ channel: number; frequency: number } | null>
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: number
396
- frequency: number
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: [CWNetwork] = []
16
+ private var scanResults: [WifiNetwork] = []
17
17
  private var isScanning = false
18
18
 
19
- private func getFrequencyChannel(frequency: Int) -> Int {
20
- // Convert frequency (MHz) to channel number
21
- if frequency >= 2412 && frequency <= 2484 {
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
- return wifiClient.interface()?.powerOn() ?? false
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
- // On iOS, Wi-Fi scanning requires location permission
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
- return status == .authorizedWhenInUse || status == .authorizedAlways
41
+ let hasPermission = status == .authorizedWhenInUse || status == .authorizedAlways
42
+ promise.resolve(withResult: hasPermission)
43
+ return promise
81
44
  }
82
45
 
83
- func scanNetworks(maxResults: Double?, timeout: Double?) throws -> [WifiNetwork] {
84
- guard let interface = wifiClient.interface() else {
85
- throw NSError(domain: "MunimWifi", code: 1, userInfo: [NSLocalizedDescriptionKey: "Wi-Fi interface not available"])
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
- guard interface.powerOn() else {
89
- throw NSError(domain: "MunimWifi", code: 2, userInfo: [NSLocalizedDescriptionKey: "Wi-Fi is not enabled"])
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
- // Check location permission
93
- let locationManager = CLLocationManager()
94
- let status = locationManager.authorizationStatus
95
- guard status == .authorizedWhenInUse || status == .authorizedAlways else {
96
- throw NSError(domain: "MunimWifi", code: 3, userInfo: [NSLocalizedDescriptionKey: "Location permission not granted"])
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
- do {
100
- let networks = try interface.scanForNetworks(withName: nil)
101
- scanResults = networks
102
-
103
- let wifiNetworks = networks.map { convertNetwork($0) }
104
- let max = maxResults.map { Int($0) } ?? wifiNetworks.count
105
- return Array(wifiNetworks.prefix(max))
106
- } catch {
107
- throw error
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(maxResults: Double?, timeout: Double?) throws {
92
+ func startScan(options: ScanOptions?) throws {
112
93
  isScanning = true
113
-
114
- guard let interface = wifiClient.interface() else {
115
- isScanning = false
116
- return
117
- }
118
-
119
- guard interface.powerOn() else {
120
- isScanning = false
121
- return
122
- }
123
-
124
- // Check location permission
125
- let locationManager = CLLocationManager()
126
- let status = locationManager.authorizationStatus
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 networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
152
- return networks.compactMap { $0.ssid }.unique()
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 networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
157
- let wifiNetworks = networks.map { convertNetwork($0) }
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
- return WifiFingerprint(
160
- networks: wifiNetworks,
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 -> Double? {
166
- let networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
167
- let network = networks.first { $0.ssid == ssid }
168
- return network.map { Double($0.rssiValue) }
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 -> String? {
172
- let networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
173
- let network = networks.first { $0.ssid == ssid }
174
- return network?.bssid
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 getChannelInfo(ssid: String) throws -> ChannelInfo? {
178
- let networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
179
- guard let network = networks.first(where: { $0.ssid == ssid }),
180
- let channel = network.wlanChannel else {
181
- return nil
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
- let channelNumber = Int(channel.channelNumber)
185
- let frequency: Int
186
- if channel.channelBand == .band2GHz {
187
- frequency = 2412 + (channelNumber - 1) * 5
213
+ _ = semaphore.wait(timeout: .now() + 5)
214
+
215
+ if let result = result {
216
+ promise.resolve(withResult: .second(result))
188
217
  } else {
189
- frequency = 5170 + (channelNumber - 36) * 5
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
- return ChannelInfo(
193
- channel: channelNumber,
194
- frequency: frequency
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 getNetworkInfo(ssid: String) throws -> WifiNetwork? {
199
- let networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
200
- guard let network = networks.first(where: { $0.ssid == ssid }) else {
201
- return nil
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 convertNetwork(network)
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
- }