Как получить имя класса объекта в виде строки в Swift?
Использование String(describing:) и Mirror
В Swift нет прямого аналога NSStringFromClass из Objective-C, но есть несколько простых способов получить имя класса в виде строки. Самый популярный и надёжный метод — использовать String(describing: type(of: self)).
class MyClass {
var name: String = "Test"
}
let obj = MyClass()
let className = String(describing: type(of: obj))
print(className) // Выведет: "MyClass"
Этот подход работает для любых типов: классов, структур, перечислений и даже протоколов. Давайте разберём его по шагам:
type(of: obj)— возвращает метатип объекта (например,MyClass.Type)String(describing:)— преобразует метатип в строку, используя его описание
Работа с опциональными и вложенными типами
Если объект может быть nil, лучше использовать безопасный подход:
let optionalObj: MyClass? = MyClass()
let className = optionalObj.map { String(describing: type(of: $0)) } ?? "nil"
print(className) // "MyClass"
Для вложенных типов (например, классов внутри других классов) результат будет включать имена всех родительских типов:
class Outer {
class Inner {}
}
let inner = Outer.Inner()
print(String(describing: type(of: inner))) // "Inner" (только имя самого внутреннего типа)
Альтернатива: Mirror
Иногда вам может понадобиться более детальная информация. Для этого можно использовать Mirror:
let mirror = Mirror(reflecting: obj)
if let displayStyle = mirror.displayStyle {
print("Тип: \(displayStyle)") // Тип: class
}
print("Имя: \(mirror.subjectType)") // Имя: MyClass
Однако Mirror даёт не только имя, но и структуру объекта, что может быть избыточно для простой задачи.
Важное замечание для Objective-C классов
Если вы работаете с классами, унаследованными от NSObject, можно использовать старый добрый NSStringFromClass:
import Foundation
class MyObjCClass: NSObject {
@objc dynamic var value: Int = 0
}
let objcObj = MyObjCClass()
print(NSStringFromClass(type(of: objcObj))) // "_TtC11MyAppModule12MyObjCClass" (искажённое имя)
Обратите внимание: для Swift-классов, не помеченных @objc, NSStringFromClass может вернуть искажённое (mangled) имя. Поэтому для чистых Swift-типов используйте String(describing: type(of:)).
Рекомендация
Для большинства случаев используйте String(describing: type(of: object)) — это самый простой и универсальный способ. Если вам нужно именно Objective-C-совместимое имя (например, для взаимодействия с UIKit), проверьте, является ли класс наследником NSObject.
Расширение для удобства
Чтобы не писать String(describing: type(of:)) каждый раз, можно создать удобное расширение для протокола Any:
extension NSObjectProtocol {
var className: String {
return String(describing: type(of: self))
}
}
// Использование:
class MyClass: NSObject {}
let obj = MyClass()
print(obj.className) // "MyClass"
Однако это расширение работает только для классов, наследующих NSObject. Для всех типов (включая структуры и перечисления) лучше использовать глобальную функцию:
func className(of object: Any) -> String {
return String(describing: type(of: object))
}
// Пример со структурой:
struct MyStruct {}
print(className(of: MyStruct())) // "MyStruct"
Для отладки: Mirror + полный путь
Если вы хотите получить полное квалифицированное имя (с модулем), используйте _typeName (но это приватный API, не для продакшена):
let fullName = _typeName(type(of: obj), qualified: true)
print(fullName) // "MyApp.MyClass"
Но лучше избегать этого в production-коде, так как API может измениться.
Итог
Самый безопасный и универсальный способ — String(describing: type(of: object)). Он работает для любых типов и не требует импорта Foundation.