#import #import #import #import #import static CLLocationManager *locationManager = nil; // UnitySendMessage 声明 extern void UnitySendMessage(const char *obj, const char *method, const char *msg); // 授权变化回调目标(通过 UnitySendMessage 通知 C#) static const char *callbackGameObject = nil; static const char *callbackMethod = nil; // GetWiFiSSID 前向声明(定义在 extern "C" 块中) extern "C" const char* GetWiFiSSID(); // 是否正在等待定位更新(收到第一个位置后立即获取 SSID) static BOOL pendingSSIDFetch = NO; // 延迟获取 SSID 并回调 C# static void FetchSSIDAndCallback() { char *ssid = (char *)GetWiFiSSID(); if (callbackGameObject != nil && callbackMethod != nil) { if (ssid != NULL) { UnitySendMessage(callbackGameObject, callbackMethod, ssid); free(ssid); } else { UnitySendMessage(callbackGameObject, callbackMethod, ""); } } } @interface WiFiHelperLocationDelegate : NSObject @end @implementation WiFiHelperLocationDelegate - (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager { if (@available(iOS 14.0, *)) { // iOS 14+ 新回调:参数是 manager,需自行查询授权状态 CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) { NSLog(@"[WiFiHelper] Location permission granted after request"); [WiFiHelperLocationDelegate startSSIDFetchFlow]; } else if (status == kCLAuthorizationStatusDenied || status == kCLAuthorizationStatusRestricted) { NSLog(@"[WiFiHelper] Location permission denied after request"); if (callbackGameObject != nil && callbackMethod != nil) { UnitySendMessage(callbackGameObject, callbackMethod, ""); } } } } - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { if (@available(iOS 14.0, *)) return; // iOS 14+ 由上面的新回调处理 // iOS 13 及以下用旧回调方法 if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) { NSLog(@"[WiFiHelper] Location permission granted after request (legacy callback)"); [WiFiHelperLocationDelegate startSSIDFetchFlow]; } else if (status == kCLAuthorizationStatusDenied || status == kCLAuthorizationStatusRestricted) { NSLog(@"[WiFiHelper] Location permission denied after request (legacy callback)"); if (callbackGameObject != nil && callbackMethod != nil) { UnitySendMessage(callbackGameObject, callbackMethod, ""); } } } // 收到第一个位置更新后再获取 SSID(NEHotspotNetwork.fetchCurrent 的隐式要求) - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { if (pendingSSIDFetch) { NSLog(@"[WiFiHelper] First location update received, fetching SSID now"); pendingSSIDFetch = NO; [manager stopUpdatingLocation]; FetchSSIDAndCallback(); } } // 定位失败时也兜底尝试获取一次 - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"[WiFiHelper] Location manager failed: %@", error.localizedDescription); if (pendingSSIDFetch) { pendingSSIDFetch = NO; [manager stopUpdatingLocation]; FetchSSIDAndCallback(); } } // 启动定位并等待第一个位置更新,超时则直接获取 + (void)startSSIDFetchFlow { if (![CLLocationManager locationServicesEnabled]) { NSLog(@"[WiFiHelper] Location services disabled on device, fetching SSID anyway"); FetchSSIDAndCallback(); return; } if (locationManager == nil) { locationManager = [[CLLocationManager alloc] init]; } pendingSSIDFetch = YES; [locationManager startUpdatingLocation]; // 超时兜底:定位迟迟无响应(如首次定位慢)时,3 秒后直接获取 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ if (pendingSSIDFetch) { NSLog(@"[WiFiHelper] Timeout waiting for location update, fetching SSID anyway"); pendingSSIDFetch = NO; [locationManager stopUpdatingLocation]; FetchSSIDAndCallback(); } }); } @end static WiFiHelperLocationDelegate *locationDelegate = nil; extern "C" { void RequestLocationPermission() { if (locationManager != nil) return; locationManager = [[CLLocationManager alloc] init]; CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) { NSLog(@"[WiFiHelper] Location permission already granted (status: %d)", (int)status); // 位置权限已获得但还需要启动定位才能让WiFi API工作 [locationManager startUpdatingLocation]; // 短暂启动后停止(不需要持续定位,只是激活权限链) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [locationManager stopUpdatingLocation]; }); return; } if (status == kCLAuthorizationStatusNotDetermined) { NSLog(@"[WiFiHelper] Requesting WhenInUse location permission"); [locationManager requestWhenInUseAuthorization]; } else { NSLog(@"[WiFiHelper] Location permission denied/restricted (status: %d)", (int)status); } } /// /// 请求位置权限(如未授权),授权完成后自动获取 WiFi 名称并回调 C#。 /// 已授权时立即获取并回调。 /// void RequestWiFiSSIDNative(const char *gameObjectName, const char *methodName) { if (callbackGameObject != nil) { free((void *)callbackGameObject); } if (callbackMethod != nil) { free((void *)callbackMethod); } callbackGameObject = gameObjectName ? strdup(gameObjectName) : nil; callbackMethod = methodName ? strdup(methodName) : nil; if (locationManager == nil) { locationManager = [[CLLocationManager alloc] init]; } if (locationDelegate == nil) { locationDelegate = [[WiFiHelperLocationDelegate alloc] init]; } locationManager.delegate = locationDelegate; CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) { NSLog(@"[WiFiHelper] Location already granted, fetching SSID directly"); [WiFiHelperLocationDelegate startSSIDFetchFlow]; return; } if (status == kCLAuthorizationStatusNotDetermined) { NSLog(@"[WiFiHelper] Requesting location permission, will fetch SSID on grant"); [locationManager requestWhenInUseAuthorization]; } else { NSLog(@"[WiFiHelper] Location permission denied/restricted (status: %d)", (int)status); if (callbackGameObject != nil && callbackMethod != nil) { UnitySendMessage(callbackGameObject, callbackMethod, ""); } } } const char* GetWiFiSSID() { CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; BOOL locationEnabled = [CLLocationManager locationServicesEnabled]; NSLog(@"[WiFiHelper] GetWiFiSSID: auth=%d servicesEnabled=%d", (int)status, (int)locationEnabled); __block NSString *resultSSID = nil; __block BOOL finished = NO; if (@available(iOS 14.0, *)) { NSLog(@"[WiFiHelper] Using NEHotspotNetwork.fetchCurrent"); [NEHotspotNetwork fetchCurrentWithCompletionHandler:^(NEHotspotNetwork * _Nullable network) { if (network && network.SSID.length > 0) { resultSSID = network.SSID; } else { NSLog(@"[WiFiHelper] NEHotspotNetwork fetchCurrent returned nil"); } finished = YES; }]; // 用 CFRunLoop 等待,不阻塞主线程事件处理 NSTimeInterval deadline = [[NSDate date] timeIntervalSinceReferenceDate] + 3.0; while (!finished && [[NSDate date] timeIntervalSinceReferenceDate] < deadline) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; } if (resultSSID != nil) { NSLog(@"[WiFiHelper] NEHotspotNetwork SSID: %@", resultSSID); } else { NSLog(@"[WiFiHelper] NEHotspotNetwork returned nil (finished=%d)", finished); } } // Fallback if (resultSSID == nil) { NSLog(@"[WiFiHelper] Fallback to CNCopyCurrentNetworkInfo"); NSArray *interfaces = (__bridge_transfer NSArray *)CNCopySupportedInterfaces(); if (interfaces != nil) { for (NSString *interfaceName in interfaces) { NSDictionary *info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName); if (info && info[(NSString *)kCNNetworkInfoKeySSID]) { resultSSID = info[(NSString *)kCNNetworkInfoKeySSID]; break; } } } } if (resultSSID == nil) { NSLog(@"[WiFiHelper] No WiFi SSID found."); return NULL; } NSLog(@"[WiFiHelper] SSID: %@", resultSSID); const char *utf8String = [resultSSID UTF8String]; size_t len = strlen(utf8String) + 1; char *result = (char *)malloc(len); if (result == NULL) return NULL; memcpy(result, utf8String, len); return result; } }