Nested and Associated Enum Values Swift - swift

One of the best practices endorsed by NSHipster when dealing with tables or collection views is to use an enum to represent each section like so:
typedef NS_ENUM(NSInteger, SomeSectionType) {
SomeSectionTypeOne = 0,
SomeSectionTypeTwo = 1,
SomeSectionTypeThree = 2
}
That makes it really easy to do a switch statement or ifs like:
if(indexPath.section == SomeSectionTypeOne) {
//do something cool
}
For sections with static content, I extended the concept to include enums for each item:
typedef NS_ENUM(NSInteger, SectionOneItemType) {
ItemTypeOne = 0,
ItemTypeTwo = 1
}
if(indexPath.section == SomeSectionTypeOne) {
switch(indexPath.item) {
case SectionOneItemType:
//do something equally cool
default:
}
}
In Swift, I'd like to replicate the same behavior except this time taking advantage of nested enums. I've been able to come up with this so far:
enum PageNumber {
enum PageOne: Int {
case Help, About, Payment
}
enum PageTwo: Int {
case Age, Status, Job
}
enum PageThree: Int {
case Information
}
case One(PageOne)
case Two(PageTwo)
case Three(PageThree)
}
but I can't see how to go from an NSIndexPath and initialize the correct case and then use a switch statement to extract the values.

Don't think you can use nested enum to decide the section and row cell comes from. Because associated values and raw values can't coexist in Swift enumeration. Multiple enums are needed
enum sectionType: Int {
case sectionTypeOne = 0, sectionTypeTwo, sectionTypeThree
}
enum rowTypeInSectionOne: Int {
case rowTypeOne = 0, rowTypeTwo, rowTypeThree
}
//enum rowTypeInSectionTwo and so on
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
switch (indexPath.section, indexPath.row) {
case (sectionType.sectionTypeOne.rawValue, rowTypeInSectionOne.rowTypeOne.rawValue):
print("good")
default:
print("default")
}

Related

How do I convert an integer to an enum based on its range in Swift?

Is there a way to convert an integer to an enum based on its value range? For the sake of elegance, I prefer to implement it into an enum or auxiliary type rather than as a separate function. Ideally, it would be great if I could perform the conversion using a cast like so:
let readPosition = Position(controller.readVal())
or
let readPosition = (Position) controller.readVal()
Below is my feeble attempt at it:
// Should be sealed, yet extensible, c# has sealed, but not Swift
public enum Position: Int {
case Both = 0, Bottom = 1, Top = 2
}
// Position should not be changed to preserve compatibility.
// Different converters will be implemented by different apps
// overtime.
extension Position {
static func convert(val: Int64?) -> Position {
switch val {
case .some(1), .some(..<8):
return Position.Top
case .some(2), .some(8...):
return Position.Bottom
default:
return Position.Both
}
}
}
You already have all you need, you can now define an initializer:
extension Position {
init(_ val: Int64) {
self = Self.convert(val)
}
}
Then you can use your requested:
let readPosition = Position(controller.readVal())

How to assign raw value to enum in Dart?

In Swift, you can easily assign raw value to enum, eg:
enum Game: Int {
case lol = 1
case dnf = 2
case dota = 3
}
However, you can't assign raw value to enum in Dart:
enum Game {
lol = 1,
dnf = 2,
dota = 3,
}
It's showed error and you can only use the simplest enum:
enum Game {
lol,
dnf,
dota,
}
It's really let me down.
Any way assign raw value to Dart's enum like Swift?
Dart 2.17 support enhanced enum
enum Game {
lol(1),
dnf(2),
dota(3);
const Game(this.value);
final int value;
}
Use it like:
void main() {
const game = Game.lol;
print(game.value); // 1
}
There's an upcoming feature in Dart known as enhanced enums, and it allows for enum declarations with many of the features known from classes. For example:
enum Game {
lol,
dnf,
dota;
int get intValue => index + 1;
}
The feature is not yet released (and note that several things are not yet working), but experiments with it can be performed with a suitably fresh version of the tools by passing --enable-experiment=enhanced-enums.
The outcome is that an enum value of type Game will have a getter intValue that returns the int values mentioned in the question, so print(myGame.intValue) will print 1, 2, or 3.
You can use enum extensions for example
enum EnumAction { subtract, add }
extension EnumActionExtension on EnumAction {
String get name {
switch (this) {
case EnumAction.add:
return 'add';
case EnumAction.subtract:
return 'subtract';
}
}
}
In your case you would return an int and an int value. Enums also have int values assigned to them by default, their respective index. You could call Game.lol.index and it would return 0.
To get an int value, simply pass an enum to the enumGame function.
enum EnumGame { lol, dnf, dota }
enumGame(EnumGame enumGame) {
switch (enumGame) {
case EnumGame.lol:
return 1;
case EnumGame.dnf:
return 2;
case EnumGame.dota:
return 3;
default:
return -1;
}
}

Use a supertype of an enum as a protocol or generic in Swift

I'm writing a class that I'd like be able to work with a "generic int enum", with the conditions that the enum in question provides one or more given definitions.
If I write 'pseudo-swift' to demonstrate what I mean (which doesn't compile!), it looks like:
protocol enum ConformingEnumSupertype: Int {
case a_required_case = 1,
case another_required_case
}
enum ConformingEnum: Int, ConformingEnumSupertype {
case a_required_case = 1,
case another_required_case,
case some_random_case
}
class ClassThatUsesConformingEnum {
let myEnumValue: ConformingEnumSupertype
init(aConformingEnumValue: ConformingEnumSupertype) {
myEnumValue = aConformingEnumValue
}
func compareSomething(someEnumValue: ConformingEnumSupertype) {
if someEnumValue == myEnumValue {
// ...etc...
}
}
}
I'm afraid don't have any compiling sample code for an attempt to put here, because nothing I write that looks like it might work compiles. I've tried using protocol associated types and typealiases, but I've not managed yet.

Using Enum in if-else in Swift

I have the following for loops with if-else condition. However, it is not quite intituitve for others to read the code at a first glance. therefore I am thinking to use Enum here.
for row in 0..<result.count {
for column in 0..<result[row].count {
if column == 0 {
// add hotels here
} else if column == 1 {
// add motels here
}
}
}
I am trying to use the following enum, but I am confused how to put into if-else condition.
enum ResortColumn {
case hotel
case motel
}
If you want to compare your enum type to an integer, you need to declare it an Int and compare its rawValue.
enum ResortColumn: Int {
case hotel
case motel
}
if column == ResultColumn.hotel.rawValue { // etc.
See the second "enum Planet" example on this page, and read the rest for a full understanding of Swift enums:
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html
enum ResortColumn: Int {
case hotel
case motel
}
for row in 0 ..< result.count {
for column in 0 ..< result[row].count {
let resortColumn = ResortColumn(rawValue: column)
switch resortColumn {
case .hotel:
// add hotels here
case .motel:
// add motels here
}
}
}
This is the best what I can come up with the context you gave. In this case, it does really matter what is behind the result variable

Access an enum value by its hashvalue?

enum have a property named 'hashValue' which is its index inside the enum.
Now my question is, is it possible to access its value by using a number? Ex: let variable:AnEnum = 0
If you want to map enum values to integers, you should do so directly with raw values. For example (from the Swift Programming Language: Enumerations):
enum Planet: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
let possiblePlanet = Planet(rawValue: 7)
I don't believe there's any documentation promising that an enum's hashValue is anything in particular (if you have a link, I've be very interested). In the absence of that, you should be explicit in your assignment of raw values.
enum Opponent: String {
case Player
case Computer
static func fromHashValue(hashValue: Int) -> Opponent {
if hashValue == 0 {
return .Player
} else {
return .Computer
}
}
}
Explanation:
Since there is no way to get back an enum value from its hashValue, you have to do it manually. It's not pretty, but it works. You essentially create a function that allows you to pass in the index of the value you want and manually return that enum value back to the caller. This could get nasty with an enum with tons of cases, but it works like a charm for me.
Swift 4, iOS 12:
Simply make your enum with explicitly setting raw type (like Int in below example):
enum OrderStatus: Int {
case noOrder
case orderInProgress
case orderCompleted
case orderCancelled
}
Usage:
var orderStatus: OrderStatus = .noOrder // default value
print(orderStatus.rawValue) // it will print 0
orderStatus = .orderCompleted
print(orderStatus.rawValue) // it will print 2
Swift 4.2 🔸(Based on previous answer by #Stephen paul)
This answer uses switch instead of if/else clauses. And returns optional as your not garantueed that the hash provided will match.
enum CellType:String{
case primary,secondary,tierary
/**
* NOTE: Since there is no way to get back an enum value from its hashValue, you have to do it manually.
* EXAMPLE: CellType.fromHashValue(hashValue: 1)?.rawValue//👉primary
*/
static func fromHashValue(hashValue: Int) -> CellType? {
switch hashValue {
case 0:
return .primary
case 1:
return .secondary
case 2:
return .tierary
default:
return nil
}
}
}
Your requirement is to have this line of code working, where 0 is the hashValue of the enum variable (note that starting with Xcode 10, 0 is never a valid hashValue...):
let variable:AnEnum = 0
This is simply done by making your enum ExpressibleByIntegerLiteral and CaseIterable:
extension AnEnum: CaseIterable, ExpressibleByIntegerLiteral {
typealias IntegerLiteralType = Int
public init(integerLiteral value: IntegerLiteralType) {
self = AnEnum.allCases.first { $0.hashValue == value }!
}
}
The CaseIterable protocol is natively available with Swift 4.2, and you can implement it yourself for older swift versions (Swift 3.x, 4.x) using the code from https://stackoverflow.com/a/49588446/1033581.
Actually, with Swift 4.2, the hashValue is not the index inside the enum anymore.
Edit: Just found a safer way to achieve that. You can use the CaseIterable (Swift 4.2) and pick the desired case in the allCases collection.
enum Foo: CaseIterable {
case bar
case baz
init?(withIndex index: Int) {
guard Foo.allCases.indices ~= index else { return nil }
self = Foo.allCases[index]
}
}
Foo(withIndex: 0) // bar
Foo(withIndex: 1) // baz
Foo(withIndex: 2) // nil
Note: I'm leaving this little trick here, because playing with unsafe pointer is fun, but please, do not use this method to create a case with an index.
It relies on Swift memory representation which might change without notice, and is really unsafe because using a wrong index produce a runtime error.
That being said, in Swift, a case is represented using an Int in raw memory. So you can use this to build a case using unsafe pointers.
enum Foo {
case bar
case baz
init(withIndex index: UInt8) {
var temp: Foo = .bar
withUnsafeMutablePointer(to: &temp) { pointer in
let ptr = UnsafeMutableRawPointer(pointer).bindMemory(to: UInt8.self, capacity: 1)
ptr.pointee = index
}
self = temp
}
}
Foo(withIndex: 0) // bar
Foo(withIndex: 1) // baz
Foo(withIndex: 2) // runtime error !