Генерация случайной буквенно-цифровой строки в Swift
Современный подход к генерации случайных строк в Swift
Начиная с Swift 4.2, появился новый набор API для работы со случайными числами, который делает код чище и безопаснее. Вот оптимальное решение для вашей задачи:
func randomAlphanumericString(length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map { _ in letters.randomElement()! })
}
// Пример использования
let randomString = randomAlphanumericString(length: 10)
print(randomString) // "aK3xR9mQ2p"
Разбор кода
Давайте разберем, что здесь происходит:
- letters — константа, содержащая все допустимые символы. Можно легко модифицировать: убрать/добавить символы.
- (0..<length) — создает диапазон от 0 до length (не включая length). Это просто счетчик для итераций.
- .map { _ in letters.randomElement()! } — для каждого элемента диапазона вызываем метод
randomElement()у строки letters. Он возвращает опциональный Character (ведь строка может быть пустой), но мы уверены, что letters не пуста, поэтому используем force unwrap (!). - String(...) — преобразует массив символов обратно в строку.
Почему это лучше вашего решения?
- Безопасность и современность:
randomElement()использует системный генератор случайных чисел (обычноarc4random_bufпод капотом на Apple платформах), что избавляет от устаревшегоarc4random_uniform. - Производительность:
randomElement()работает за O(1) и не требует вычисления индекса черезindex(startIndex, offsetBy:), который в вашем примере имеет сложность O(n) для каждой итерации. - Лаконичность: весь код умещается в одну строку (с учетом функции).
Криптографически стойкая версия
Если вам нужна строка для токенов, паролей или сессионных ключей, обычный randomElement() не подходит — он использует не криптостойкий генератор. Для таких целей используйте SecRandomCopyBytes из Security framework:
import Security
func cryptographicallySecureRandomString(length: Int) -> String? {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var data = [UInt8](repeating: 0, count: length)
let status = SecRandomCopyBytes(kSecRandomDefault, length, &data)
guard status == errSecSuccess else { return nil }
let characters = data.map { byte in
letters[letters.index(letters.startIndex, offsetBy: Int(byte) % letters.count)]
}
return String(characters)
}
// Пример
if let secureString = cryptographicallySecureRandomString(length: 20) {
print(secureString) // "F7j2Lp9XqR4vW8mN1kA"
}
Что выбрать?
- Обычная генерация (первый вариант) — для ID в базе данных, имен файлов, случайных никнеймов. Быстро и достаточно.
- Криптостойкая генерация (второй вариант) — для токенов аутентификации, ключей шифрования, сброса паролей. Медленнее, но защищено от предсказания.
Также рекомендую вынести набор символов в глобальную константу, чтобы не создавать строку при каждом вызове функции:
private let alphanumerics = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func randomString(length: Int) -> String {
return String((0..<length).map { _ in alphanumerics.randomElement()! })
}
Надеюсь, это поможет! Swift постоянно развивается, и использование новых API делает код не только красивее, но и безопаснее.
<<Альтернативный подход с использованием UUID и фильтрации
Если вам не нужна полная гибкость в выборе символов, можно использовать встроенный UUID. Он генерирует 36-символьную строку (с дефисами), но после удаления дефисов остается 32 символа, которые гарантированно содержат только hex-символы (0-9, a-f):
func uuidBasedRandomString(length: Int) -> String {
let uuid = UUID().uuidString.replacingOccurrences(of: "-", with: "")
// UUID содержит 32 hex-символа, обрезаем до нужной длины
return String(uuid.prefix(length))
}
print(uuidBasedRandomString(length: 10)) // "3F8A2C1E9B"
Плюсы и минусы
- + Простота: не нужно создавать набор символов и вручную выбирать элементы.
- + Криптостойкость:
UUIDна Apple платформах генерируется с использованием системного CSPRNG, поэтому подходит для токенов. - - Ограниченный алфавит: только hex-символы (0-9, A-F). Нельзя добавить буквы в нижнем регистре или специальные символы.
- - Фиксированная максимальная длина: UUID дает только 32 символа. Для более длинных строк придется комбинировать несколько UUID.
Когда использовать
Этот подход идеален для генерации коротких идентификаторов (до 32 символов), где не требуется широкий алфавит. Например, для ключей кэша или временных меток. Если нужно больше символов — просто вызовите функцию несколько раз и объедините результаты:
func longUUIDString(length: Int) -> String {
var result = ""
while result.count < length {
result += UUID().uuidString.replacingOccurrences(of: "-", with: "")
}
return String(result.prefix(length))
}
Этот метод чуть медленнее из-за создания нескольких UUID, но для большинства задач производительность остается приемлемой.
Ответов пока нет.
Войдите чтобы оставить ответ