I have properties in enum and I need an array of those constants to match them with String key.
I need to iterate through them automatically. I cannot change input since it is generated.
public enum Items {
public static let a = Assets(named: "A")
public static let b = Assets(named: "B")
public static let c = Assets(named: "C")
public static let d = Assets(named: "D")
public static let e = Assets(named: "E")
}
I tried Mirror them.
If you are just using the enum as a placeholder to put your static variables in, it's easy. Just create a static array with all the properties in it.
struct Assets
{
let name: String
var assets: String { "assets for \(name)" }
init(named: String)
{
self.name = named
}
}
enum Items2 {
static let a = Assets(named: "A")
static let b = Assets(named: "B")
static let c = Assets(named: "C")
static let d = Assets(named: "D")
static let e = Assets(named: "E")
static var allItems: [Assets] { return [a, b, c, d, e] }
}
for item in Items2.allItems
{
print(item.assets)
}
However, if you are concerned that you might forget to add new properties to the array, you can use the enum properly. Use an enum with a rawValue of String and that conforms to CaseIterable and define all your cases. Then add a property that gets the Assets based on the case's rawValue. Then you can use the enum's allCases property to get all the cases.
enum Items: String, CaseIterable
{
case a = "A"
case b = "B"
case c = "C"
case d = "D"
case e = "E"
var assets: Assets { Assets(named: rawValue) }
static var allItems: [Assets] { allCases.map{ $0.assets } }
}
for item in Items.allItems
{
print(item.assets)
}
A CaseIterable enum has the allCases property synthesised automatically. If you add a new case it is automatically in allCases.
Related
I would like to have a variable, which can have multiple types (only ones, I defined), like:
var example: String, Int = 0
example = "hi"
This variable should be able to hold only values of type Int and String.
Is this possible?
Thanks for your help ;)
An “enumeration with associated value” might be what you are looking for:
enum StringOrInt {
case string(String)
case int(Int)
}
You can either assign a string or an integer:
var value: StringOrInt
value = .string("Hello")
// ...
value = .int(123)
Retrieving the contents is done with a switch-statement:
switch value {
case .string(let s): print("String:", s)
case .int(let n): print("Int:", n)
}
If you declare conformance to the Equatable protocol then
you can also check values for equality:
enum StringOrInt: Equatable {
case string(String)
case int(Int)
}
let v = StringOrInt.string("Hi")
let w = StringOrInt.int(0)
if v == w { ... }
Here is how you can achieve it. Works exactly how you'd expect.
protocol StringOrInt { }
extension Int: StringOrInt { }
extension String: StringOrInt { }
var a: StringOrInt = "10"
a = 10 //> 10
a = "q" //> "q"
a = 0.8 //> Error
NB! I would not suggest you to use it in production code. It might be confusing for your teammates.
UPD: as #Martin R mentioned: Note that this restricts the possible types only “by convention.” Any module (or source file) can add a extension MyType: StringOrInt { } conformance.
No, this is not possible for classes, structs, etc.
But it is possible for protocols.
You can this:
protocol Walker {
func go()
}
protocol Sleeper {
func sleep()
}
var ab = Walker & Sleeper
or even
struct Person {
var name: String
}
var ab = Person & Walker & Sleeper
But I don't recomment use this way.
More useful this:
struct Person: Walker, Sleeper {
/// code
}
var ab = Person
You can use Tuple.
Example:
let example: (String, Int) = ("hi", 0)
And access each data by index:
let stringFromExampleTuple = example.0 // "hi"
let intFromtExampleTuple = example.1 // 0
I am new on swift. I have 2+ enums and i would like to create enum to object generator function. I couldn't find what type I should use for the Enum parameter in my function.
My enums;
public enum Animal: String, CaseIterable {
case DOG = "dog"
case CAT = "cat"
case BIRD = "bird"
}
public enum Car: String, CaseIterable {
case BMW = "bmw"
case AUDI = "audi"
}
My Function;
func enumToObj(Enum:TYPE?) -> Dictionary <String, String> {
var enumsObject: [String:String] = [:];
for enumData in Enum.allCases {
let value = enumData.rawValue;
enumsObject[value] = value;
}
return enumsObject;
}
//my expectation
enumToObj(Animal) && enumToObj(Car)
You can define a generic function which take as argument a type that is CaseIterable and RawRepresentable:
func enumToObj<T>(_ Enum:T.Type) -> [String: T.RawValue]
where T: CaseIterable & RawRepresentable
{
var enumsObject: [String: T.RawValue] = [:];
for enumData in Enum.allCases {
let value = enumData.rawValue;
enumsObject["\(enumData)"] = value;
}
return enumsObject;
}
print(enumToObj(Animal.self))
// ["CAT": "cat", "DOG": "dog", "BIRD": "bird"]
print(enumToObj(Car.self))
// ["AUDI": "audi", "BMW": "bmw"]
Using reduce(into:_:) this can also be written as
func enumToObj<T>(_ Enum:T.Type) -> [String: T.RawValue]
where T: CaseIterable & RawRepresentable
{
return Enum.allCases.reduce(into: [:]) { (enumsObject, enumData) in
let value = enumData.rawValue;
enumsObject["\(enumData)"] = value;
}
}
I am trying to write a generic function in the the body of the function, it reads one of the property of the Type.
My function:
func doSomething<Key>(
_ key: Key
) -> Int {
let a = key.aProperty
return doSomethingElse(a)
}
and I have put different struct to doSomething as long as they have the 'aProperty' as one of its properties
struct A {
let a : String
let aProperty: String
}
struct B {
let b : String
let aProperty: String
}
let a = A()
let b = B()
doSomething<A>(a)
doSomething<B>(b)
But I get compiler error in my doSomething function saying 'Value of type 'Key' has no member 'aProperty' .
So can you please tell me what am I missing in defining my doSomething function?
You can use protocol.
func doSomethingElse(_ a: String) -> Int { 0 }
protocol HasAProperty {
var aProperty: String { get }
}
func doSomething<Key: HasAProperty>(
_ key: Key
) -> Int {
let a = key.aProperty
return doSomethingElse(a)
}
struct A: HasAProperty {
let a : String
let aProperty: String
}
struct B: HasAProperty {
let b : String
let aProperty: String
}
let a = A(a: "a", aProperty: "aProperty")
let b = B(b: "b", aProperty: "aProperty")
doSomething(a)
doSomething(b)
Given the following codable struct:
struct MyBanana: Codable {
var b: String?
var z: String?
enum CodingKeys: String, CodingKey {
case b = "B"
case z = "ZOOM"
}
}
How can i get an array of strings like so: ["B", "ZOOM"] without an instance of MyBanana?
Additionally, is there a way to get this from auto-synthesised CodingKeys, e.g.
struct MyBanana: Codable {
var b: String?
var z: String?
}
would return ["b","z"]
I tried the following for the first question:
func whatever<T, Key>(keyedBy: Key.Type) -> [T] where T: Codable, Key: CodingKey
{
let allKeys: [Key] = [Key]()
for k in keyedBy {
allKeys.append(k)
}
return [T]()
}
But i get
Type 'Key.Type' does not conform to protocol 'Sequence'
Update
For declared CodingKeys #Cameron Porter's answer works fine (add CaseIterable to the enum)
For synthesized ones this works, albeit pretty manual still:
extension Decodable {
func getAllCodingKeys(_ activator: (String) -> String) -> [String] {
let mirror = Mirror(reflecting: self)
return mirror.children.filter({ $0.label != nil }).map({ activator($0.label!) })
}
}
Then in your Codable, add
func getAllKeys() -> [String] {
return self.getAllCodingKeys { CodingKeys(stringValue: $0)!.stringValue }
}
struct MyBanana: Codable {
var b: String?
var z: String?
enum CodingKeys: String, CodingKey, CaseIterable {
case b = "B"
case z = "ZOOM"
}
static var allCases: [String] {
return CodingKeys.allCases.map { $0.rawValue }
}
}
And then you can get your array of coding keys as
MyBanana.allCases
Doesn't work for auto-synthesised coding keys however
I have the following situation
enum FooEnum: Int {
fooCase = 1245325,
fooCase2 = 3525325
}
Is there a way for me to somehow pass the string fooCase or fooCase2 to the FooEnum type and the FooEnum to generate a variable of type FooEnum with the following enum presentations: FooEnum.fooCase or FooEnum.fooCase2
PS: I can't change Int to String since I am keeping an order with the integers.
Example: (Pseudo code)
FooEnum c = FooEnum("fooCase")
c has chosen fooCase
Like structs and classes you can add initializers to enums
enum FooEnum: Int {
case fooCase = 1245325
case fooCase2 = 3525325
public init?(_ string: String) {
switch string {
case "fooCase": self = .fooCase
case "fooCase2": self = .fooCase2
default: return nil
}
}
}
let c = FooEnum("fooCase") // `FooEnum c =` is ObjC syntax ;)