Как определить модель текущего iPhone или устройства iOS в Swift?
Решение с использованием sysctl и utsname
Для определения точной модели устройства в Swift необходимо получить идентификатор модели из системного вызова sysctl или структуры utsname, а затем сопоставить его с человекочитаемым названием. Стандартный UIDevice.current.model действительно даёт только общую категорию.
Вот полное, актуальное и эффективное решение:
import UIKit
public extension UIDevice {
/// Точное название модели устройства (например, "iPhone 14 Pro Max")
var modelName: String {
let identifier = self.modelIdentifier
return Self.modelMapping[identifier] ?? identifier
}
/// Идентификатор модели (например, "iPhone15,3")
var modelIdentifier: String {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.compactMap { child -> String? in
guard let value = child.value as? Int8, value != 0 else { return nil }
return String(UnicodeScalar(UInt8(value)))
}.joined()
return identifier
}
// MARK: - Словарь соответствия идентификаторов и названий
private static let modelMapping: [String: String] = [
// iPhone
"iPhone14,2": "iPhone 13 Pro",
"iPhone14,3": "iPhone 13 Pro Max",
"iPhone14,4": "iPhone 13 mini",
"iPhone14,5": "iPhone 13",
"iPhone14,6": "iPhone SE (3rd generation)",
"iPhone14,7": "iPhone 14",
"iPhone14,8": "iPhone 14 Plus",
"iPhone15,2": "iPhone 14 Pro",
"iPhone15,3": "iPhone 14 Pro Max",
"iPhone15,4": "iPhone 15",
"iPhone15,5": "iPhone 15 Plus",
"iPhone16,1": "iPhone 15 Pro",
"iPhone16,2": "iPhone 15 Pro Max",
// iPad
"iPad13,1": "iPad Air (4th generation)",
"iPad13,2": "iPad Air (4th generation)",
"iPad13,16": "iPad Air (5th generation)",
"iPad13,17": "iPad Air (5th generation)",
"iPad14,1": "iPad mini (6th generation)",
"iPad14,2": "iPad mini (6th generation)",
"iPad13,4": "iPad Pro 11-inch (3rd generation)",
"iPad13,5": "iPad Pro 11-inch (3rd generation)",
"iPad13,6": "iPad Pro 11-inch (3rd generation)",
"iPad13,7": "iPad Pro 11-inch (3rd generation)",
"iPad13,8": "iPad Pro 12.9-inch (5th generation)",
"iPad13,9": "iPad Pro 12.9-inch (5th generation)",
"iPad13,10": "iPad Pro 12.9-inch (5th generation)",
"iPad13,11": "iPad Pro 12.9-inch (5th generation)",
"iPad14,3": "iPad Pro 11-inch (4th generation)",
"iPad14,4": "iPad Pro 11-inch (4th generation)",
"iPad14,5": "iPad Pro 12.9-inch (6th generation)",
"iPad14,6": "iPad Pro 12.9-inch (6th generation)",
// iPod touch
"iPod9,1": "iPod touch (7th generation)",
// Simulator
"i386": "Simulator",
"x86_64": "Simulator",
"arm64": "Simulator"
]
}
// Пример использования:
let device = UIDevice.current
print("Device model: \(device.modelName)")
// Вывод: "iPhone 14 Pro Max" (на реальном устройстве)
Как это работает
- Получение идентификатора: Функция
modelIdentifierиспользует системный вызовuname(), который заполняет структуруutsname. Полеmachineсодержит строку вида "iPhone15,3". Это самый надёжный способ, не требующий дополнительных разрешений. - Сопоставление с названием: Статический словарь
modelMappingсодержит актуальные пары "идентификатор → название". Если идентификатор отсутствует в словаре (например, для новой модели, ещё не вышедшей на момент обновления кода), возвращается сам идентификатор как запасной вариант. - Обработка симулятора: На симуляторе
uname()возвращает архитектуру процессора (x86_64 или arm64), поэтому словарь содержит соответствующие ключи.
Важные замечания
- Обновление словаря: Каждый раз, когда Apple выпускает новое устройство, словарь нужно дополнять. Рекомендую подписаться на DeviceKit или отслеживать обновления на The Apple Wiki.
- Альтернатива: Если не хотите поддерживать словарь вручную, используйте библиотеку
DeviceKit— она автоматически обновляется через Swift Package Manager. - Производительность: Вызов
uname()крайне быстрый, можно вызывать его без кэширования. Но для частого использования (например, в каждой ячейке таблицы) лучше сохранить результат в свойстве.
Это решение работает на iOS 12+ и не требует дополнительных разрешений. Оно используется в продакшн-приложениях с миллионами пользователей.
<<Альтернативный подход: использование sysctlbyname
Вместо uname() можно использовать функцию sysctlbyname, которая более гибкая и позволяет получать разные системные параметры. Для определения модели устройства подойдёт ключ "hw.product" (доступен с iOS 16) или классический "hw.machine".
import UIKit
extension UIDevice {
var modelName: String {
let identifier = hardwareIdentifier()
// Здесь используйте тот же словарь modelMapping, что и в первом ответе
return Self.modelMapping[identifier] ?? identifier
}
private func hardwareIdentifier() -> String {
var size = 0
sysctlbyname("hw.machine", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.machine", &machine, &size, nil, 0)
return String(cString: machine)
}
}
Преимущества и недостатки
- Плюсы: Более современный C-интерфейс, легче интегрируется с Swift. На iOS 16+ можно использовать
"hw.product", который сразу возвращает понятный идентификатор вроде "iPhone15,3". - Минусы: Требуется ручное управление памятью (хотя в Swift это не так критично). Словарь соответствия всё равно необходим.
Совет для iOS 16+
Начиная с iOS 16, Apple ввела новое свойство UIDevice.current.modelIdentifier (внимание: это не путать с нашим кастомным свойством). Оно возвращает тот же идентификатор, что и uname(). Если ваше приложение поддерживает только iOS 16+, можно упростить код:
if #available(iOS 16.0, *) {
let identifier = UIDevice.current.modelIdentifier
print(identifier) // "iPhone15,3"
} else {
// fallback на старый метод
}
Однако на момент написания ответа (2024 год) многие приложения всё ещё поддерживают iOS 15, поэтому универсальное решение с uname() остаётся предпочтительным.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Работа с файловой системой в C#: полное руководство для разработчиков