Swift encode URL: почему ваш код ломается на кириллице и пробелах
Каждый разработчик, который хоть раз отправлял сетевой запрос на Swift, сталкивался с этой болью: вы формируете URL, подставляете параметры, а сервер возвращает 400 ошибку или, что еще хуже, — молча игнорирует запрос. Знакомо? Чаще всего проблема кроется в неправильном кодировании URL. Казалось бы, что может быть проще — взял строку, заменил пробелы на %20, и готово. Но Swift (как и любой строгий язык) не прощает халтуры.
В этой статье мы разберем 5 самых распространенных ошибок при Swift encode URL, которые собирают тысячи просмотров на Stack Overflow. Вы узнаете, как их избежать и писать надежный код, который работает с любыми данными — от английских букв до сложной кириллицы и спецсимволов.
Ошибка №1: Использование addingPercentEncoding с неправильным набором символов
Это, пожалуй, самая популярная ловушка. Многие новички видят метод addingPercentEncoding(withAllowedCharacters:) и думают: "О, идеально!". Но передают туда .urlHostAllowed или .urlPathAllowed для всего URL целиком. Результат — краш или некорректный запрос.
Почему это не работает?
Каждая часть URL (схема, хост, путь, query-параметры) имеет свой разрешенный набор символов. Если вы кодируете весь URL как хост, вы потеряете слеши и двоеточия. Если как путь — сломаете query-строку.
// ❌ Ошибка: кодируем весь URL как хост
let urlString = "https://example.com/путь?name=Иван & фамилия=Петров"
let encoded = urlString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
// Результат: "https%3A%2F%2Fexample.com%2F%D0%BF%D1%83%D1%82%D1%8C%3Fname%3D%D0%98%D0%B2%D0%B0%D0%BD%20%26%20%D1%84%D0%B0%D0%BC%D0%B8%D0%BB%D0%B8%D1%8F%3D%D0%9F%D0%B5%D1%82%D1%80%D0%BE%D0%B2"
// Сервер не поймет такой URL
Запомните золотое правило: кодируйте каждую часть URL отдельно или используйте URLComponents.
Правильный подход: URLComponents
// ✅ Правильно: используем URLComponents
var components = URLComponents()
components.scheme = "https"
components.host = "example.com"
components.path = "/путь"
components.queryItems = [
URLQueryItem(name: "name", value: "Иван & Петров"),
URLQueryItem(name: "фамилия", value: "Иванов")
]
if let url = components.url {
print(url.absoluteString)
// "https://example.com/%D0%BF%D1%83%D1%82%D1%8C?name=%D0%98%D0%B2%D0%B0%D0%BD%20%26%20%D0%9F%D0%B5%D1%82%D1%80%D0%BE%D0%B2&%D1%84%D0%B0%D0%BC%D0%B8%D0%BB%D0%B8%D1%8F=%D0%98%D0%B2%D0%B0%D0%BD%D0%BE%D0%B2"
}
URLComponents автоматически применяет правильное percent encoding для каждого компонента. Это избавляет вас от головной боли и делает код чище.
Ошибка №2: Игнорирование queryItems в пользу ручной сборки строки
"Зачем мне эти ваши компоненты? Я просто склею строку через +". Знакомая мысль? И это ведет ко второй популярной ошибке. Ручная сборка query-строки — прямой путь к багам.
// ❌ Ошибка: ручная сборка query-строки
let name = "Иван & Петров"
let query = "name=\(name)&age=25"
let urlString = "https://example.com?\(query)"
// Проблема: амперсанд в имени будет воспринят как разделитель параметров
Даже если вы замените пробелы, вы забудете про &, #, ? и другие спецсимволы. А если параметр придет от пользователя? Ждите инъекций и сломанных запросов.
Решение: всегда используйте queryItems
// ✅ Правильно: queryItems делает всю работу за вас
var components = URLComponents(string: "https://example.com")!
components.queryItems = [
URLQueryItem(name: "name", value: "Иван & Петров"),
URLQueryItem(name: "age", value: "25"),
URLQueryItem(name: "q", value: "Swift encode URL tutorial")
]
if let url = components.url {
print(url.absoluteString)
// name=Иван%20%26%20Петров&age=25&q=Swift%20encode%20URL%20tutorial
}
Этот подход гарантирует корректное URL encoding Swift для всех параметров. Вы просто передаете сырые значения, а система сама решает, какие символы нужно экранировать.
Ошибка №3: Забываем про кодирование пути (path)
Третья ошибка — когда разработчики кодируют query-параметры, но забывают про путь. Кириллица в пути — частая причина 404 ошибок.
// ❌ Ошибка: кириллица в пути без кодирования
let url = URL(string: "https://example.com/каталог/товар?id=1")
// nil! URL инициализатор вернет nil, так как строка невалидна
Swift строг: если в строке есть недопустимые символы, URL(string:) вернет nil. И ваше приложение либо упадет, либо тихо ничего не сделает.
Решение: кодируем путь отдельно
// ✅ Правильно: кодируем путь через addingPercentEncoding
let path = "/каталог/товар"
let encodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "/"
// encodedPath = "/%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3/%D1%82%D0%BE%D0%B2%D0%B0%D1%80"
var components = URLComponents()
components.scheme = "https"
components.host = "example.com"
components.path = encodedPath
components.queryItems = [URLQueryItem(name: "id", value: "1")]
if let url = components.url {
print(url.absoluteString)
// "https://example.com/%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3/%D1%82%D0%BE%D0%B2%D0%B0%D1%80?id=1"
}
Обратите внимание: .urlPathAllowed разрешает слеши, поэтому структура папок сохраняется. Но если у вас в названии товара есть слеш — он тоже будет закодирован, что правильно.
Ошибка №4: Двойное кодирование (double encoding)
Самая коварная ошибка. Вы уже закодировали URL, передали его в функцию, а та кодирует его снова. В результате %20 превращается в %2520, сервер получает мусор.
// ❌ Ошибка: двойное кодирование
let original = "Swift encode URL"
let