Есть ли способ использовать вкладки для равномерного разметки строк описания в Swift?

Переопределение переменной description пользовательского класса:

override var description : String {
        let mirrored_object = Mirror(reflecting: self)
        let str:NSMutableString = NSMutableString()
        for (index, attr) in mirrored_object.children.enumerated() {
            if let property_name = attr.label as String! {
                //str.append(" Attr \(index): \(property_name) = \(attr.value)\n")
                str.append("\t\(property_name) = \t\t\(attr.value)\n")
            }
        }
        return str as String
    }

Производит вывод, который выглядит как:

userID =        Optional(0)
username =          Optional("testuser")
email =         Optional("test@gmail.com")

Есть ли способ установить вкладки в выводе так, чтобы значения атрибутов выстраивались так же, как это?

userID =        Optional(0)
username =      Optional("testuser")
email =         Optional("test@gmail.com")

Кроме того, есть ли способ избавиться или сократить часть "Необязательно" и просто показать значение?

2 ответа

Решение

Я не буду использовать вкладки, но использую padding(...):

var description : String {
    let mirrored_object = Mirror(reflecting: self)
    let childrenWithLabel = mirrored_object.children.filter { $0.label != nil }
    let maxLen = childrenWithLabel.map { Int($0.label!.characters.count) }.max() ?? 0
    let lines = childrenWithLabel.map { $0.label!.padding(toLength: maxLen, withPad: " ", startingAt: 0) + " = \($0.value)" }
    return lines.joined(separator: "\n")
}

Для структуры, как

struct Foo: CustomStringConvertible
{
    let userID = 42
    let username = "Foo"
    let verylongpropertyname: String? = "Bar"
}

это производит

userID               = 42
username             = Foo
verylongpropertyname = Optional("Bar")

Что касается "Необязательной" части, это не так просто, как подсказывает totiG, потому что значение, которое вы получаете от зеркала, имеет тип Any, Смотрите этот вопрос.

Обновить

Я упустил из виду, что вы хотели иметь немного другой формат.

var description : String {
    let mirrored_object = Mirror(reflecting: self)
    let childrenWithLabel = mirrored_object.children.filter { $0.label != nil }
    let separator = " = "
    let firstColumnWidth = (childrenWithLabel.map { Int($0.label!.characters.count) }.max() ?? 0) + separator.characters.count
    let lines = childrenWithLabel.map {
        ($0.label! + separator).padding(toLength: firstColumnWidth, withPad: " ", startingAt: 0) + "\($0.value)"
    }
}

производит

userID =               42
username =             Foo
verylongpropertyname = Optional("Bar")

Обновление 2

Чтобы избавиться от "необязательной" вещи, пожалуйста, смотрите мой ответ здесь.

Если вы используете (из указанного ответа) unwrap() или же unwrapUsingProtocol() в description как это:

var description : String {
    let mirrored_object = Mirror(reflecting: self)
    let childrenWithLabel = mirrored_object.children.filter { $0.label != nil }
    let separator = " = "
    let firstColumnWidth = (childrenWithLabel.map { Int($0.label!.characters.count) }.max() ?? 0) + separator.characters.count
    let lines = childrenWithLabel.map {
        ($0.label! + separator).padding(toLength: firstColumnWidth, withPad: " ", startingAt: 0) + "\(unwrap($0.value))"
    }
    return lines.joined(separator: "\n")
}

это будет производить

userID =               42
username =             Foo
verylongpropertyname = Bar

Попробуйте использовать этот метод. Сначала он вычисляет максимальную длину свойства, а затем использует ее для дополнения имен свойств:

let maxPropertyLength: Int = mirrored_object.children.map { Int($0.label?.characters.count ?? 0) }.max() ?? 0
for attr in mirrored_object.children {
    if let propertyName = attr.label {
        str.append("\(propertyName.padding(toLength: maxPropertyLength + 2, withPad: " ", startingAt: 0)) = \(attr.value)\n")
    }
}
return str as String
Другие вопросы по тегам