Как правильно кодировать URL в Swift? Проблема с кириллицей и спецсимволами
Правильный способ кодирования URL в Swift
Проблема, с которой вы столкнулись, очень распространена. Главное правило: кодировать нужно каждый компонент URL отдельно, а не всю строку целиком. Это связано с тем, что разные части URL имеют разные допустимые наборы символов.
Основное решение
Используйте addingPercentEncoding(withAllowedCharacters:) с правильным набором символов для каждой части URL. Для query-параметров (часть после ?) лучше всего использовать .urlQueryAllowed, но с одним важным дополнением — он не кодирует символ & и =, которые могут сломать URL, если они есть в значении параметра.
// Правильный способ кодирования параметров запроса
let searchText = "Москва центр & Подмосковье"
let encodedQuery = searchText.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
// Результат: "Москва%20центр%20&%20Подмосковье"
// Проблема: & не закодирован!
Поэтому для полной безопасности лучше создать свой набор символов:
extension String {
func urlEncoded() -> String? {
let allowedCharacters = CharacterSet.urlQueryAllowed
.subtracting(CharacterSet(charactersIn: "&=?/#%+"))
return self.addingPercentEncoding(withAllowedCharacters: allowedCharacters)
}
}
// Использование
let searchText = "Москва центр & Подмосковье"
if let encoded = searchText.urlEncoded() {
let urlString = "https://api.example.com/search?q=\(encoded)"
print(urlString) // https://api.example.com/search?q=%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%20%D1%86%D0%B5%D0%BD%D1%82%D1%80%20%26%20%D0%9F%D0%BE%D0%B4%D0%BC%D0%BE%D1%81%D0%BA%D0%BE%D0%B2%D1%8C%D0%B5
}
Работа с несколькими параметрами
Для удобства работы с несколькими параметрами рекомендую использовать URLComponents:
func buildURL(base: String, params: [String: String]) -> URL? {
guard var components = URLComponents(string: base) else { return nil }
components.queryItems = params.map { (key, value) in
URLQueryItem(name: key, value: value)
}
return components.url
}
// Пример использования
let params = [
"q": "Москва центр",
"category": "недвижимость",
"page": "1"
]
if let url = buildURL(base: "https://api.example.com/search", params: params) {
print(url.absoluteString)
// https://api.example.com/search?q=%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%20%D1%86%D0%B5%D0%BD%D1%82%D1%80&category=%D0%BD%D0%B5%D0%B4%D0%B2%D0%B8%D0%B6%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C&page=1
}
URLComponents автоматически правильно кодирует каждый параметр, учитывая все тонкости. Это самый надёжный и рекомендуемый способ.
Важные моменты
- Не кодируйте уже закодированные строки. Если вы получили данные, которые уже содержат %20, повторное кодирование превратит %20 в %2520, что сломает запрос.
- Кодируйте только значения, не ключи. Ключи параметров обычно не содержат спецсимволов, но если сомневаетесь — кодируйте и их.
- Для path-компонентов (части URL до ?) используйте
.urlPathAllowed. - Для host-компонентов используйте
.urlHostAllowed.
Полный пример с обработкой ошибок
func search(query: String, completion: @escaping (Result) -> Void) {
let params = ["q": query]
guard var components = URLComponents(string: "https://api.example.com/search") else {
completion(.failure(URLError(.badURL)))
return
}
components.queryItems = params.map { URLQueryItem(name: $0.key, value: $0.value) }
guard let url = components.url else {
completion(.failure(URLError(.badURL)))
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
} else if let data = data {
completion(.success(data))
}
}.resume()
}
Используйте URLComponents — это стандартный и безопасный способ работы с URL в Swift. Он избавит вас от множества проблем с кодированием.
Альтернативный подход: использование Alamofire
Если вы используете популярную библиотеку Alamofire для сетевых запросов, то вся работа с кодированием URL берётся на себя библиотекой. Вам не нужно вручную кодировать параметры:
import Alamofire
let parameters: [String: String] = [
"q": "Москва центр",
"category": "недвижимость"
]
AF.request("https://api.example.com/search",
parameters: parameters)
.responseJSON { response in
// Обработка ответа
}
По умолчанию Alamofire использует URLEncoding.default, который правильно кодирует все параметры. Если вам нужно изменить поведение (например, использовать query-строку вместо тела запроса для POST), можно явно указать:
AF.request(url,
parameters: parameters,
encoding: URLEncoding.queryString)
Этот подход особенно удобен для больших проектов, где много сетевых запросов. Однако для простых проектов или изучения Swift рекомендую сначала освоить работу с URLComponents из первого ответа — это даст лучшее понимание того, как всё работает "под капотом".
Ответов пока нет.
Войдите чтобы оставить ответ