select random value from given enum
public class SwiftConfettiView: UIView {
public enum ConfettiType {
case confetti
case triangle
case star
case diamond
case image(UIImage)
}
// usage in other class
confettiView.type = .confetti
Would like to set randomly confettiView triangle, star,diamond,confetti
//This will not work
confetti.type = ConfettiType.allCases.randomElement()!
Type 'SwiftConfettiView.ConfettiType' has no member 'allCases'
//So, all all confetti to an array list and load in from there!
var confettiList = [SwiftConfettiView.ConfettiType.confetti
, SwiftConfettiView.ConfettiType.diamond
, SwiftConfettiView.ConfettiType.triangle
, SwiftConfettiView.ConfettiType.star
]
confettiView.type = confettiList.randomElement()!
It's working properly!
is this approach is correct or wrong?
Here your enum is associated type. So if case of type image you have to provide a image as parameter. I have considered a default image.
extension ConfettiType: CaseIterable {
static var allCases: [ConfettiType] {
let img: UIImage = UIImage(named: "default_image")! // change as your expectation
return [.confetti, .triangle, .star, .diamond, .image(img)]
}
}
let randomEnum = ConfettiType.allCases.randomElement()
Otherwise if your image type would be something like this image(UIImage?) then we can put nil as default value. In that case it would be more convenient.
#Alexander identified your issue in the comments: Your issue is that you need to conform to CaseIterable to get access to allCases, and that only works if your enum doesn't have associated values (because there's no way to enumerate all possible UIImages).
Your workaround is fine, and it can be made nicer by taking advantage of Swift's type inference.
You might add a randomChoice(from:) function to your enum class that lets the caller specify which items to choose from. Because of type inference, you can specify the cases without fully qualifying them (eg. .confetti and .triange is sufficient).
Here is a full example:
public class SwiftConfettiView: UIView {
public enum ConfettiType {
case confetti
case triangle
case star
case diamond
case image(UIImage)
static func randomChoice(from choices: ConfettiType...) -> ConfettiType {
return choices.randomElement()!
}
}
func test() {
for _ in 1...10 {
let choice = ConfettiType.randomChoice(from: .confetti, .triangle, .star, .diamond)
print(choice)
}
}
}
SwiftConfettiView().test()
triangle
triangle
diamond
diamond
triangle
star
confetti
confetti
star
triangle
Notes:
Alternatively, you could have randomChoice take [ConfettiType]. In that case, you need to decide how to handle an empty array (either return an optional so that you can return nil for an empty array, or provide a default value such as .confetti for that case).
return choices.randomElement() ?? .confetti
Taking an array gives you the ability to provide a default value, such as:
static func randomChoice(from choices: [ConfettiType] = [.confetti, .triangle, .star, .diamond]) -> ConfettiType {
return choices.randomElement() ?? .confetti
}
For example:
// take the default values
let choice = ConfettiType.randomChoice()
// specify specific choices
let choice = ConfettiType.randomChoice(from: [.star, .diamond])
If you always just want to choose from those 4 enum values, you could just have randomChoice take no arguments and just hard code the choice from those 4 values. In that case, randomChoice could be implemented as a computed property:
static var randomChoice: ConfettiType { return [.confetti, .triangle, .star, .diamond].randomElement()! }
and called like this:
let choice = ConfettiType.randomChoice
Related
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())
I have an enum that could generate Some object, as shown below. However, when I assign the badgeValue for the Some object, it doesn't store it.
class Some {
public var badgeValue : String = "default"
}
enum MyEnum: Int {
case x
var some: Some {
let some: Some
switch self {
case .x:
some = Some()
}
return some
}
}
let x = MyEnum(rawValue: 0)
x?.some.badgeValue = "New"
print("\(x?.some.badgeValue ?? "Nothing")") // This will print `default`
I'm expecting it to print New. Why is it print default instead? If I instantiate Some directly, then the badgeValue can be updated. What's so special about the enum store var that can't be
You have made your MyEnum var some a computed property. A computed property gets evaluated every time you read its value. Thus, every time you invoke x?.some, you get a new Some object.
As Rob pointed out, my initial suggestion for a way to fix this won't work. Enums can't have stored properites. I think you'll have to use an associated value on your enum case to do what you want.
Look on you property. It's computed. Every time you create a new instance of Some class.
var some: Some {
let some: Some
switch self {
case .x:
some = Some()
}
return some
}
Note: This is basically the same question as another one I've posted on Stackoverflow yesterday. However, I figured that I used a poor example in that question that didn't quite boil it down to the essence of what I had in mind. As all replies to that original post refer to that first question I thought it might be a better idea to put the new example in a separate question — no duplication intended.
Model Game Characters That Can Move
Let's define an enum of directions for use in a simple game:
enum Direction {
case up
case down
case left
case right
}
Now in the game I need two kinds of characters:
A HorizontalMover that can only move left and right. ← →
A VerticalMover that can only move up and down. ↑ ↓
They can both move so they both implement the
protocol Movable {
func move(direction: Direction)
}
So let's define the two structs:
struct HorizontalMover: Movable {
func move(direction: Direction)
let allowedDirections: [Direction] = [.left, .right]
}
struct VerticalMover: Movable {
func move(direction: Direction)
let allowedDirections: [Direction] = [.up, .down]
}
The Problem
... with this approach is that I can still pass disallowed values to the move() function, e.g. the following call would be valid:
let horizontalMover = HorizontalMover()
horizontalMover.move(up) // ⚡️
Of course I can check inside the move() funtion whether the passed direction is allowed for this Mover type and throw an error otherwise. But as I do have the information which cases are allowed at compile time I also want the check to happen at compile time.
So what I really want is this:
struct HorizontalMover: Movable {
func move(direction: HorizontalDirection)
}
struct VerticalMover: Movable {
func move(direction: VerticalDirection)
}
where HorizontalDirection and VerticalDirection are subset-enums of the Direction enum.
It doesn't make much sense to just define the two direction types independently like this, without any common "ancestor":
enum HorizontalDirection {
case left
case right
}
enum VerticalDirection {
case up
case down
}
because then I'd have to redefine the same cases over and over again which are semantically the same for each enum that represents directions. E.g. if I add another character that can move in any direction, I'd have to implement the general direction enum as well (as shown above). Then I'd have a left case in the HorizontalDirection enum and a left case in the general Direction enum that don't know about each other which is not only ugly but becomes a real problem when assigning and making use of raw values that I would have to reassign in each enumeration.
So is there a way out of this?
Can I define an enum as a subset of the cases of another enum like this?
enum HorizontalDirection: Direction {
allowedCases:
.left
.right
}
No. This is currently not possible with Swift enums.
The solutions I can think of:
Use protocols as I outlined in your other question
Fallback to a runtime check
Here's a possible compile-time solution:
enum Direction: ExpressibleByStringLiteral {
case unknown
case left
case right
case up
case down
public init(stringLiteral value: String) {
switch value {
case "left": self = .left
case "right": self = .right
case "up": self = .up
case "down": self = .down
default: self = .unknown
}
}
public init(extendedGraphemeClusterLiteral value: String) {
self.init(stringLiteral: value)
}
public init(unicodeScalarLiteral value: String) {
self.init(stringLiteral: value)
}
}
enum HorizontalDirection: Direction {
case left = "left"
case right = "right"
}
enum VerticalDirection: Direction {
case up = "up"
case down = "down"
}
Now we can define a move method like this:
func move(_ allowedDirection: HorizontalDirection) {
let direction = allowedDirection.rawValue
print(direction)
}
The drawback of this approach is that you need to make sure that the strings in your individual enums are correct, which is potentially error-prone. I have intentionally used ExpressibleByStringLiteral for this reason, rather than ExpressibleByIntegerLiteral because it is more readable and maintainable in my opinion - you may disagree.
You also need to define all 3 of those initializers, which is perhaps a bit unwieldy, but you would avoid that if you used ExpressibleByIntegerLiteral instead.
I'm aware that you're trading compile-time safety in one place for another, but I suppose this kind of solution might be preferable in some situations.
To make sure that you don't have any mistyped strings, you could also add a simple unit test, like this:
XCTAssertEqual(Direction.left, HorizontalDirection.left.rawValue)
XCTAssertEqual(Direction.right, HorizontalDirection.right.rawValue)
XCTAssertEqual(Direction.up, VerticalDirection.up.rawValue)
XCTAssertEqual(Direction.down, VerticalDirection.down.rawValue)
You probably solved your issue, but to anyone looking for an answer, for some time now (not sure when Apple introduced it) you can use associated values inside enum cases to model these kinds of states.
enum VerticalDirection {
case up
case down
}
enum HorizontalDirection {
case left
case right
}
enum Direction {
case vertical(direction: VerticalDirection)
case horizontal(direction: HorizontalDirection)
}
So you can use a method like this:
func move(_ direction: Direction) {
print(direction)
}
move(.horizontal(.left))
And if you conform to Equatable protocol:
extension Direction: Equatable {
static func ==(lhs: Direction, rhs: Direction) -> Bool {
switch (lhs, rhs) {
case (.vertical(let lVertical), .vertical(let rVertical)):
switch (lVertical, rVertical) {
case (.up, .up):
return true
case (.down, .down):
return true
default:
return false
}
case (.horizontal(let lHorizontal), .horizontal(let rHorizontal)):
switch (lHorizontal, rHorizontal) {
case (.left, .left):
return true
case (.right, .right):
return true
default:
return false
}
default:
return false
}
}
}
you can do something like this:
func isMovingLeft(direction: Direction) -> Bool {
return direction == .horizontal(.left)
}
let characterDirection: Direction = .horizontal(.left)
isMovingLeft(direction: characterDirection) // true
isMovingLeft(direction: characterDirection) // false
Use Swift protocol OptionSet
struct Direction: OptionSet {
let rawValue: int
static let up = Direction(rawValue: 1<<0)
static let right = Direction(rawValue: 1<<1)
static let down = Direction(rawValue: 1<<2)
static let left = Direction(rawValue: 1<<3)
static let horizontal = [.left, .right]
static let vertical = [.up, down]
}
In order to write generic code for an NSValueTransformer, I need to be able to check that an enum is of type String for example. Ie.:
enum TestEnum: String {
case Tall
case Short
}
I am expecially interested in a test that can be used with the guard statement. Something allong the line of:
guard let e = myEnum as <string based enum test> else {
// throw an error
}
Please note that not all enums have raw values. For eample:
enum Test2Enum {
case Fat
case Slim
}
Hence a check on the raw value type can not be used alone for this purpose.
EDIT
After some further investigation it has become clear that NSValueTransformer can not be used to transform Swift enums. Please see my second comment from matt's answer.
First, it's your enums, so you can't not know what type they are. Second, you're not going to receive an enum type, but an enum instance. Third, even if you insist on pretending not to know what type this enum is, it's easy to make a function that can be called only with an enum that has a raw value and check what type that raw value is:
enum E1 {
case One
case Two
}
enum E2 : String {
case One
case Two
}
enum E3 : Int {
case One
case Two
}
func f<T:RawRepresentable>(t:T) -> Bool {
return T.RawValue.self == String.self
}
f(E3.One) // false
f(E2.One) // true
f(E1.One) // compile error
Generics to the rescue :
func enumRawType<T>(of v:T)-> Any?
{ return nil }
func enumRawType<T:RawRepresentable>(of v:T)-> Any?
{
return type(of:v.rawValue)
}
enumRawType(of:E1.One) // nil
enumRawType(of:E2.One) // String.Type
enumRawType(of:E3.One) // Int.Type
wondering why Swift switch statement does not allow to instantiate classes like in other languages and how to solve this. Would be glad if anyone can help out on this.
In the example i have created a simple Vehicle class and trying to instantiate its sub classes via switch depending on classSetter value. However the final line of print statement cannot print name property of any of the classes if it is instantiated within switch (or seems to any other kind of conditional) statement.
import UIKit
class Vehicle {
var name: String {return ""}
}
class Car: Vehicle {
override var name: String {return "Car"}
}
class Bike: Vehicle {
override var name: String {return "Bike"}
}
var classSetter:Int = 1
switch classSetter {
case 1:
println("Initializing Car")
var vehicle = Car()
case 2:
println("Initialized Bike")
let vehicle = Bike()
default:
println("just defaulted")
}
println("Name property from initialization is \(vehicle.name)")
Your two vehicles are being declared within the switch’s { }, so they only exist in that block (that is their “scope”). They don’t exist outside it, so you can’t refer to them there, hence the error.
The default solution to this (that other answers are giving) is to declare the vehicle as a var outside the switch, but here’s an alternative: wrap the switch in a closure expression and return a value from it. Why do this? Because then you can use let and not var to declare vehicle:
let vehicle: Vehicle = { // start of a closure expression that returns a Vehicle
switch classSetter {
case 1:
println("Initializing Car")
return Car()
case 2:
println("Initialized Bike")
return Bike()
default:
println("just defaulted")
return Vehicle()
}
}() // note the trailing () because you need to _call_ the expression
println("Name property from initialization is \(vehicle.name)")
It would be nice if if and switch were expressions (i.e. evaluated to a result) so you didn’t need this closure, but for now it’s a reasonable workaround.
Note, several of the answers here that use the var approach suggest making vehicle an Optional value (i.e. Vehicle?). This is not necessary – so long as the code is guaranteed to assign vehicle a value before it is used (and the compiler will check this for you), it doesn’t have to be optional. But I still think the closure expression version is a better way.
By the way, you might want to consider using a protocol for Vehicle instead of a base class, since that way you don’t have to give Vehicle a default but invalid implementation for name:
protocol Vehicle {
var name: String { get }
}
// one of the benefits of this is you could
// make Car and Bike structs if desired
struct Car: Vehicle {
var name: String {return "Car"}
}
struct Bike: Vehicle {
var name: String {return "Bike"}
}
Though this would mean you couldn’t have a default return from the switch statement of a Vehicle(). But chances are that would be bad anyway – an optional Vehicle? with nil representing failure might be a better option:
let vehicle: Vehicle? = {
switch classSetter {
case 1:
println("Initializing Car")
return Car()
case 2:
println("Initialized Bike")
return Bike()
default:
println("no valid value")
return nil
}
}()
// btw since vehicle is a Vehicle? you need to unwrap the optional somehow,
// one way is with optional chaining (will print (nil) here)
println("Name property from initialization is \(vehicle?.name)")
If you didn’t want this to be a possibility at all, you could consider making the indicator for different kinds of vehicles an enum so it could only be one of a valid set of vehicles:
enum VehicleKind {
case Bike, Car
}
let classSetter: VehicleKind = .Car
let vehicle: Vehicle = {
switch classSetter {
case .Car:
println("Initializing Car")
return Car()
case .Bike:
println("Initialized Bike")
return Bike()
// no need for a default clause now, since
// those are the only two possibilities
}
}()
Your assumption is incorrect.
The scope of the variable vehicle is inside the switch. You are then trying to access it outside the switch.
You need to create the variable outside the switch. You can still instantiate it inside.
var vehicle: Vehicle?
Switch... {
Case
vehicle = Car()
}
println(vehicle)
Writing on my phone so couldn't give full proper code but this will give you the idea.
What you're doing doesn't work in other languages either. You need to declare the vehicle variable before you enter the switch so that it's in the same scope as your println. After that, you an assign it whatever you need to inside the switch.