Swift: 如何访问枚举的可变元素?

Swift: How to access variable element of an enum?

几个小时以来,我一直在努力获取枚举的可变元素。

Swifticons”- pod 为我提供了以下枚举:

public enum WeatherType: Int {
    static var count: Int {
        return weatherIcons.count
    }

    public var text: String? {
        return weatherIcons[rawValue]
    }

    case alien, barometer, celsius, owm300, owm301, owm302, and200moreOfTheseNames
}

private let weatherIcons = ["\u{f075}", "\u{f079}", and202moreOfTheseFontCharacters]

从外部 API (openWeatherMap.org) 我刚得到一个天气代码(比如说“300”)- 我想访问图标 "owm300".

但是我如何在不知道 rawValue(比如 - 198)的情况下访问枚举的这个元素?

Swift 目前没有枚举案例的可枚举序列。您的一个选择是复制 list of icon names,然后搜索您的图标名称,并将该索引用作枚举的原始值:

let weatherIcons = [...]
let iconName = "owm300"
let possibleIconIndex = weatherIcons.index {
    [=10=].caseInsensitiveCompare(iconName) == .orderedSame
}
if let iconIndex = possibleIconIndex {
    let weatherIcon = WeatherIcon(rawValue: iconIndex)!
    // ...
} else {
    // graceful fallback for when the weather icon is missing
}

当然,您需要自己找出从服务获取的数据与枚举名称之间的映射,但这可以像 "owm\(weatherCode)".

一样简单

当 Swift 4.2 登陆时,您将能够使您的枚举符合名为 CaseIterable 的新协议。符合它的枚举得到一个 allCases 静态变量的合成实现。然后,您将能够使用该枚举自动构建 string-to-enum 字典:

let nameToEnum = WeatherIcon.allCases.map { (String([=11=]), [=11=]) }
let mapping = Dictionary(uniqueKeysWithValues: nameToEnum)

然而,这将要求 WeatherIcon 声明 符合 CaseEnumerable,因为添加 extension 没有效果。

我能想到的最简单的方法之一是创建某种映射字典,您可以在其中跟踪天气响应代码和它映射到的 WeatherType,

let weatherCodeMapping: [Int: WeatherType] = [300: .owm300,
                                              301: .owm301,
                                              302: .owm302]

有了这个,你不需要知道任何具体的 rawValue,你可以简单地获取代码,

 let weatherType = weatherCodeMapping[weatherCode]

然后根据 weatherType 为您的图像创建一些其他映射。

let weatherIcon = weatherIconMapping[weatherType]

或者直接创建一个从天气代码到图标的映射。

计划如下:

  1. 我们需要枚举所有枚举案例。我们将通过迭代原始值来做到这一点(幸运的是,WeatherType 得到了 Int 的支持)。
  2. 我们将存储映射 StringWeatherType 的惰性初始化字典。
  3. 最后,我们声明一个静态函数,该函数 returns 是可选的 WeatherType? 因为我们可能会遇到未知值。

代码如下:

extension WeatherType {
  // just some little convenience
  private typealias W = WeatherType

  // 1. define the sequence of all cases
  private static func allCases() -> AnySequence<W> {
    return AnySequence { () -> AnyIterator<W> in
      var raw = 0
      return AnyIterator {
        // Iterates while raw value can be converted to an enum case
        if let next = W(rawValue: raw) {
          raw += 1
          return next
        }
        return nil
      }
    }
  }

  // 2. Static properties are lazy so we'll use them to store the dictionary with String to WeatherType mapping
  private static let typeMap = W.allCases().reduce([String: W]()) { acc, next in
    var acc = acc
    acc[String(describing: next)] = next
    return acc
  }

  // 3. Declare the mapping function
  static func from(string: String) -> WeatherType? {
    return W.typeMap[string]
  }
}

这里有一个小测试:

let str = "301"
let type = WeatherType.from(string: "owm\(str)")
print(type == .owm301)