Assign list of variadic parameters to variable - ios

This is the case:
public enum NodeFeature: UInt16 {
case relay = 0x01
case proxy = 0x02
case friend = 0x04
case lpn = 0x08
}
public struct NodeFeatures {
public let rawValue: UInt16
public init(rawValue: UInt16) {
self.rawValue = rawValue
}
public init(features: NodeFeature...) {
var rawValue = UInt16(0)
for feature in features {
rawValue |= feature.rawValue
}
self.rawValue = rawValue
}
public func hasFeature(_ feature: NodeFeature) -> Bool {
return rawValue & feature.rawValue > 0
}
}
And this is a response from server:
"feature": {
"relay": true,
"proxy": false,
"friend": false,
"lowPower": false
}
Now I need to create an instance of NodeFeatures with only true values:
var features = [NodeFeature]() // how to declare and prepare the list of args?
if true {
features.append(.first)
} else if true {
features.append(.third)
}
let wrapper = NodeFeatures(features: features) //... to pass it as a variable to the initializer.
But the error is following:
Cannot convert value of type '[NodeFeature]' to expected argument type 'NodeFeatures'

You cannot pass an array to a function taking a variadic argument,
or dynamically "build" a variadic argument list, compare e.g.
Passing an array to a function with variable number of args in Swift.
But fortunately, the type has another initializer
public init(rawValue: UInt16)
which you can use in different ways.
Option 1: Use an integer bit mask to assemble the features
instead of an array:
var rawFeatures = UInt16(0)
if condition {
rawFeatures |= NodeFeature.relay.rawValue
} else if condition {
rawFeatures |= NodeFeature.proxy.rawValue
}
let wrapper = NodeFeatures(rawValue: rawFeatures)
Option 2: Keep your array, but compute the combined raw value
to create the NodeFeatures value:
var features = [NodeFeature]()
if condition {
features.append(.relay)
} else if condition {
features.append(.proxy)
}
let rawFeatures = features.reduce(0, { $0 | $1.rawValue })
let wrapper = NodeFeatures(rawValue: rawFeatures)
Option 3: Define another initializer taking an array argument
in an extension:
extension NodeFeatures {
public init(features: [NodeFeature]) {
let rawValue = features.reduce(0, { $0 | $1.rawValue })
self.init(rawValue: rawValue)
}
}
Now you can pass your array directly:
var features = [NodeFeature]()
if condition {
features.append(.relay)
} else if condition {
features.append(.proxy)
}
let wrapper = NodeFeatures(features: features)

Related

how to use `if case` statement as boolean in swift

I have this enum (with associated type)
enum CookieType {
case regular(type: Int)
case gem(type: GemType)
}
struct Cookie {
let type: CookieType
let otherStuff...
}
Now if i want to do pattern matching, i can do this with no problem:
if case .gem == cookie.type {
}
However, I want to use case .gem == cookie.type as a boolean. The following gives error
var cookies: [Cookie] {
return [cookieA, cookieB]
}
var gems: [Cookie] {
return cookies.filter { case $0.type == .gem } // this has error
}
which means case $0.type == .gem is not a boolean. How can I deal with this?
case .gem = cookie.type is not a boolean expression, and if statements not only accept boolean expressions. However, in the closure argument for filter, you must write an Bool expression or a block that returns Bool.
One way to do that is:
cookies.filter {
if case $0.type = .gem { return true }
else { return false }
}
Or, you can add convenient properties to CookieType that gives you Bool values, if you tend to do this a lot:
enum CookieType {
case regular(type: Int)
case gem(type: GemType)
var isRegular: Bool {
if case .regular = self { return true }
else { return false }
}
var isGem: Bool {
if case .gem = self { return true }
else { return false }
}
}
Then you can do:
cookies.filter(\.isGem)
Normally, if case and switch statements are abstractions based on the pattern matching operator ~=, but that's not how enums with associated types are implemented. As such, for the time being, you need to reverse-engineer how it might have been done, to allow this:
cookies.filter { CookieType.regular ~= $0.type }
I do not believe it is possible to avoid the explicit CookieType there, but it still reads better than all alternatives.
/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter case: Looks like `Enum.case`.
public func ~= <Enum, AssociatedValue>(
case: (AssociatedValue) -> Enum,
instance: Enum
) -> Bool {
Mirror.associatedValue(of: instance, ifCase: `case`) != nil
}
public extension Mirror {
/// Get an `enum` case's `associatedValue`.
static func associatedValue<AssociatedValue>(
of subject: Any,
_: AssociatedValue.Type = AssociatedValue.self
) -> AssociatedValue? {
guard let childValue = Self(reflecting: subject).children.first?.value
else { return nil }
if let associatedValue = childValue as? AssociatedValue {
return associatedValue
}
let labeledAssociatedValue = Self(reflecting: childValue).children.first
return labeledAssociatedValue?.value as? AssociatedValue
}
/// Get an `enum` case's `associatedValue`.
/// - Parameter case: Looks like `Enum.case`.
static func associatedValue<Enum, AssociatedValue>(
of instance: Enum,
ifCase case: (AssociatedValue) throws -> Enum
) rethrows -> AssociatedValue? {
try associatedValue(of: instance).filter {
.equate(try `case`($0), to: instance) {
Self(reflecting: $0).children.first?.label
}
}
}
}
public extension Optional {
/// Transform `.some` into `.none`, if a condition fails.
/// - Parameters:
/// - isSome: The condition that will result in `nil`, when evaluated to `false`.
func filter(_ isSome: (Wrapped) throws -> Bool) rethrows -> Self {
try flatMap { try isSome($0) ? $0 : nil }
}
}
public extension Equatable {
/// Equate two values using a closure.
static func equate<Wrapped, Equatable: Swift.Equatable>(
_ optional0: Wrapped?, to optional1: Wrapped?,
using transform: (Wrapped) throws -> Equatable
) rethrows -> Bool {
try optional0.map(transform) == optional1.map(transform)
}
}
Inspired by #Jessy and SwiftLee, here is my solution:
// -----------------------
// CaseReflectable
// -----------------------
// designed for enums only
// (use it on other types not recommended)
protocol CaseReflectable {}
// default behaviors.
extension CaseReflectable {
/// case name
var caseName: String {
let mirror = Mirror(reflecting: self)
// enum cases:
// - normal case: no children
// - case with associated values: one child (with label)
guard let label = mirror.children.first?.label else {
return "\(self)" // normal case
}
// case with associated values
return label
}
/// associated values
var associatedValues: Any? {
// if no children, a normal case, no associated values.
guard let firstChild = Mirror(reflecting: self).children.first else {
return nil
}
return firstChild.value
}
}
// --------------------------
// custom operator ~=
// --------------------------
/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
func ~= <Enum: CaseReflectable, AssociatedValue>(
// an enum case (with associated values)
enumCase: (AssociatedValue) -> Enum, // enum case as function
// an instance of Enum
instance: Enum
) -> Bool
{
// if no associated values, `instance` can't be of `enumCase`
guard let values = instance.associatedValues else { return false }
// if associated values not of the same type, return false
guard values is AssociatedValue else { return false }
// create an instance from `enumCase` (as function)
let case2 = enumCase(values as! AssociatedValue)
// if same case name, return true
return case2.caseName == instance.caseName
}
// ------------
// Enum
// ------------
// enum with associated values
// (conforms to `CaseReflectable`)
enum Enum: CaseReflectable {
case int(Int)
case int2(Int)
case person(name: String, age: Int)
case str(String)
}
// ------------
// main
// ------------
let a: Enum = .int(3)
Enum.int ~= a // true
Enum.int2 ~= a // false
let joe = Enum.person(name: "joe", age: 8)
Enum.person ~= joe // true
Enum.int ~= joe // false
// array of enum cases
let items: [Enum] = [
.int(1), .str("hi"), .int(2)
]
// filter enum cases
let filtered = items.filter { Enum.int ~= $0 }
print(filtered) // [Enum.int(1), Enum.int(2)]

How to display OptionSet values in human-readable form?

Swift has the OptionSet type, which basically adds set operations to C-Style bit flags. Apple is using them pretty extensively in their frameworks. Examples include the options parameter in animate(withDuration:delay:options:animations:completion:).
On the plus side, it lets you use clean code like:
options: [.allowAnimatedContent, .curveEaseIn]
However, there is a downside as well.
If I want to display the specified values of an OptionSet, there doesn't seem to be a clean way to do it:
let options: UIViewAnimationOptions = [.allowAnimatedContent, .curveEaseIn]
print("options = " + String(describing: options))
Displays the very unhelpful message:
options = UIViewAnimationOptions(rawValue: 65664)
The docs for some of these bit fields expresses the constant as a power-of-two value:
flag0 = Flags(rawValue: 1 << 0)
But the docs for my example OptionSet, UIViewAnimationOptions, doesn't tell you anything about the numeric value of these flags and figuring out bits from decimal numbers is not straightforward.
Question:
Is there some clean way to map an OptionSet to the selected values?
My desired output would be something like:
options = UIViewAnimationOptions([.allowAnimatedContent, .curveEaseIn])
But I can't think of a way to do this without adding messy code that would require me to maintain a table of display names for each flag.
(I'm interested in doing this for both system frameworks and custom OptionSets I create in my own code.)
Enums let you have both a name and a raw value for the enum, but those don't support the set functions you get with OptionSets.
Here is one approach I've taken, using a dictionary and iterating over the keys. Not great, but it works.
struct MyOptionSet: OptionSet, Hashable, CustomStringConvertible {
let rawValue: Int
static let zero = MyOptionSet(rawValue: 1 << 0)
static let one = MyOptionSet(rawValue: 1 << 1)
static let two = MyOptionSet(rawValue: 1 << 2)
static let three = MyOptionSet(rawValue: 1 << 3)
var hashValue: Int {
return self.rawValue
}
static var debugDescriptions: [MyOptionSet:String] = {
var descriptions = [MyOptionSet:String]()
descriptions[.zero] = "zero"
descriptions[.one] = "one"
descriptions[.two] = "two"
descriptions[.three] = "three"
return descriptions
}()
public var description: String {
var result = [String]()
for key in MyOptionSet.debugDescriptions.keys {
guard self.contains(key),
let description = MyOptionSet.debugDescriptions[key]
else { continue }
result.append(description)
}
return "MyOptionSet(rawValue: \(self.rawValue)) \(result)"
}
}
let myOptionSet = MyOptionSet([.zero, .one, .two])
// prints MyOptionSet(rawValue: 7) ["two", "one", "zero"]
This article in NSHipster gives an alternative to OptionSet that offers all the features of an OptionSet, plus easy logging:
https://nshipster.com/optionset/
If you simply add a requirement that the Option type be CustomStringConvertible, you can log Sets of this type very cleanly. Below is the code from the NSHipster site - the only change being the addition of CustomStringConvertible conformance to the Option class
protocol Option: RawRepresentable, Hashable, CaseIterable, CustomStringConvertible {}
enum Topping: String, Option {
case pepperoni, onions, bacon,
extraCheese, greenPeppers, pineapple
//I added this computed property to make the class conform to CustomStringConvertible
var description: String {
return ".\(self.rawValue)"
}
}
extension Set where Element == Topping {
static var meatLovers: Set<Topping> {
return [.pepperoni, .bacon]
}
static var hawaiian: Set<Topping> {
return [.pineapple, .bacon]
}
static var all: Set<Topping> {
return Set(Element.allCases)
}
}
typealias Toppings = Set<Topping>
extension Set where Element: Option {
var rawValue: Int {
var rawValue = 0
for (index, element) in Element.allCases.enumerated() {
if self.contains(element) {
rawValue |= (1 << index)
}
}
return rawValue
}
}
Then using it:
let toppings: Set<Topping> = [.onions, .bacon]
print("toppings = \(toppings), rawValue = \(toppings.rawValue)")
That outputs
toppings = [.onions, .bacon], rawValue = 6
Just like you want it to.
That works because a Set displays its members as a comma-delimited list inside square brackets, and uses the description property of each set member to display that member. The description property simply displays each item (the enum's name as a String) with a . prefix
And since the rawValue of a Set<Option> is the same as an OptionSet with the same list of values, you can convert between them readily.
I wish Swift would just make this a native language feature for OptionSets.
StrOptionSet Protocol:
Add a labels set property to test each label value on Self.
StrOptionSet Extension:
Filter out which is not intersected.
Return the label text as array.
Joined with "," as CustomStringConvertible::description
Here is the snippet:
protocol StrOptionSet : OptionSet, CustomStringConvertible {
typealias Label = (Self, String)
static var labels: [Label] { get }
}
extension StrOptionSet {
var strs: [String] { return Self.labels
.filter{ (label: Label) in self.intersection(label.0).isEmpty == false }
.map{ (label: Label) in label.1 }
}
public var description: String { return strs.joined(separator: ",") }
}
Add the label set for target option set VTDecodeInfoFlags.
extension VTDecodeInfoFlags : StrOptionSet {
static var labels: [Label] { return [
(.asynchronous, "asynchronous"),
(.frameDropped, "frameDropped"),
(.imageBufferModifiable, "imageBufferModifiable")
]}
}
Use it
let flags: VTDecodeInfoFlags = [.asynchronous, .frameDropped]
print("flags:", flags) // output: flags: .asynchronous,frameDropped
This is how I did it.
public struct Toppings: OptionSet {
public let rawValue: Int
public static let cheese = Toppings(rawValue: 1 << 0)
public static let onion = Toppings(rawValue: 1 << 1)
public static let lettuce = Toppings(rawValue: 1 << 2)
public static let pickles = Toppings(rawValue: 1 << 3)
public static let tomatoes = Toppings(rawValue: 1 << 4)
public init(rawValue: Int) {
self.rawValue = rawValue
}
}
extension Toppings: CustomStringConvertible {
static public var debugDescriptions: [(Self, String)] = [
(.cheese, "cheese"),
(.onion, "onion"),
(.lettuce, "lettuce"),
(.pickles, "pickles"),
(.tomatoes, "tomatoes")
]
public var description: String {
let result: [String] = Self.debugDescriptions.filter { contains($0.0) }.map { $0.1 }
let printable = result.joined(separator: ", ")
return "\(printable)"
}
}
struct MyOptionSet: OptionSet {
let rawValue: UInt
static let healthcare = MyOptionSet(rawValue: 1 << 0)
static let worldPeace = MyOptionSet(rawValue: 1 << 1)
static let fixClimate = MyOptionSet(rawValue: 1 << 2)
static let exploreSpace = MyOptionSet(rawValue: 1 << 3)
}
extension MyOptionSet: CustomStringConvertible {
static var debugDescriptions: [(Self, String)] = [
(.healthcare, "healthcare"),
(.worldPeace, "world peace"),
(.fixClimate, "fix the climate"),
(.exploreSpace, "explore space")
]
var description: String {
let result: [String] = Self.debugDescriptions.filter { contains($0.0) }.map { $0.1 }
return "MyOptionSet(rawValue: \(self.rawValue)) \(result)"
}
}
Usage
var myOptionSet: MyOptionSet = []
myOptionSet.insert(.healthcare)
print("here is my options: \(myOptionSet)")

Enum of structs in Swift 3.0

I am trying to create an enum of a struct that I would like to initialize:
struct CustomStruct {
var variable1: String
var variable2: AnyClass
var variable3: Int
init (variable1: String, variable2: AnyClass, variable3: Int) {
self.variable1 = variable1
self.variable2 = variable2
self.variable3 = variable3
}
}
enum AllStructs: CustomStruct {
case getData
case addNewData
func getAPI() -> CustomStruct {
switch self {
case getData:
return CustomStruct(variable1:"data1", variable2: SomeObject.class, variable3: POST)
case addNewData:
// Same to same
default:
return nil
}
}
}
I get the following errors:
Type AllStructs does not conform to protocol 'RawRepresentable'
I am assuming that enums cannot be used this way. We must use primitives.
It should be:
struct CustomStruct {
var apiUrl: String
var responseType: AnyObject
var httpType: Int
init (variable1: String, variable2: AnyObject, variable3: Int) {
self.apiUrl = variable1
self.responseType = variable2
self.httpType = variable3
}
}
enum MyEnum {
case getData
case addNewData
func getAPI() -> CustomStruct {
switch self {
case .getData:
return CustomStruct(variable1: "URL_TO_GET_DATA", variable2: 11 as AnyObject, variable3: 101)
case .addNewData:
return CustomStruct(variable1: "URL_TO_ADD_NEW_DATA", variable2: 12 as AnyObject, variable3: 102)
}
}
}
Usage:
let data = MyEnum.getData
let myObject = data.getAPI()
// this should logs: "URL_TO_GET_DATA 11 101"
print(myObject.apiUrl, myObject.responseType, myObject.httpType)
Note that upon Naming Conventions, struct should named as CustomStruct and enum named as MyEnum.
In fact, I'm not pretty sure of the need of letting CustomStruct to be the parent of MyEnum to achieve what are you trying to; As mentioned above in the snippets, you can return an instance of the struct based on what is the value of the referred enum.
I'm not commenting on the choice to use an enum here, but just explaining why you got that error and how to declare an enum that has a custom object as parent.
The error shows you the problem, CustomStruct must implement RawRepresentable to be used as base class of that enum.
Here is a simplified example that shows you what you need to do:
struct CustomStruct : ExpressibleByIntegerLiteral, Equatable {
var rawValue: Int = 0
init(integerLiteral value: Int){
self.rawValue = value
}
static func == (lhs: CustomStruct, rhs: CustomStruct) -> Bool {
return
lhs.rawValue == rhs.rawValue
}
}
enum AllStructs: CustomStruct {
case ONE = 1
case TWO = 2
}
A few important things that we can see in this snippet:
The cases like ONE and TWO must be representable with a Swift literal, check this Swift 2 post for a list of available literals (int,string,array,dictionary,etc...). But please note that in Swift 3, the LiteralConvertible protocols are now called ExpressibleByXLiteral after the Big Swift Rename.
The requirement to implement RawRepresentable is covered implementing one of the Expressible protocols (init?(rawValue:) will leverage the initializer we wrote to support literals).
Enums must also be Equatable , so you'll have to implement the equality operator for your CustomStruct base type.
Did you try conforming to RawRepresentable like the error is asking?
Using JSON representation should work for variable1 and variable3. Some extra work may be required for variable2.
struct CustomStruct: RawRepresentable {
var variable1: String
var variable2: AnyClass
var variable3: Int
init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8) else {
return nil
}
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return nil
}
self.variable1 = (json["variable1"] as? String) ?? ""
self.variable2 = (json["variable2"] as? AnyClass) ?? AnyClass()
self.variable3 = (json["variable3"] as? Int) ?? 0
}
var rawValue: String {
let json = ["variable1": self.variable1,
"variable2": self.variable2,
"variable3": self.variable3
]
guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else {
return ""
}
return String(data: data, encoding: .utf8) ?? ""
}
}
According to the documentation:
If a value (known as a “raw” value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.
So yes, you cannot set a struct type to be enum's raw value.
In your case I would suggest using string as the enum raw value and some dictionary mapping these strings to CUSTOM_STRUCT type.
A bit late on the party but maybe useful for someone else. I would consider to simply use computed variables instead of a struct.
enum MyEnum {
case getData
case addNewData
var variable1: String {
switch self {
case .getData: return "data1"
case .addNewData: return "data2"
}
}
var variable2: Int {
switch self {
case .getData: return 1
case .addNewData: return 2
}
}
// ....
}
Usage:
let data = MyEnum.getData
print (data.variable1) // "data1"

Swift 3 - Sequence ambiguous without more context

I tried to create a custom iterator which returns wrapper abcContainer over raw data class abc
// raw data class
class abc {
var name : String = "";
init( _ value : String) {
name = value;
}
}
// with container, only "name" is to be visible
class abcContainer {
private var _abc : abc;
init( _ obj : abc) {
_abc = obj;
}
// + extra methods here
func getName() -> String {
return _abc.name
}
}
The point would be that the dictionary would return instances of abcContainer instead of just the plain raw abc class.
I wanted to use the sequence protocol to make the conversion automatic, but I was not able to transform the [String:abc] into [String:abcContainer] automatically like this:
// the iterator is implemented just iterating the inner basic dict
// but wrapping the result value as abcContainer
class abcIterator : Sequence, IteratorProtocol {
private var __source : [String:abc]?;
var index = 0
var myIterator : DictionaryIterator<String, abc>;
init(_ ctxArray: [String:abc]) {
self.__source = ctxArray
index = 0;
myIterator = (__source?.makeIterator())!
}
func next() -> abcContainer? {
let nextItem = myIterator.next();
if(nextItem != nil) {
return abcContainer((nextItem?.value)!);
}
return nil;
}
}
// this was supposed to be the wrapper over the collection
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
func makeIterator() -> abcIterator {
return abcIterator(self.__source!);
}
}
I'm probably missing something very basic here. When I try to use the collection like this:
var dict : [String:abc] = [String:abc]();
dict["abba"] = abc("John Smith");
for (key,value) in abcCollection(dict) {
print(key, value.getName());
}
I get error: Expression type "abcCollection" is ambiguous without more context
Does anyone have idea how to make it work? What is missing? I have a feeling that this answer has the information I need...
Swift 2 to 3 Migration for Swift Sequence Protocol
The problem in your original code is that abcCollection(dict)
returned a sequence of abcContainer objects, and those cannot
be assigned to a (key, value) tuple.
You can achieve your goal with
class abcCollection : Sequence {
private var __source : [String:abc]
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
let mapped = self.__source.lazy.map {
($0.key as AnyObject, abcContainer($0.value))
}
return AnyIterator(mapped.makeIterator())
}
}
Making __source non-optional makes all the (optional) unwrappings
redundant, and lazy.map { ... } returns a lazily evaluated
sequence of key/value pairs which is then type-erased.
Ok, perhaps the answer was abcIterator was not necessary, you could have defined the iterator directly just like done in the linked answer like this:
class abcCollection : Sequence {
private var __source : [String:abc]?;
init(_ list: [String:abc]) {
self.__source = list
}
public func makeIterator() -> AnyIterator<(AnyObject,abcContainer)> {
var it = self.__source?.makeIterator();
return AnyIterator {
let n = it?.next();
if n == nil { return nil }
return (n?.key as AnyObject, abcContainer((n?.value)!))
}
}
}
After that, the custom collection returned wrapped objects correctly.

How to get a swift enum's associated value regardless of the enum case

I have an object FormField which has two properties: a String name, and a value which can accept any type--hence I've made it Any!. However, I've been told in a separate question to use an enum with associated values instead of Any!.
enum Value {
case Text(String!)
case CoreDataObject(NSManagedObject!)
}
class FormField {
var name: String
var value: Value?
// initializers...
}
This approach makes it awfully verbose to check for nullity however. If I wanted to display an alert view for all the missing fields in the form, I'll have to repeat a nil check for every case in a switch statement:
for field in self.fields {
if let value = field.value {
switch value {
case .Text(let text):
if text == nil {
missingFields.append(field.name)
}
case .CoreDataObject(let object):
if object == nil {
missingFields.append(field.name)
}
}
}
}
Is there a shorter way of accessing the enum's associated value, regardless of the type? If I make FormField.value an Any! the above code would be as easy as:
for field in self.fields {
if field.value == nil {
missingFields.append(field.name)
}
}
Define a method isMissing() inside the enum - write it once and only once. Then you get nearly exactly what you prefer:
for field in self.fields {
if field.value.isMissing() {
missingFields.append(field.name)
}
}
It would look something like this (from the Swift Interpreter):
1> class Foo {}
>
2> enum Value {
3. case One(Foo!)
4. case Two(Foo!)
5.
6. func isMissing () -> Bool {
7. switch self {
8. case let .One(foo): return foo == nil
9. case let .Two(foo): return foo == nil
10. }
11. }
12. }
13> let aVal = Value.One(nil)
aVal: Value = One {
One = nil
}
14> aVal.isMissing()
$R0: Bool = true
With Swift 2 it's possible to get the associated value using reflection.
To make that easier just add the code below to your project and extend your enum with the EVAssociated protocol.
public protocol EVAssociated {
}
public extension EVAssociated {
public var associated: (label:String, value: Any?) {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return (associated.label!, associated.value)
}
print("WARNING: Enum option of \(self) does not have an associated value")
return ("\(self)", nil)
}
}
}
Then you can access the .asociated value with code like this:
class EVReflectionTests: XCTestCase {
func testEnumAssociatedValues() {
let parameters:[EVAssociated] = [usersParameters.number(19),
usersParameters.authors_only(false)]
let y = WordPressRequestConvertible.MeLikes("XX", Dictionary(associated: parameters))
// Now just extract the label and associated values from this enum
let label = y.associated.label
let (token, param) = y.associated.value as! (String, [String:Any]?)
XCTAssertEqual("MeLikes", label, "The label of the enum should be MeLikes")
XCTAssertEqual("XX", token, "The token associated value of the enum should be XX")
XCTAssertEqual(19, param?["number"] as? Int, "The number param associated value of the enum should be 19")
XCTAssertEqual(false, param?["authors_only"] as? Bool, "The authors_only param associated value of the enum should be false")
print("\(label) = {token = \(token), params = \(param)")
}
}
// See http://github.com/evermeer/EVWordPressAPI for a full functional usage of associated values
enum WordPressRequestConvertible: EVAssociated {
case Users(String, Dictionary<String, Any>?)
case Suggest(String, Dictionary<String, Any>?)
case Me(String, Dictionary<String, Any>?)
case MeLikes(String, Dictionary<String, Any>?)
case Shortcodes(String, Dictionary<String, Any>?)
}
public enum usersParameters: EVAssociated {
case context(String)
case http_envelope(Bool)
case pretty(Bool)
case meta(String)
case fields(String)
case callback(String)
case number(Int)
case offset(Int)
case order(String)
case order_by(String)
case authors_only(Bool)
case type(String)
}
The code above is now available as a cocoapod susbspec at
https://github.com/evermeer/Stuff#enum
It also has an other nice enum extension for enumerating all enum values.
If the associated values were of the same type for all enum cases the following approach could help.
enum Value {
case text(NSString!), two(NSString!), three(NSString!) // This could be any other type including AnyClass
}
// Emulating "fields" datastruct for demo purposes (as if we had struct with properties).
typealias Field = (en: Value, fieldName: String)
let fields: [Field] = [(.text(nil),"f1"), (.two(nil), "f2"), (.three("Hey"), "f3")] // this is analog of "fields"
let arrayOfFieldNamesWithEmptyEnums: [String] = fields.compactMap({
switch $0.en {
case let .text(foo), let .two(foo), let .three(foo): if foo == nil { return $0.fieldName } else { return nil }}
})
print("arrayOfFieldNamesWithEmptyEnums \(arrayOfFieldNamesWithEmptyEnums)")
Many other things can be obtained similarly.
let arrayOfEnumsWithoutValues: [Value] = fields.compactMap({
switch $0.en {
case let .text(foo), let .two(foo), let .three(foo): if foo == nil { return $0.en } else { return nil }}
})
print("arrayOfEnumsWithoutValues \(arrayOfEnumsWithoutValues)")
// just to check ourselves
if let index = arrayOfEnumsWithoutValues.index(where: { if case .two = $0 { return true }; return false }) {
print(".two found at index \(index)")
}

Resources