Let's say there is a struct with two variables: a and b.
It is possible to declare one of the two variables and ignore the other based on a condition?
In other words, is it possible from this struct:
struct example {
let a: Int
let b: Int
}
To do this:
struct example {
if (condition) {
let a: Int
} else {
let b: Int
}
}
This is not possible, because if you did this, what would this code do:
let e = exampleReturningFunction()
print(e.a)
If condition is false, should this crash? Should a have some default value? What value? (If so, just use a default value in the struct.)
In most cases, what you really want here is an enum with associated data:
enum Example {
case a(Int)
case b(Int)
}
If there are many related properties, you can group them together:
struct A { ... }
struct B { ... }
enum Example {
case a(A)
case b(B)
}
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 have two enums Items and Filters as
enum Items: Int {
case ItemA = 0,
ItemB,
ItemC,
ItemD,
ItemE
}
enum Filters: Int {
case FilterA = 0,
FilterB
}
I need to have the Items enum to be in Int.
And I am trying to implement Filters enum so that:
Filters(0).filter() returns [ItemA, ItemB]
Filters(1).filter() returns [ItemC, ItemD, ItemE]
Filters(0).count() returns 2
Filters(1).count() returns 3
Enums in swift is so much different than how I know enums from C... Can't get a grasp. Thanks!
There are several errors and bad practices in your code.
An enum name must be sigular
An enum case name should begin with a lowercase letter
An enum case name should not contain the name of the enum itself (eg. a instead of ItemA)
The filter and count methods IMO should be added to the Item enum
The filter and count methods should receive a Filter value, not an Int
You are free to manually assign 0 to the first case but this is not needed because the compiler will do it for you
IMO Condition is a better name for the Filter enum
Solution
Here's my solution
enum Condition: Int {
case a, b
}
enum Item: Int {
case a, b, c, d, e
static func filter(with condition: Condition) -> [Item] {
switch condition {
case .a: return [.a, .b]
case .b: return [.c, .d, .e]
}
}
static func count(with condition: Condition) -> Int {
return filter(with: condition).count
}
}
Usage
Item.filter(with: .a) // [a, b]
Item.filter(with: .b) // [c, d, e]
Item.count(with: .a) // 2
Item.count(with: .b) // 3
I hope that you can find this useful:
enum Items: Int {
case ItemA = 0,
ItemB,
ItemC,
ItemD,
ItemE
}
enum Filters: Int {
case FilterA = 0,
FilterB
func items() -> [Items]? {
switch self {
case .FilterA:
return [Items.ItemA, Items.ItemB]
case .FilterB:
return [Items.ItemC]
}
return nil
}
func count() -> Int {
return items()?.count ?? 0
}
}
let filter = Filters.FilterA
filter.items()
filter.count()
you can instantiate filter enum like this also:
let filter = Filters(rawValue: 0)
FYI swift cases should start with lowercase by the book, I left them uppercased to do not creat more confusions compare to your question code snippet.
Since this looks like static data a simple solution would be to keep the different sets of enum elements in a dictionary. And I agree with another poster that the naming of your enum could be better
enum Item: Int {
case a, b, c, d, e
}
let itemFilter = [0: [Item.a, Item.b]],
[1: [Item.c, Item.d, Item.e]]
if let filter = itemFilter[0] {
print(filter)
print(filter.count)
}
I want to get the associated value of swift enum object, is there a way to do it shorter/better than in switch statement below?
enum Test {
case a(Int), b(Int), c(Int)
}
func printValue(_ t: Test) {
switch t {
case .a(let v), .b(let v), .c(let v): print("value \(v)")
}
}
Your code for extracting the associated value from multiple enums is the most economical and easy-to-read, there's no need to improve it.
However, the fact that you are looking to extract an associated value regardless of enum's case suggests that you are not using associated values correctly: rather than associating a value with each individual case, you should create a composite type that holds the Int and an enum without an associated value, i.e.
enum Test {
case a, b, c
}
class MyClass {
var num : Int
var tst : Test
}
Now that the associated value is "outside" each enum element, it can be accessed independently of the case, and you can also give it a meaningful name, which adds to readability of your program.
You might want to use mirror type - it's not the better way, but it can be helpful in some cases:
enum Test {
case a(Int), b(Int), c(Int)
}
func printValue(_ t: Test) {
let mirror = Mirror(reflecting: t)
print(mirror.children.first?.value ?? "")
}
printValue(.a(15))
Also using if/case like this, it's a shorter way if you need to extract value only from one case, sometimes it's helpful:
if case .a(let val) = t {
print("value \(val)")
}
Or may be raw value will fit better for your case:
enum Test: Int {
case a = 1
case b = 2
case c = 5
}
func printValue(_ t: Test) {
print("value \(t.rawValue)")
}
printValue(.a)
I have an enum in C and the index needs to be represented by a String.
How can a Swift enum of String type be used by integer index?
I would like to copy the enum to Swift, set the type to string and define all of the raw values to display text, and then use the C enum value to extract the raw value text for the Swift String enum.
Otherwise I will just create an array of strings.. But the enum would be more usable.
Swift 4.2 introduced CaseIterable which does exactly the same thing without the need to declare an allValues array. It works like this:
enum MyEnum: String, CaseIterable {
case foo = "fooString"
case bar = "barString"
case baz = "bazString"
}
and you can access its values by
MyEnum.allCases
or a value at a specific index by
MyEnum.allCases[index]
In Swift, enum types do not hold its index info of cases (at least, not provided for programmers).
So:
How can a Swift enum of String type be used by integer index?
The answer is "You cannot".
You can bind Int (or enum cases) and String values in many ways other than just create an array of strings..
For example, if your bound Strings can be the same as case labels, you can write something like this:
enum MyEnum: Int {
case foo
case bar
case baz
var string: String {
return String(self)
}
}
if let value = MyEnum(rawValue: 0) {
print(value.string) //->foo
}
If your Strings need to be a little more complex to display text, you can use Swift Dictionary to bind enum cases and Strings.
enum AnotherEnum: Int {
case foo
case bar
case baz
static let mapper: [AnotherEnum: String] = [
.foo: "FooString",
.bar: "BarString",
.baz: "BazString"
]
var string: String {
return AnotherEnum.mapper[self]!
}
}
if let value = AnotherEnum(rawValue: 1) {
print(value.string) //->BarString
}
A little bit more readable than a simple array of strings.
Simple workaround which is also useful if you want to enumerate a string enum.
enum MyEnum: String {
case foo = "fooString"
case bar = "barString"
case baz = "bazString"
static let allValues = [foo, bar, baz] //must maintain second copy of values
}
//enumeration advantage
for value in MyEnum.allValues {
print(value)
}
//get value by index
let value = MyEnum.allValues[1]
print(value) //barString
You can add an index as a part of the enum.
enum StringEnum: String, CaseIterable {
case pawn, rook, knight, bishop, king, queen
var name: String { self.rawValue.uppercased() }
var index: Int { StringEnum.allCases.firstIndex(of: self) ?? 0 }
}
And find enum cases by index with the function:
func findEnum(by index: Int) -> StringEnum? {
StringEnum.allCases.first(where: { $0.index == index })
}
I want to create a protocol that enforces a certain case on all enums conforming to this protocol.
For example, if I have a enum like this:
enum Foo{
case bar(baz: String)
case baz(bar: String)
}
I want to extend it with a protocol that adds another case:
case Fuzz(Int)
Is this possible?
Design
The work around is to use a struct with static variables.
Note: This is what is done in Swift 3 for Notification.Name
Below is an implementation on Swift 3
Struct:
struct Car : RawRepresentable, Equatable, Hashable, Comparable {
typealias RawValue = String
var rawValue: String
static let Red = Car(rawValue: "Red")
static let Blue = Car(rawValue: "Blue")
//MARK: Hashable
var hashValue: Int {
return rawValue.hashValue
}
//MARK: Comparable
public static func <(lhs: Car, rhs: Car) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}
Protocol
protocol CoolCar {
}
extension CoolCar {
static var Yellow : Car {
return Car(rawValue: "Yellow")
}
}
extension Car : CoolCar {
}
Invoking
let c1 = Car.Red
switch c1 {
case Car.Red:
print("Car is red")
case Car.Blue:
print("Car is blue")
case Car.Yellow:
print("Car is yellow")
default:
print("Car is some other color")
}
if c1 == Car.Red {
print("Equal")
}
if Car.Red > Car.Blue {
print("Red is greater than Blue")
}
Note:
Please note this approach is not a substitute for enum, use this only when the values are not known at compile time.
no, since you can't declare a case outside of an enum.
An extension can add a nested enum, like so:
enum Plants {
enum Fruit {
case banana
}
}
extension Plants {
enum Vegetables {
case potato
}
}
Here are a couple additional takes that may help somebody out there:
Using your example:
enum Foo {
case bar(baz: String)
case baz(bar: String)
}
You can consider to "nest" it in a case of your own enum:
enum FooExtended {
case foo(Foo) // <-- Here will live your instances of `Foo`
case fuzz(Int)
}
With this solution, it becomes more laborious to access the "hidden" cases associated type. But this simplification could actually be beneficial in certain applications.
Another alternative passes by just recreate and extend it while having a way to convert Foo into the extended enum FooExtended (eg. with a custom init):
enum FooExtended {
case bar(baz: String)
case baz(bar: String)
case fuzz(Int)
init(withFoo foo: Foo) {
switch foo {
case .bar(let baz):
self = .bar(baz: baz)
case .baz(let bar):
self = .baz(bar: bar)
}
}
}
There may be many places where one, the other, or both of these solutions make absolutely no sense, but I'm pretty sure they may be handy to somebody out there (even if only as an exercise).