Универсальный необязательный enum func - PullRequest
0 голосов
/ 29 мая 2018

В моем проекте у меня есть пара перечислений, которые я использую везде, эти перечисления создаются на основе внешнего json, поэтому ввод всегда необязателен.Если перечисление не может быть создано из входных данных, я определю значение по умолчанию.

Пример того, как я делаю вещи сейчас:

enum TextAlign:String {
    case left, center, right
}

let rawData = [String:Any]()

func getTextAlign() -> TextAlign {
    if let rawTextAlignString = rawData["textAlign"] as? String, let align = TextAlign(rawValue: rawTextAlignString) {
        return align
    }

    return TextAlign.left
}
let textAlign = self.getTextAlign()

Это работает, очевидно, но я быхотел бы сделать мой конструктор немного более быстрым, универсальным и применимым к большему количеству этих перечислений.Моя цель состоит в том, чтобы создать эти перечисления следующим образом:

let textAlign = TextAlign(rawOptionalValue: rawData["textAlign"] as? String) ?? TextAlign.left

Итак, я в основном хочу отказавшийся инициализатор, который я могу просто написать для перечисления TextAlign, но должен быть способ объявить это более«универсальный» способ, чтобы я мог использовать инициализатор во всех моих экземплярах enum: String.Я немного борюсь с синтаксисом и опциями обобщений в swift.

Любая идея?

Обновление # 1

Я вижу много ответов, которые не всенеправильно, но, вероятно, я не достаточно ясно понял, что я ищу.

У меня есть несколько перечислений, подобных этому:

enum TextAlign:String {
    case left, center, right
}

enum CornerRadius:String {
    case none, medium, large
}

enum Spacing:String {
    case tight, medium, loose
}

Я хотел бы определить только одну функцию, которая можетинициализировать все эти перечисления.(не потому, что я ленивый, а потому, что я хочу понять, как использовать Generics для этого)

Вероятно, мне нужен статический функционал в расширении, которое применяется ко всем этим перечислениям String / RawRepresentable.Я не хочу писать все эти неудачные инициализаторы для каждого перечисления.(Я полагаю, что это возможно, но я не могу понять синтаксис)

Обновление # 2

Немного поиграв с ответом Джоакима, я нашел следующее решение:

extension RawRepresentable {
    static func create<T:Any>(_ value: Any?, defaultValue: Self) -> Self where Self.RawValue == T {
        guard let rawValue = value as? T, let instance = Self.init(rawValue: rawValue) else {
            return defaultValue
        }

        return instance
    }
}

Это позволяет мне создавать экземпляры перечислений типа String и Int (и более) с помощью этой функции.Вот так:

enum TextAlign:String {
    case left, center, right
}

enum CornerRadius:Int {
    case none, medium, large
}

let json:[String:Any] = [
    "textAlign":"left",
    "cornerRadius":0
]

let cornerRadius = CornerRadius.create(json["cornerRadius"], defaultValue: .medium)
let align = TextAlign.create(json["textAlign"], defaultValue: .center)

Мне нравится, что я могу просто указать Any? в качестве аргумента и что он сам выполняет кастинг, выполняя let rawValue = value as? T.

Update #3 (решение)

Хорошо, сложность всего этого все еще немного беспокоила меня, поэтому я попробовал маршрут init, который, по моему мнению, выглядит чище.Теперь все выглядит так:

extension RawRepresentable {
    init(from value: Any?, or defaultValue: Self) {
        self = Self.init(from: value) ?? defaultValue
    }

    init?(from value: Any?) {
        guard let rawValue = value as? Self.RawValue, let instance = Self.init(rawValue: rawValue) else {
            return nil
        }

        self = instance
    }
}

Я создал неудачный и не сбойный init со значением по умолчанию для удобства.

Теперь я могу просто создать экземпляр любого перечисления, подобного этому:

let cornerRadius = CornerRadius(json["cornerRadius"], or: .medium)

// or an optional one
let align = TextAlign(json["textAlign"])

Теперь я закончил с обновлениями ...

Ответы [ 5 ]

0 голосов
/ 29 мая 2018

Я не думаю, что вам нужна новая функция для этого.Просто используйте Optional.map(_:):

let alignment = rawData["textAlign"].map(TextAlign.init(rawValue:)) ?? .left
0 голосов
/ 29 мая 2018

Вот функция, которую вы можете использовать для создания элементов перечисления из строки

func createEnumItem<T: RawRepresentable>(_ value: String) -> T? where T.RawValue == String {
    return  T.init(rawValue: value)
}

, а затем использовать ее как

let textAlign: TextAlign = createEnumItem("right")!
let radius: CornerRadius = createEnumItem("medium")!

Обратите внимание, что вы всегда включаете тип перечисления вобъявление переменной.

Конечно, поскольку возвращаемое значение является необязательным, вам нужно обработать его лучше, чем мой пример здесь.

Обновление

Если вы всегда знаете, что у вас по умолчанию, это модифицированная версия

func createEnumItem<T: RawRepresentable>(_ value: String, withDefault defaultItem: T) -> T where T.RawValue == String {
    guard let item = T.init(rawValue: value) else {
        return defaultItem
    }
    return item
}
0 голосов
/ 29 мая 2018

Вы можете определить статическую функцию подобным образом и использовать ее везде, где вам нужно в вашем коде

enum TextAlign: String {
    case left, center, right

    static func getTextAlign(rawData: [String:Any]) -> TextAlign {
        if let rawTextAlignString = rawData["textAlign"] as? String, let align = TextAlign(rawValue: rawTextAlignString) {
            return align
        }

        return TextAlign.left
    }
}

// Test
var myRawData = [String:Any]()

let textAlign1 = TextAlign.getTextAlign(rawData: myRawData) // left

myRawData["textAlign"] = "center"

let textAlign2 = TextAlign.getTextAlign(rawData: myRawData) // center
0 голосов
/ 29 мая 2018

Вы можете объявить свои перечисления следующим образом

enum TextAlign: String {
    case left, center, right

    init(rawOptionalValue: String?) {
        self = TextAlign(rawValue: rawOptionalValue ?? TextAlign.left.rawValue) ?? .left
    }
}

И затем создать его следующим образом:

let textAlign = TextAlign(rawOptionalValue: rawData["textAlign"] as? String)

Обновление

Вот один пример со значением по умолчанию какнеобязательный параметр:

enum TextAlign: String {
    case left, center, right

    init(rawOptionalValue: String?, defaultValue: TextAlign = TextAlign.left) {
        self = TextAlign(rawValue: rawOptionalValue ?? defaultValue.rawValue) ?? defaultValue
    }
}

let textAlign1 = TextAlign(rawOptionalValue: "left") // .left
let textAlign2 = TextAlign(rawOptionalValue: "right") // .right
let textAlign3 = TextAlign(rawOptionalValue: "center") // .center
let textAlign4 = TextAlign(rawOptionalValue: "notAnAlignment") // .left
let textAlign5 = TextAlign(rawOptionalValue: nil, defaultValue: .center) // .center

Обновление 2

Хорошо, теперь я понимаю.Итак, основываясь на вашем последнем обновлении, вы также можете сделать это, я думаю:

extension RawRepresentable {
    init(rawOptionalValue: Any?, defaultValue: Self) {
        guard let value = rawOptionalValue as? Self.RawValue else {
            self = defaultValue
            return
        }
        self = Self.init(rawValue: value) ?? defaultValue
    }
}

Единственное, что здесь можно сравнить с моей предыдущей попыткой, это то, что вы не можете предоставить значение по умолчанию defaultValue.Таким образом, вы бы использовали это так:

let textAlign1 = TextAlign(rawOptionalValue: "left", defaultValue: .left) // .left
let textAlign2 = TextAlign(rawOptionalValue: "right", defaultValue: .left) // .right
let textAlign3 = TextAlign(rawOptionalValue: "center", defaultValue: .left) // .center
let textAlign4 = TextAlign(rawOptionalValue: "notAnAlignment", defaultValue: .left) // .left
let textAlign5 = TextAlign(rawOptionalValue: nil, defaultValue: .center) // .center
0 голосов
/ 29 мая 2018

Вы можете попробовать это:

 let textAlign = TextAlign(rawValue: rawData["textAlign"] as? String ?? TextAlign.left.rawValue)
...