Как правильно закодировать URL (URL encode) в Swift для iOS?
Проблема: почему простое кодирование не работает
Вы столкнулись с классической проблемой: .urlQueryAllowed действительно разрешает символ &, так как технически он допустим в значении параметра. Но когда вы вставляете закодированную строку напрямую в URL, символ & интерпретируется как разделитель параметров, а не как часть значения. Это ломает запрос.
Правильное решение: используем URLComponents
Самый надежный способ кодирования URL в Swift — это использование URLComponents и URLQueryItem. Эти классы автоматически экранируют все необходимые символы, включая &, =, #, + и пробелы. Вот как это работает:
import Foundation
let baseURL = "https://api.example.com/search"
let query = "Привет, мир! & тест"
// 1. Создаем URLComponents из базового URL
guard var components = URLComponents(string: baseURL) else {
fatalError("Неверный базовый URL")
}
// 2. Создаем массив query items (параметров)
let queryItem = URLQueryItem(name: "q", value: query)
components.queryItems = [queryItem]
// 3. Получаем итоговый URL
guard let finalURL = components.url else {
fatalError("Не удалось создать URL")
}
print(finalURL.absoluteString)
// Вывод: https://api.example.com/search?q=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C%20%D0%BC%D0%B8%D1%80!%20%26%20%D1%82%D0%B5%D1%81%D1%82
Обратите внимание: символ & теперь корректно закодирован как %26, а пробелы — как %20. Кириллица тоже правильно преобразована в процентную кодировку (UTF-8).
Почему это работает?
URLComponentsзнает, какие символы должны быть закодированы в разных частях URL (схема, хост, путь, query).URLQueryItemавтоматически кодирует и имя, и значение параметра, экранируя&,=и другие опасные символы.- Итоговый URL гарантированно валиден и может быть передан в URLSession или открыт в браузере.
Что делать с несколькими параметрами?
Просто добавьте несколько URLQueryItem в массив:
let params = [
URLQueryItem(name: "q", value: "Привет, мир! & тест"),
URLQueryItem(name: "page", value: "1"),
URLQueryItem(name: "limit", value: "10")
]
components.queryItems = params
Когда можно использовать addingPercentEncoding?
Метод addingPercentEncoding(withAllowedCharacters:) полезен, когда нужно закодировать только часть строки, которая не является полным значением параметра. Например, для кодирования отдельного компонента пути:
let pathComponent = "файл с пробелом.txt"
let encodedPath = pathComponent.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
let url = URL(string: "https://example.com/\(encodedPath)")
Но для query-параметров всегда используйте URLComponents — это избавит вас от множества головных болей.
Дополнительный совет: декодирование
Если вам нужно декодировать закодированный URL обратно в читаемую строку, используйте removingPercentEncoding:
let encoded = "%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C%20%D0%BC%D0%B8%D1%80!"
let decoded = encoded.removingPercentEncoding
print(decoded ?? "") // "Привет, мир!"
В итоге: забудьте про ручное кодирование query-параметров через addingPercentEncoding. URLComponents + URLQueryItem — это стандартный и правильный путь в современном Swift.
Альтернативный подход: форматирование строки с CharacterSet
Если вы по каким-то причинам не можете использовать URLComponents (например, работаете с очень старым кодом или вам нужно закодировать только одно значение), можно расширить CharacterSet для экранирования всех опасных символов:
extension String {
func urlEncoded() -> String? {
let allowed = CharacterSet.urlQueryAllowed
.subtracting(CharacterSet(charactersIn: "&+=#%@"))
return self.addingPercentEncoding(withAllowedCharacters: allowed)
}
}
let query = "Привет, мир! & тест"
if let encoded = query.urlEncoded() {
let url = "https://api.example.com/search?q=\(encoded)"
print(url)
// https://api.example.com/search?q=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C%20%D0%BC%D0%B8%D1%80!%20%26%20%D1%82%D0%B5%D1%81%D1%82
}
Этот метод вычитает из стандартного набора .urlQueryAllowed символы, которые могут сломать URL (&, =, #, %, @).
Когда это может пригодиться?
- Если вы работаете с
URLSessionнапрямую и не хотите создавать объектыURLComponents. - Для простых случаев с одним параметром.
- Если вам нужно закодировать только значение, а имя параметра уже безопасно.
Однако помните: URLComponents — это более универсальное и поддерживаемое решение. Мой вариант — для случаев, когда нужно быстрое и простое кодирование без лишних объектов.