You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift UIKit天气APP API通信Bug:非拉丁字符请求失败排查

Swift UIKit天气应用非拉丁字符API请求问题

问题现象

  • 含非拉丁字符的城市名(如"Eskişehir")请求失败,转成纯拉丁字符("Eskisehir")后请求正常
  • 直接在Chrome地址栏粘贴带非拉丁字符的URL能返回有效数据,但Xcode模拟器中执行相同请求无结果

相关代码

//=====================View controller ==========

func textFieldDidEndEditing(_ textField: UITextField) {
    if let city = searchText.text{
        weatherManager.fetchWeather(city)
    }
    searchText.text = ""
}

func weatherDataFetched(_ weatherDataModel: WeatherDataModel){
    tempLabel.text = weatherDataModel.tempString
    cityLabel.text = weatherDataModel.name
    weatherImage.image = UIImage(systemName: weatherDataModel.weatherStatus)
}


//========================== Weather manager side=====================

struct WeatherManager{
static let apiKey = "bla bla bla"
static let units = "metric"
var url = "https://api.openweathermap.org/data/2.5/weather?units=\(units)&appid=\(apiKey)"

var delegate : WeatherManagerDelegation?

func fetchWeather(_ cityName:String){
    let fullUrl = "\(url)&q=\(cityName)"
    sendRequest(fullUrl)
}

func sendRequest(_ fullUrl: String){
    if let url = URL(string: fullUrl){
        let urlSession = URLSession(configuration: .default)
        let dataTask = urlSession.dataTask(with: url) { data, response, error in
            
            if  error != nil{
                print("DataTask Error!")
                return
            }
            
            if let checkedData = data{
                if let weatherDataModel = self.parseJSON(weatherData: checkedData){
                    
                    DispatchQueue.main.async {
                        self.delegate?.weatherDataFetched(weatherDataModel)
                    }
                }
            }
        }
        
        dataTask.resume()
    }
}

func parseJSON(weatherData: Data) -> WeatherDataModel?{
    let decoder = JSONDecoder()
    do{
        let decodedModel = try decoder.decode(WeatherData.self, from: weatherData)
        let id = decodedModel.weather[0].id
        let temp = decodedModel.main.temp
        let name = decodedModel.name
        let weatherDataModel = WeatherDataModel(id: id, name: name, temp: temp)
        print(temp)
        return weatherDataModel
        
    } catch{
        print("Json parsing error")
        return nil
    }
    
}
}


//===============Weather Data ================

struct WeatherData : Decodable{
let name: String
let main: Main
let weather: [Weather]
}

struct Main : Decodable{
let temp: Double
}

struct Weather : Decodable{
let id: Int
}

//============== Weather Data Model =================
struct WeatherDataModel{

let id: Int
let name: String
let temp: Double

var tempString: String{
    return String(format: "%.1f", temp)
}

var weatherStatus: String{
    switch id{
    case 200...299:
        return Constants.bolt
    case 300...399:
        return Constants.drizzle
    case 500...599:
        return Constants.rain
    case 600...699:
        return Constants.snow
    case 700...799:
        return Constants.fog
    case 800:
        return Constants.sun
    case 801...900:
        return Constants.bolt
    default:
        return Constants.cloud
    }
}

init(id: Int, name: String, temp: Double) {
    self.id = id
    self.name = name
    self.temp = temp
}

}

问题原因

核心原因是URL未进行正确的百分号编码:

  • 浏览器会自动对URL中的非ASCII字符(比如"ş")进行百分号编码(转成%C5%9F),所以直接在地址栏能正常请求
  • 但Swift中直接拼接含非拉丁字符的字符串生成URL时,URL(string:)会返回nil,导致后续请求根本没执行(代码里sendRequest方法中如果URL(string: fullUrl)返回nil,就不会创建dataTask,自然无结果)

解决方案

修改fetchWeather方法,对城市名称进行URL编码:

func fetchWeather(_ cityName: String) {
    // 对城市名称进行URL编码,指定编码格式为UTF-8
    guard let encodedCityName = cityName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
        print("城市名称编码失败")
        return
    }
    let fullUrl = "\(url)&q=\(encodedCityName)"
    sendRequest(fullUrl)
}
  • addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)会把所有不符合URL查询参数规范的字符(包括非拉丁字符)转换成百分号编码格式,确保URL合法
  • 编码后的URL会和浏览器自动处理的格式一致,API就能正常识别请求

后续问题解答

1. 支持用户用母语搜索是否有难度?

难度不大,只要做好两步:

  • 确保城市名称的URL编码正确(上面的解决方案已经覆盖)
  • 确认OpenWeatherMap API支持对应语言的城市名称搜索:OpenWeatherMap本身支持多语言城市名称查询,只要编码正确,大部分母语城市名(含非拉丁字符)都能被识别

2. 如何限制用户仅使用拉丁字符?

可以通过文本输入框的UITextFieldDelegate方法限制输入内容:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // 定义允许的拉丁字符集合:字母、空格、连字符等(可根据需求调整)
    let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -")
    let characterSet = CharacterSet(charactersIn: string)
    return allowedCharacters.isSuperset(of: characterSet)
}
  • 实现这个方法后,用户输入非拉丁字符时会被拦截,无法输入到文本框中
  • 也可以配合输入验证,在用户提交时提示只能输入拉丁字符

内容的提问来源于stack exchange,提问作者Anıl Karadağ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 13:18:09