How to loop over struct properties in Swift? - ios

Is it possible to iterate over properties of a struct in Swift?
I need to register cells-reuse identifiers in a view controller that makes use of many different cell types (cells are organized in different nib files). So my idea was to put all reuse identifiers and the corresponding nib-files as static tuple-properties (reuseID, nibName) in a struct. But how can I iterate over all of them to register the cells with the tableView?
I already tried something (see my answer below). But is there a more easy way to do this, e.g. without putting every property inside an array?

Although old question, with Swift evolving this question has new answer. I think that you approach is way better for the described situation, however original question was how to iterate over struct properties, so here is my answer(works both for classes and structs)
You can use Mirror Structure Reference. The point is that after calling reflect to some object you get it's "mirror" which is pretty sparingly however still useful reflection.
So we could easily declare following protocol, where key is the name of the property and value is the actual value:
protocol PropertyLoopable
{
func allProperties() throws -> [String: Any]
}
Of course we should make use of new protocol extensions to provide default implementation for this protocol:
extension PropertyLoopable
{
func allProperties() throws -> [String: Any] {
var result: [String: Any] = [:]
let mirror = Mirror(reflecting: self)
guard let style = mirror.displayStyle where style == .Struct || style == .Class else {
//throw some error
throw NSError(domain: "hris.to", code: 777, userInfo: nil)
}
for (labelMaybe, valueMaybe) in mirror.children {
guard let label = labelMaybe else {
continue
}
result[label] = valueMaybe
}
return result
}
}
So now we can loop over the properties of any class or struct with this method. We just have to mark the class as PropertyLoopable.
In order to keep things static(as in the example) I will add also a singleton:
struct ReuseID: PropertyLoopable {
static let instance: ReuseID = ReuseID()
}
Whether singleton used or not, we could finally loop over the properties like follows:
do {
print(try ReuseID.instance.allProperties())
} catch _ {
}
And that's it with looping struct properties. Enjoy swift ;)

Using hris.to's awesome answer, I wanted to provide a Swift 3 answer that's more to the point and doesn't use singletons.
Protocol & Extension:
protocol Loopable {
func allProperties() throws -> [String: Any]
}
extension Loopable {
func allProperties() throws -> [String: Any] {
var result: [String: Any] = [:]
let mirror = Mirror(reflecting: self)
// Optional check to make sure we're iterating over a struct or class
guard let style = mirror.displayStyle, style == .struct || style == .class else {
throw NSError()
}
for (property, value) in mirror.children {
guard let property = property else {
continue
}
result[property] = value
}
return result
}
}
Example:
struct Person: Loopable {
var name: String
var age: Int
}
var bob = Person(name: "bob", age: 20)
print(try bob.allProperties())
// prints: ["name": "bob", "age": 20]

Now there's a much easier way to do this:
1: Create an Encodable protocol extension:
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
}
2: Make your struct/class conform to Encodable protocol
struct MyStruct: Encodable {...}
class MyClass: Encodable {...}
And then you can get a Dictionary representing your struct/class instance at any time:
var a: MyStruct
var b: MyClass
print(a.dictionary)
print(b.dictionary)
And then you can loop through the keys:
for (key, value) in a.dictionary { ... }
for (key, value) in b.dictionary { ... }

I made a recursive function based on #John R Perry's solution that goes deeper into properties that are objects. It also takes an parameter to limit how many levels deep it goes (default is Int.max) to help prevent stackoverflow's:
protocol Loopable {
func allProperties(limit: Int) [String: Any]
}
extension Loopable {
func allProperties(limit: Int = Int.max) [String: Any] {
return props(obj: self, count: 0, limit: limit)
}
private func props(obj: Any, count: Int, limit: Int) -> [String: Any] {
let mirror = Mirror(reflecting: obj)
var result: [String: Any] = [:]
for (prop, val) in mirror.children {
guard let prop = prop else { continue }
if limit == count {
result[prop] = val
} else {
let subResult = props(obj: val, count: count + 1, limit: limit)
result[prop] = subResult.count == 0 ? val : subResult
}
}
return result
}
}
I got rid of the check for if the object is a class or struct because that the parameter not being a class or struct is the base case of the recursive function, and it was easier to handle it manually than with errors.
Testing it:
class C {
var w = 14
}
class B: Loopable {
var x = 12
var y = "BHello"
var z = C()
static func test() -> String {
return "Test"
}
}
class A: Loopable {
var a = 1
var c = 10.0
var d = "AHello"
var e = true
var f = B()
var g = [1,2,3,4]
var h: [String: Any] = ["A": 0, "B": "Dictionary"]
var i: Int?
}
print(A().allProperties())
prints:
["e": true, "g": [1, 2, 3, 4], "f": ["z": ["w": 14], "x": 12, "y": "BHello"], "h": ["A": 0, "B": "Dictionary"], "c": 10.0, "i": nil, "d": "AHello", "a": 1]
(Dictionaries are unordered, so if you get a different order, that's why)

Here is an example of iterating over struct properties (reuse identifiers of UITableViewCells and the corresponding NIB-names) using Swifts tuple feature. This is useful if you like organizing your cells in nib files and have a UIViewController that makes use of many different cell types.
struct ReuseID {
static let prepaidRechargeCreditCell = "PrepaidRechargeCreditCell"
static let threeTitledIconCell = "ThreeTitledIconCell"
static let usageCell = "UsageCell"
static let detailsCell = "DetailsCell"
static let phoneNumberCell = "PhoneNumberCell"
static let nibNamePrepaidRechargeCreditCell = "PrepaidRechargeCreditCell"
static let nibNameThreeTitledIconCell = "IconCellWith3Titles"
static let nibNameUsageCell = "ListElementRingViewCell"
static let nibNameDetailsCell = "ListElementStandardViewCell"
static let nibNamePhoneNumberCell = "PhoneNumberCell"
static let allValuesAndNibNames = [
(ReuseID.prepaidRechargeCreditCell, ReuseID.nibNamePrepaidRechargeCreditCell),
(ReuseID.threeTitledIconCell, ReuseID.nibNameThreeTitledIconCell),
(ReuseID.usageCell, ReuseID.nibNameUsageCell),
(ReuseID.detailsCell, ReuseID.nibNameDetailsCell),
(ReuseID.phoneNumberCell, ReuseID.nibNamePhoneNumberCell)]
}
With that struct it is easy to register all cell types using a for-loop:
for (reuseID, nibName) in ReuseID.allValuesAndNibNames {
if let xibPath = NSBundle.mainBundle().pathForResource(nibName, ofType: "nib") {
let fileName = xibPath.lastPathComponent.stringByDeletingPathExtension
self.tableView.registerNib(UINib(nibName: fileName, bundle: nil), forCellReuseIdentifier: reuseID)
} else {
assertionFailure("Didn't find prepaidRechargeCreditCell 👎")
}
}

Knowing that in Swift 1.2 you could use reflect(), and since Swift 2 you can use Mirror, here is an addition to hris.to's answer for Swift 3 and 4.
protocol Loopable {
var allProperties: [String: Any] { get }
}
extension Loopable {
var allProperties: [String: Any] {
var result = [String: Any]()
Mirror(reflecting: self).children.forEach { child in
if let property = child.label {
result[property] = child.value
}
}
return result
}
}
Usage on any struct or class:
extension NSString: Loopable {}
print("hello".allProperties)
// ["_core": Swift._StringCore(_baseAddress: Optional(0x00000001157ee000), _countAndFlags: 5, _owner: nil)]

struct IdentifiersModel : Codable {
let homeReuseID: String = "Home_Reuse_ID"
let offersReuseID: String = "Offers_Reuse_ID"
let testReuseID: String = "Test_Reuse_ID"
func toDic() -> [String:Any] {
var dict = [String:Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
if let key = child.label {
dict[key] = child.value
}
}
return dict
}
}
Create a new instance from the struct and call toDic() function
let identifiersModel = IdentifiersModel()
print(identifiersModel.toDic())
Output:
["offersReuseID": "Offers_Reuse_ID", "testReuseID": "Test_Reuse_ID", "homeReuseID": "Home_Reuse_ID"]

I found that RPatel99's answer worked the best for me, but it did not handle the case where there as an array of Loopables inside another Loopable.
Here is a version that fixes that issue.
protocol Loopable {
func allProperties(limit: Int) -> [String: Any]
}
extension Loopable {
func allProperties(limit: Int = Int.max) -> [String: Any] {
return props(obj: self, count: 0, limit: limit)
}
private func props(obj: Any, count: Int, limit: Int) -> [String: Any] {
let mirror = Mirror(reflecting: obj)
var result: [String: Any] = [:]
for (property, value) in mirror.children {
var val = value
if let values = value as? [Loopable] {
var vals = [Any]()
for val in values {
vals.append(val.allProperties())
}
val = vals
}
guard let prop = property else { continue }
if limit == count {
result[prop] = val
} else {
let subResult = props(obj: val, count: count + 1, limit: limit)
result[prop] = subResult.count == 0 ? val : subResult
}
}
return result
}
}
class C {
var w = 14
}
class B: Loopable {
var x = 12
var y = "BHello"
var z = C()
static func test() -> String {
return "Test"
}
}
class A: Loopable {
var a = 1
var c = 10.0
var d = "AHello"
var e = B()
var f = [B(), B(), B()]
var g = [1,2,3,4]
var h: [String: Any] = ["A": 0, "B": B().allProperties()]
var i: Int?
}
print(A().allProperties())
The result I get is this
["h": ["A": 0, "B": ["z": ["w": 14], "y": "BHello", "x": 12]], "e": ["y": "BHello", "z": ["w": 14], "x": 12], "f": [["y": "BHello", "z": ["w": 14], "x": 12], ["x": 12, "z": ["w": 14], "y": "BHello"], ["y": "BHello", "x": 12, "z": ["w": 14]]], "i": nil, "g": [1, 2, 3, 4], "a": 1, "d": "AHello", "c": 10.0]
I hope someone else will find this useful.
This result could probably also be achieved by something like this

Related

Retrieving custom Object from NSUserDefaults

I have a dictionary of values
class Objects {
let values = [
"AAA": ["AAAAAAA", "111111111"],
"BBB": ["BBBBBBBB", "2222222"],
"CCC": ["CCCCCCCC", "3333333333"],
"DDD": ["DDDDDD", "44444444"],
]
}
Which I turn into custom objects and display in a tableview.
struct Object {
var heading : String!
var imageName: String!
}
Then the user can select two objects to store in UserDefaults
let defaults = UserDefaults.standard
func addObject(_ object1: String, object2: String) {
// Get objects for user
var userObjects = fetchObjectsFromUserDefaults()
// Add to user currencies
userObjects.append([object1,object2])
//Update user defaults value for key
// [ [Object1, Object2], [Object1, Object2] ]
defaults.set(userObject, forKey: "userCurrencies")
}
// Gets [[String]] values from user defaults for key
func fetchObjectsFromUserDefaults() -> [[String]] {
if let objects = UserDefaults.standard.value(forKey: "userObjects") {
return objects as! [[String]]
} else {
return []
}
}
// Uses [[String]] values and turns them into objects by using the dictionary to determine property values
func getObject() -> [[Object]] {
let userObject = fetchObjectsFromUserDefaults()
// [ [Object1, Object2], [Object1, Object2] ]
let object = Object()
var fetchedObject = [[Object]]()
if !userObjects.isEmpty {
for c in userObjects {
var set = [Object]()
if let val = object.available[c[0]] {
set.append(Currency(currencyTitle: c[0], imageName: val[0] ))
}
if let val2 = object.available[c[1]] {
set.append(Currency(currencyTitle: c[0], imageName: val2[0] ))
}
if !set.isEmpty {
fetchedObjects.append(set)
}
}
return fetchedObjects
}
return [[]]
}
View Controller
Here I get the objects to load into the TableView
let fetched = dataManager.getObjects
print(fetched)
self.objects = fetched()
However this prints out
(Function)
What am I doing wrong and is their a better method of storing and retrieving this data from user defaults ? I feel this is over kill and there is a swifter and safer approach.
Step 1.
Make your struct Codable. The compiler will write all of the functions for you if all of the members of your struct are Codable and fortunately String is Codable so its just:
struct Object: Codable {
var heading : String!
var imageName: String!
}
Step 2.
The problem with Codable is that it converts to and from Data, but you want to convert to and from a Dictionary. Fortunately JSONSerialization converts from Data to Dictionary so make a new protocol and give it a default implementation with a protocol extension:
protocol JSONRepresentable {
init?(json: [String: Any])
func json() -> [String: Any]
}
extension JSONRepresentable where Self: Codable {
init?(json: [String:Any]) {
guard let value = (try? JSONSerialization.data(withJSONObject: json, options: []))
.flatMap ({ try? JSONDecoder().decode(Self.self, from: $0) }) else {
return nil
}
self = value
}
func json() -> [String:Any] {
return (try? JSONEncoder().encode(self))
.flatMap { try? JSONSerialization.jsonObject(with: $0, options: []) } as? [String: Any] ?? [:]
}
}
Step 3.
Conform your struct to JSONRepresentable
struct Object: Codable, JSONRepresentable {
var heading : String!
var imageName: String!
}
Step 4.
Place your object into Userdefaults and get it out again:
let o = Object.init(heading: "s", imageName: "a").json()
UserDefaults.standard.set(o, forKey: "test")
print(Object.init(json: UserDefaults.standard.dictionary(forKey: "test") ?? [:]))
Here is the whole playground if you want to try:
import UIKit
struct Object: Codable, JSONRepresentable {
var heading : String!
var imageName: String!
}
protocol JSONRepresentable {
init?(json: [String: Any])
func json() -> [String: Any]
}
extension JSONRepresentable where Self: Codable {
init?(json: [String:Any]) {
guard let value = (try? JSONSerialization.data(withJSONObject: json, options: []))
.flatMap ({ try? JSONDecoder().decode(Self.self, from: $0) }) else {
return nil
}
self = value
}
func json() -> [String:Any] {
return (try? JSONEncoder().encode(self))
.flatMap { try? JSONSerialization.jsonObject(with: $0, options: []) } as? [String: Any] ?? [:]
}
}
let o = Object.init(heading: "s", imageName: "a").json()
UserDefaults.standard.set(o, forKey: "test")
print(Object.init(json: UserDefaults.standard.dictionary(forKey: "test") ?? [:]))

Swift iOS -Compare elements in 1 array by property

I have an array of objects and I want to compare the objects based on property to find out if the properties are all the same. Right now I loop through all the objects, place all values of the properties in a separate array, and then use filterArr.allSatisfy { $0 == filterArr.last } to detemermine wether the properties are all the same or not.
This method works but I know there has to be a more elegant way then what I'm doing.
I actually went looking for an answer to this but every single thing I came across was comparing the elements of 2 different arrays instead of 1.
class IceCream {
var flavor: String?
var price: Double?
}
let iceCream1 = IceCream()
iceCream1.flavor = "vanilla"
iceCream1.price = 1.5
let iceCream2 = IceCream()
iceCream2.flavor = "chocolate"
iceCream2.price = 2.0
let iceCream3 = IceCream()
iceCream3.flavor = "vanilla"
iceCream3.price = 1.5
let iceCream4 = IceCream()
iceCream4.flavor = "strawberry"
iceCream4.price = 2.5
let iceCreams = [iceCream1, iceCream2, iceCream3, iceCream4]
var filterArr = [String]()
for iceCream in iceCreams {
filterArr.append(iceCream.flavor ?? "")
}
let areItemsEqual = filterArr.allSatisfy { $0 == filterArr.last }
print(areItemsEqual) // prints false
You can avoid having to initialize and then assign the properties on separate lines with a struct.
struct IceCream {
let flavor: String?
let price: Double?
}
let iceCreams: [IceCream] = [
IceCream(flavor: "vanilla", price: 1.5),
IceCream(flavor: "chocolate", price: 2.0),
IceCream(flavor: "vanilla", price: 1.5),
IceCream(flavor: "strawberry", price: 2.5)
]
Using the generics sugar provided by #Alexander and #matt, we have a nice looking extension.
extension Collection {
func allEqual<T: Equatable>(by key: KeyPath<Element, T>) -> Bool {
return allSatisfy { first?[keyPath:key] == $0[keyPath:key] }
}
}
print(iceCreams.allEqual(by: \.flavor))
Alternatively, you could specify an IceCream be equal to one another by flavor.
extension IceCream: Equatable {
static func == (lhs: IceCream, rhs: IceCream) -> Bool {
return lhs.flavor == rhs.flavor
}
}
extension Collection where Element: Equatable {
func allEqual() -> Bool {
return allSatisfy { first == $0 }
}
}
print(iceCreams.allEqual())
Here's a pretty Swifty way to do it. I define an extension on Collection that checks for equality among the collection's items, according to a given predicate:
extension Collection {
func allEqual<T: Equatable>(by deriveKey: (Element) -> T) -> Bool {
guard let firstElement = self.first else {
return true // empty lists are all-equal
}
let sampleKey = deriveKey(firstElement)
return self.dropFirst().allSatisfy{ deriveKey($0) == sampleKey }
}
}
struct IceCream {
let flavor: String
let price: Double
}
let iceCreams = [
IceCream(flavor: "vanilla", price: 1.5),
IceCream(flavor: "chocolate", price: 2.0),
IceCream(flavor: "vanilla", price: 1.5),
IceCream(flavor: "strawberry", price: 2.5)
]
let allItemsEqualByFlavour = iceCreams.allEqual(by: { $0.flavor})
print(allItemsEqualByFlavour) // false
let vanillaOnlyIceCreams = iceCreams.filter{ $0.flavor == "vanilla" }
print(vanillaOnlyIceCreams.allEqual(by: { $0.flavor})) // true
Here's an elegant way to make sure your ice creams are the same along any arbitrary axis, i.e. either flavor or price or any other equatable property you may later inject:
extension Array {
func allSameForProperty<T:Equatable> (_ p:KeyPath<Element,T>) -> Bool {
return self.isEmpty || self.allSatisfy{
self.first![keyPath:p] == $0[keyPath:p]
}
}
}
Let's test it. First, some initial conditions:
struct Icecream {
let flavor : String
let price : Double
}
let arr = [
Icecream(flavor: "vanilla", price: 1.5),
Icecream(flavor: "vanilla", price: 1.75)
]
And now the actual test:
arr.allSameForProperty(\Icecream.flavor) // true
arr.allSameForProperty(\Icecream.price) // false

Swift Array Type

I think I should be more clear, here are my classes:
import Foundation
import Alamofire
public class BaseService<T> : ServiceRequest {
public var requestType: Alamofire.Method = .GET
public var path: String = "/"
var requireLogin:Bool = false
var consumer:RequestInformer?
public func requestSuccess<T>(request: Request, response: T) {
consumer?.requestSuccess(request, response: response)
}
public func requestFailed(request: Request, error: NSError) {
consumer?.requestFailed(request, error: error)
}
}
extension BaseService where T:Sortable {
func start() {
if requireLogin {
}
//problem is here, T is [CampaignModel] but I need CampaignModel class, not array. NetworkManager.sharedInstance.sendRequest(self,ResponseListModel<T>.self)
}
}
extension BaseService where T:NSObject {
func start() {
if requireLogin {
}
NetworkManager.sharedInstance.sendRequest(self,ResponseModel<T>.self)
}
}
Then I want to use BaseService like below:
import Alamofire
public class ListCampaignService : BaseService<[CampaignModel]> {
override init() {
super.init()
path = "/xxx/yyy.json"
requestType = .GET
}
}
That means ListCampaignService returns CampaignModel array. Bu I have to handle object return type rather than array, something like below
import Alamofire
public class SomeOtherService : BaseService<SomeClass> {
override init() {
super.init()
path = "/aaa/bbb.json"
requestType = .GET
}
}
Try this as a start:
func getType<T>(array:[T]) -> T.Type {
return T.self
}
getType(["1", "2", "3"]) // String.Type
getType([1, 2, 3]) // Int.Type
getType([1, "2", 3.0]) // NSObject.Type
getType([[1], [2], [3]]) // Array<Int>.Type
getType([]) // compile time error "Generic parameter 'T' could not be inferred"
If you're trying to get all the objects of a particular type from a generic Array you could extend Array like this:
extension Array {
func itemsOfType<T>() -> [T] {
return self.flatMap { $0 as? T }
}
}
let x = [1, 2, 3, "foo", "bar", NSDate()]
let ints : [Int] = x.itemsOfType() // [1, 2, 3]
let altInts = x.itemsOfType() as [Int] // [1, 2, 3]
let str : [String] = x.itemsOfType() // ["foo", "bar"]
let dates : [NSDate] = x.itemsOfType() // ["May 17, 2016, 6:23 AM"]
If you use generics in your function, it's really simple:
func test<T>(array: [T]) {
// array elements are of type T
print(T.self)
}
If you don't use generics (outside functions) then you can do this:
let array = [1, 2, 3]
let type = array.dynamicType.Element.self
print(type) // Int.Type
If you're coming from the future: Swift 3.0 will use Self instead of dynamicType

How to return a JSONObject as a String in swift?

I would like to create a new object called RegisterIn, the target of the object is to generate a json object as dictionary and return as a String
Here is my code
public class RegisterIn {
private var a : String = "" //required
private var b: String = "" //required
private var c: String = "" //required
private var d: Int = 0 //required
private let BD_a : String = "a"
private let BD_b : String = "b"
private let BD_c : String = "c"
private let BD_d : String = "d"
init(a: String, b: String, c: String, d: Int) {
self.a = a
self.b = b
self.c = c
self.d = d
}
func getJSONObject() {
let jsonDic : [String: AnyObject] = [
BD_a: a,
BD_b: b,
BD_c: c,
BD_d: d
]
do {
let jsonObject = try NSJSONSerialization.dataWithJSONObject( jsonDic, options: NSJSONWritingOptions.PrettyPrinted)
} catch let error as NSError {
print(error)
}
}
func toString() {
return String(getJSONObject()) <- this line occur error
}
}
At function getJSONObject, I think it return a jsonObject as [String: AnyObject]. In my ViewController, I want to assign it to a Label.text, it always
my ViewController:
#IBOutlet weak var jsonLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let a = RegisterIn.init(a: "123", b: "456", c: "789", d: 00000)
jsonLabel.text = a
}
I think I have to change some code in RegisterIn class, really need some help!
you never returned a string from getJSONObject(), try
func getJSONObject() -> String? {
let jsonDic : [String: AnyObject] = [
BD_a: a,
BD_b: b,
BD_c: c,
BD_d: d
]
do {
let jsonObject = try NSJSONSerialization.dataWithJSONObject( jsonDic, options: NSJSONWritingOptions.PrettyPrinted)
return NSString(data: jsonObject, encoding:NSUTF8StringEncoding)
} catch let error as NSError {
print(error)
return nil
}
}
func toString() {
return getJSONObject() //to be more correct, but this function is sort of redundant, just call getJSONObject directly, but up to you whats the best
}
It may be
"let jsonObject = try NSJSONSerialization.dataWithJSONObject( jsonDic, options: NSJSONWritingOptions.PrettyPrinted)"
has problem.
You can make options with null for the replace the PrettyPrinted.
"options:[])"

Convert Swift Dictionary With Enum Value To NSDictionary

If I have a dictionary with the type Dictionary<String, MyEnum>, I cannot figure out how to convert it to an NSDictionary so I can serialiaze it to JSON via NSJSONSerialize.dataWithJSONObject.
The compiler tells me that "Dictionary<String, MyEnum> is not convertible to NSDictionary". Do I need to create a new dictionary with the string values of the enum a la
var newDict = Dictionary<String, String> (or <String, AnyObject>);
for (key, val) in oldDict {
newDict[key] = val;
}
or is there a better way?
NSJSONSerialize and friends can only deal with small subset of NSObject children (NSNumber, NSString, NSArray, NSDictionary, and NSNull) that correspond to the JSON native data types. In addition, a Dictionary can only be converted to an NSDictionary if both the key and value are NSObject or inherently convertible to NSObject (String and numeric types).
In order to serialize your Dictionary you'll need to convert the Enum to one of those NSJSONSerialize data-types, similar to your example:
enum MyEnum : String {
case one = "one"
case two = "two"
}
let dict = ["one":MyEnum.one, "two":MyEnum.two]
var newDict = Dictionary<String, String>()
for (key, val) in dict {
newDict[key] = val.rawValue
}
let data = NSJSONSerialization.dataWithJSONObject(newDict, options: .allZeros, error: nil)
As an alternative, since this kind of manipulation is fairly common, you might want to consider adding this category to Dictionary, which gives it a convenient map function:
extension Dictionary {
init(_ pairs: [Element]) {
self.init()
for (k, v) in pairs {
self[k] = v
}
}
func map<K: Hashable, V>(transform: Element -> (K, V)) -> [K: V] {
return Dictionary<K, V>(Swift.map(self, transform))
}
}
Once that's done, the conversion is simply:
let mapped = dict.map { (key, value) in (key, value.rawValue) }
public protocol EnumCollection: Hashable {
static func cases() -> AnySequence<Self>
static var allValues: [Self] { get }
}
public extension EnumCollection {
public static func cases() -> AnySequence<Self> {
return AnySequence { () -> AnyIterator<Self> in
var raw = 0
return AnyIterator {
let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } }
guard current.hashValue == raw else {
return nil
}
raw += 1
return current
}
}
}
public static var allValues: [Self] {
return Array(self.cases())
}
}
public enum ScreenName: String, EnumCollection
{
case HomeView = "Home View"
case SignUpViewController = "Domain Validate View"
var name: String {
get { return String(describing: self) }
}
var description: String {
get { return String (self.rawValue) }
}
// a dictionary mapping the raw values to the values
static var allValuesDictionary: [String : String]{
var values = [String : String]()
let mirror = Mirror(reflecting: ScreenName.allValues)
for (_, v) in mirror.children{
guard let screenName = v as? ScreenName else{continue}
values[screenName.name] = screenName.description
}
return values
}
}
Now you can call the method allValuesDictionary will return the dictionary with <key, value> = <case, rawValue>
<HomeView, "Home View">, <SignUpViewController, "Domain Validate View">

Resources