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.
Files changed (37) hide show
  1. package/README.md +132 -8
  2. package/ios/HybridMunimWifi.swift +178 -145
  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,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: [CWNetwork] = []
15
+ private var scanResults: [WifiNetwork] = []
17
16
  private var isScanning = false
18
17
 
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
- }
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
- return wifiClient.interface()?.powerOn() ?? false
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
- 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
- }
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
- guard interface.powerOn() else {
89
- throw NSError(domain: "MunimWifi", code: 2, userInfo: [NSLocalizedDescriptionKey: "Wi-Fi is not enabled"])
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
- // 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
- }
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
- 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
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
- 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
- }
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
- let networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
152
- return networks.compactMap { $0.ssid }.unique()
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
- let networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
157
- let wifiNetworks = networks.map { convertNetwork($0) }
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: wifiNetworks,
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
- 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) }
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 networks = scanResults.isEmpty ? (try? wifiClient.interface()?.scanForNetworks(withName: nil)) ?? [] : scanResults
173
- let network = networks.first { $0.ssid == ssid }
174
- return network?.bssid
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
- 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
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
- let channelNumber = Int(channel.channelNumber)
185
- let frequency: Int
186
- if channel.channelBand == .band2GHz {
187
- frequency = 2412 + (channelNumber - 1) * 5
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
- frequency = 5170 + (channelNumber - 36) * 5
186
+ configuration = NEHotspotConfiguration(ssid: options.ssid)
190
187
  }
191
188
 
192
- return ChannelInfo(
193
- channel: channelNumber,
194
- frequency: frequency
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 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
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
- return convertNetwork(network)
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
- }