I am trying to extract programmatically the names of the enum cases of UIBlurEffect.Style which have a rawValue of Int not String. The names in an array would be ["extraLight","light","dark","regular",...]
Doing print(UIBlurEffect.Style.systemChromeMaterialLight) doesn't print systemChromeMaterialLight instead it prints UIBlurEffectStyle
I tried also using Mirror but this yields a name of __C.UIBlurEffectStyle
Example code of what I am trying to do:
let myStyles : [UIBlurEffect.Style] = [.light, .dark, .regular, .prominent]
for style in myStyles {
print(style) // does not work, produces "UIBlurEffectStyle"
myFunction(styleName: String(reflecting: style)) // does not work, produces "UIBlurEffectStyle"
myFunction(styleName: String(describing: style)) // does not work, produces "UIBlurEffectStyle"
myFunction(styleName: "\(style)") // does not work, produces "UIBlurEffectStyle"
}
I am using Swift 5, iOS 14, and Xcode 12.3
For reference, the enum is defined as follows by Apple:
extension UIBlurEffect {
#available(iOS 8.0, *)
public enum Style : Int {
case extraLight = 0
case light = 1
case dark = 2
#available(iOS 10.0, *)
case regular = 4
...
Are you doing something dynamic that is related to the name on your app so you can show the correct one based on the selection?
If you are, I suggest you create your own local String enum and then add a var or function to get the blur from it instead of trying to reverse this.
But if you really, really need this for some other reason, there is a workaround, which I do not recommend, but it's here in case you want to test it out:
let blurStyle = String(describing: UIBlurEffect(style: .systemMaterialDark))
let style = blurStyle.components(separatedBy: "style=").last?.replacingOccurrences(of: "UIBlurEffectStyle", with: "")
print(style) // SystemMaterialDark
Creating your own app Style enum:
enum AppBlurStyle: String {
case extraLight
case dark
case light
case regular
var blurEffectStyle: UIBlurEffect.Style {
switch self {
case .extraLight: UIBlurEffect.Style.extraLight
case .dark: UIBlurEffect.Style.dark
case .light: UIBlurEffect.Style.light
case .regular: UIBlurEffect.Style.regular
}
}
var blurEffect: UIBlurEffect {
switch self {
case .extraLight: UIBlurEffect(style: .extraLight)
case .dark: UIBlurEffect(style:.dark)
case .light: UIBlurEffect(style:.light)
case .regular: UIBlurEffect(style:.regular)
}
}
}
Or you can even just extend UIBlurEffect.Style and add a name property, mapping them individually:
extension UIBlurEffect.Style {
var name: String {
switch self {
case .extraLight: "extraLight"
case .dark: "dark"
case .light: "light"
case .regular: "regular"
...
}
}
}
Related
When I have a situation where I already know enum case statement I want to get the associated value of, is there a cleaner way than using a switch statement to pluck out the associated value?
To have to come up with a switch statement, provide multiple cases, or a default case just to extract the associated value is gaudy.
enum CircularReasoning {
case justPi(pi: Double)
case pizzaPie(howMany: Int)
}
var piInTheSky : Double
let whatLogic = CircularReasoning(pi: 3.1415926)
β¬οΈ πΈπ’π―π΅ π΅π° π’π·π°πͺπ₯ β¬οΈ
switch whatLogic {
case .justPi(let pi):
piInTheSky = pi!
default:
break
}
You can use if case .<enum_case>(let value) as in TylerP's example,
or if case let .<enum_case>(value):
enum Foo {
case anInt(Int)
case aFloat(Float)
}
let aFoo: Foo = .anInt(9)
// Example of `if case .<enum_case)(let value)` syntax:
if case .anInt(let aValue) = aFoo {
print("aFoo = anInt(\(aValue))")
// Example of `if case let .enum_case(value)` syntax:
} else if case let .aFloat(aValue) = aFoo {
print("aFoo = aFloat(\(aValue))")
}
Both work. I'm not sure why the language includes both variants.
If you only care about one enum type, then either if syntax makes sense to me. If you are dealing with more than one possible enum value then the switch version seems cleaner.
Here's an adaptation of #DuncanC's excellent upvoted and accepted answer, as applied to a fictitious version of my real-world use case.
It illustrates a possible way to use his answer to reduce the space required to extract associated values, especially if one has a lot of one-off cases...
Note: Not implying this is appropriate or professional swift styling; it's clearly idiosyncratic, yet compact. (I usually don't compress things into one liners like this, unless they get really repetitive/redundant & produce lot of pointless vertical bloat).
enum RealmKey { case realmOfRealms, anyOldRealm, someOtherRealm }
.
.
.
enum SymbolToken {
case realm (key: RealmKey?)
case space (key: SpaceKey?)
case area (key: AreaKey?)
case region (key: RegionKey?)
case preserve (key: PeserveKey?)
case openParen
case closeParen
case logicalAnd
case logicalOr
case logicalNot
var realmKey : RealmKey? { if case .realm (let key) = self { return key } else { return nil } }
var spaceKey : SpaceKey? { if case .space (let key) = self { return key } else { return nil } }
var areaKey : AreaKey? { if case .area (let key) = self { return key } else { return nil } }
var regionKey : RegionKey? { if case .region (let key) = self { return key } else { return nil } }
var preserveKey : PreserveKey? { if case .preserve (let key) = self { return key } else { return nil } }
}
let realm = SymbolToken.realm(.realmOfRealms)
let realmKey = realm.realmKey
This is what I mean.
enum Device {
case iPhone_13Pro, iPhone_12Pro
case iPadPro_129
case iPadPro_11
}
// Hypothetical Scenario
enum Device_Group {
case iPhones
case iPads
}
Is there any way to do as above to represent a certain group of cases like this (it can be another enum OR a different case within the same enum - so that I could do as below?
// DO THIS
switch device {
case iPhones:
print("These are iPhones")
case iPads:
print("These are iPads")
}
// INSTEAD OF THIS
switch device {
case .iPhone_13Pro, .iPhone_12Pro:
print("These are iPhones")
case .iPadPro_129, .iPadPro_11:
print("These are iPads")
}
I dont know if this might be a weird question, but I find that grouping multiple cases has a certain benefit while using a switch statement. Would appreciate any advise on this. Thanks in advance.
You can used nested Enum and a case with parameter
enum Devices {
case phone(iPhone)
case tablet(iPad)
enum iPhone {
case phone7
case phoneX
}
enum iPad {
case mini
case pro
}
}
let randomDevice = Devices.phone(.phone7)
switch randomDevice {
case .phone:
print("Its a phone")
default:
break
}
// prints "Its a phone"
You can define two different enums for iPhones and iPads and then use them as Associated Values for Devices's types.
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html
enum Ipad {
case iPadAir
case iPadPro
}
enum Iphone {
case iPhone12
case iPhone13
}
enum Device {
case iPad(model: Ipad)
case iPhone(model: Iphone)
}
func findDeviceType(device: Device) {
switch device {
case .iPad:
print("iPad")
case .iPhone:
print("iPhone")
}
}
findDeviceType(device: Device.iPad(model: .iPadAir)) // iPad
You can look at OptionSet as well and it's flexible approach to group and serialize your items because they have unique values. For instance:
struct Device: OptionSet {
let rawValue: Int
static let iPhone_13Pro = Self(rawValue: 1 << 0)
static let iPhone_12Pro = Self(rawValue: 1 << 1)
static let iPadPro_129 = Self(rawValue: 1 << 2)
static let iPadPro_11 = Self(rawValue: 1 << 3)
static let iPhone: Self = [.iPhone_13Pro, .iPhone_12Pro]
static let iPad: Self = [.iPadPro_129, .iPadPro_11]
static let all: Self = [.iPhone, .iPad]
}
let device = Device.iPhone_13Pro
if Device.iPhone.contains(device) {
print("It's iPhone")
}
Consider:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
How can I access, say, lefEdge's associated value, directly, without using a switch statement?
let noIdeaHowTo = leftEdge.associatedValue + 0.5
This doesn't even compile!
I had a look at these SO questions but none of the answers seem to address this issue.
The noIdeaHowTo non compiling line above should really be that one-liner, but because the associated value can be any type, I fail to even see how user code could write even a "generic" get or associatedValue method in le enum itself.
I ended up with this, but it is gross, and needs me to revisit the code each time I add/modify a case ...
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
var associatedValue: CGFloat {
get {
switch self {
case .Horizontal(let value): return value
case .Vertical(let value): return value
}
}
}
}
Any pointer anyone?
As others have pointed out, this is now kind of possible in Swift 2:
import CoreGraphics
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let min = Line.Horizontal(0.0)
let mid = Line.Horizontal(0.5)
let max = Line.Horizontal(1.0)
func doToLine(line: Line) -> CGFloat? {
if case .Horizontal(let value) = line {
return value
}
return .None
}
doToLine(min) // prints 0
doToLine(mid) // prints 0.5
doToLine(max) // prints 1
You can use a guard statement to access the associated value, like this.
enum Line {
case Horizontal(Float)
case Vertical(Float)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
guard case .Horizontal(let leftEdgeValue) = leftEdge else { fatalError() }
print(leftEdgeValue)
I think you may be trying to use enum for something it was not intended for. The way to access the associated values is indeed through switch as you've done, the idea being that the switch always handles each possible member case of the enum.
Different members of the enum can have different associated values (e.g., you could have Diagonal(CGFloat, CGFloat) and Text(String) in your enum Line), so you must always confirm which case you're dealing with before you can access the associated value. For instance, consider:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
case Diagonal(CGFloat, CGFloat)
case Text(String)
}
var myLine = someFunctionReturningEnumLine()
let value = myLine.associatedValue // <- type?
How could you presume to get the associated value from myLine when you might be dealing with CGFloat, String, or two CGFloats? This is why you need the switch to first discover which case you have.
In your particular case it sounds like you might be better off with a class or struct for Line, which might then store the CGFloat and also have an enum property for Vertical and Horizontal. Or you could model Vertical and Horizontal as separate classes, with Line being a protocol (for example).
Why this is not possible is already answered, so this is only an advice. Why don't you implement it like this. I mean enums and structs are both value types.
enum Orientation {
case Horizontal
case Vertical
}
struct Line {
let orientation : Orientation
let value : CGFloat
init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
}
let x = Line(.Horizontal, 20.0)
// if you want that syntax 'Line.Horizontal(0.0)' you could fake it like this
struct Line {
let orientation : Orientation
let value : CGFloat
private init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
static func Horizontal(value: CGFloat) -> Line { return Line(.Horizontal, value) }
static func Vertical(value: CGFloat) -> Line { return Line(.Vertical, value) }
}
let y = Line.Horizontal(20.0)
You can get the associated value without using a switch using the if case let syntax:
enum Messages {
case ping
case say(message: String)
}
let val = Messages.say(message: "Hello")
if case let .say(msg) = val {
print(msg)
}
The block inside the if case let will run if the enum value is .say, and will have the associated value in scope as the variable name you use in the if statement.
With Swift 2 it's possible to get the associated value (read only) using reflection.
To make that easier just add the code below to your project and extend your enum with the EVAssociated protocol.
public protocol EVAssociated {
}
public extension EVAssociated {
public var associated: (label:String, value: Any?) {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return (associated.label!, associated.value)
}
print("WARNING: Enum option of \(self) does not have an associated value")
return ("\(self)", nil)
}
}
}
Then you can access the .asociated value with code like this:
class EVReflectionTests: XCTestCase {
func testEnumAssociatedValues() {
let parameters:[EVAssociated] = [usersParameters.number(19),
usersParameters.authors_only(false)]
let y = WordPressRequestConvertible.MeLikes("XX", Dictionary(associated: parameters))
// Now just extract the label and associated values from this enum
let label = y.associated.label
let (token, param) = y.associated.value as! (String, [String:Any]?)
XCTAssertEqual("MeLikes", label, "The label of the enum should be MeLikes")
XCTAssertEqual("XX", token, "The token associated value of the enum should be XX")
XCTAssertEqual(19, param?["number"] as? Int, "The number param associated value of the enum should be 19")
XCTAssertEqual(false, param?["authors_only"] as? Bool, "The authors_only param associated value of the enum should be false")
print("\(label) = {token = \(token), params = \(param)")
}
}
// See http://github.com/evermeer/EVWordPressAPI for a full functional usage of associated values
enum WordPressRequestConvertible: EVAssociated {
case Users(String, Dictionary<String, Any>?)
case Suggest(String, Dictionary<String, Any>?)
case Me(String, Dictionary<String, Any>?)
case MeLikes(String, Dictionary<String, Any>?)
case Shortcodes(String, Dictionary<String, Any>?)
}
public enum usersParameters: EVAssociated {
case context(String)
case http_envelope(Bool)
case pretty(Bool)
case meta(String)
case fields(String)
case callback(String)
case number(Int)
case offset(Int)
case order(String)
case order_by(String)
case authors_only(Bool)
case type(String)
}
The code above is from my project https://github.com/evermeer/EVReflection
https://github.com/evermeer/EVReflection
After reading How to test equality of Swift enums with associated values, I implemented the following enum:
enum CardRank {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
func ==(a: CardRank, b: CardRank) -> Bool {
switch (a, b) {
case (.Number(let a), .Number(let b)) where a == b: return true
case (.Jack, .Jack): return true
case (.Queen, .Queen): return true
case (.King, .King): return true
case (.Ace, .Ace): return true
default: return false
}
}
The following code works:
let card: CardRank = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack!")
} else if card == CardRank.Number(2) {
print("A two cannot be played at this time.")
}
However, this doesn't compile:
let number = CardRank.Number(5)
if number == CardRank.Number {
print("You must play a face card!")
}
... and it gives the following error message:
Binary operator '==' cannot be applied to operands of type 'CardRank' and '(Int) -> CardRank'
I'm assuming this is because it's expecting a full type and CardRank.Number does not specify an entire type, whereas CardRank.Number(2) did. However, in this case, I want it to match any number; not just a specific one.
Obviously I can use a switch statement, but the whole point of implementing the == operator was to avoid this verbose solution:
switch number {
case .Number:
print("You must play a face card!")
default:
break
}
Is there any way to compare an enum with associated values while ignoring its associated value?
Note: I realize that I could change the case in the == method to case (.Number, .Number): return true, but, although it would return true correctly, my comparison would still look like its being compared to a specific number (number == CardRank.Number(2); where 2 is a dummy value) rather than any number (number == CardRank.Number).
Edit: As Etan points out, you can omit the (_) wildcard match to use this more cleanly:
let number = CardRank.Number(5)
if case .Number = number {
// Is a number
} else {
// Something else
}
Unfortunately, I don't believe that there's an easier way than your switch approach in Swift 1.2.
In Swift 2, however, you can use the new if-case pattern match:
let number = CardRank.Number(5)
if case .Number(_) = number {
// Is a number
} else {
// Something else
}
If you're looking to avoid verbosity, you might consider adding an isNumber computed property to your enum that implements your switch statement.
Unfortunately in Swift 1.x there isn't another way so you have to use switch which isn't as elegant as Swift 2's version where you can use if case:
if case .Number = number {
//ignore the value
}
if case .Number(let x) = number {
//without ignoring
}
In Swift 4.2 Equatable will be synthesized if all your associated values conform to Equatable. All you need to do is add Equatable.
enum CardRank: Equatable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
https://developer.apple.com/documentation/swift/equatable?changes=_3
What I usually do to compare if two enum cases "match" no matter their associated value is:
I have a protocol Matchable:
protocol Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool
}
Then I make enums conform to it:
extension CardRank: Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case
(.number, .number),
(.jack, .jack),
(.queen, .queen),
(.king, .king),
(.ace, .ace):
return true
default:
return false
}
}
}
let card1: CardRank = .number(1)
let card2: CardRank = .number(2)
let card3: CardRank = .jack
print(card1 ~= card2) // true
print(card1 ~= card3) // false
Here's a simpler approach:
enum CardRank {
case Two
case Three
case Four
case Five
case Six
case Seven
case Eight
case Nine
case Ten
case Jack
case Queen
case King
case Ace
var isFaceCard: Bool {
return (self == Jack) || (self == Queen) || (self == King)
}
}
There's no need to overload the == operator, and checking for card type does not require confusing syntax:
let card = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack")
} else if !card.isFaceCard {
print("You must play a face card!")
}
I didn't want to conform Equatable (it didn't help me either) and I wanted to filter for other cases than a specific one, so instead of simply writing card != .Number I had to write the following. (I adjusted my code to this question.)
enum CardRank {
...
var isNumber: Bool {
if case .Number = self { return true }
return false
}
}
So I can write not a number in a complex condition:
if something && !card.isNumber { ... }
I wish I could just write card != .Number, but the compiler was always complaining with Type of expression is ambiguous without more context. Maybe in an upcoming swift version!
You don't need func == or Equatable. Just use an enumeration case pattern.
let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }
extension CardRank {
func isSameCaseAs(_ other: CardRank) -> Bool {
switch (self, other) {
case (.Number, .Number),
(.Jack, .Jack),
(.Queen, .Queen),
(.King, .King),
(.Ace, .Ace):
return true
default:
return false
}
}
}
let number = CardRank.Number(1)
let otherNumber = CardRank.Number(2)
number.isSameCaseAs(otherNumber) // true
Just create an extension and ignore the associated types.
From Swift 5.3, you can use the Comparable Enums feature:
enum CardRank: Comparable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
let cards: [CardRank] = [
.Queen, .Number(8), .Ace, .Number(3), .King
]
print(cards.sorted())
// [.Number(3), .Number(8), .Queen, .King, .Ace]
Given the following enum:
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
}
How to generate a random variable of type GameLevel in Swift?
I updated your enum as per Apple standards (Capital letter to start a Type, and no abbreviations.
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
}
First, how to create a constant or variable with a value for level.
let level = GameLevel.Level(1)
Next, for a random value to level use arc4random_uniform:
let maxGameLevel: UInt32 = 10
let randomGameLevel: Int = Int(arc4random_uniform(maxGameLevel))
let level = GameLevel.Level(randomGameLevel)
Of course, this can be put into a function:
func RandomGameLevel() -> GameLevel {
let maxGameLevel: UInt32 = 10
return .Level(Int(arc4random_uniform(maxGameLevel)))
}
let level = RandomGameLevel()
Finally, here is how you would use it in a case statement:
switch level {
case .Level(let levelValue):
println("Level \(levelValue)")
case .TutorialLevel:
println("Tutorial Level")
case .BossLevel:
println("Boss Level")
}
Update
OK, it's not too hard to include the other values. I'll also put all of this into GameLevel to package it up better.
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
static func Random() -> GameLevel {
let maxGameLevel: UInt32 = 10 /* levels will be 0 through 9 */
let otherGameLevels: UInt32 = 2 /* TutorialLevel and BossLevel */
let levelValue = Int(arc4random_uniform(maxGameLevel + otherGameLevels))
switch levelValue {
case 10: return .TutorialLevel
case 11: return .BossLevel
default: return .Level(levelValue)
}
}
}
Then
let level = GameLevel.Random()
Not the cleanest, but it's a start.
enum GameLevel: CaseIterable {
case Level(Int)
case TutorialLevel, BossLevel
}
let level:GameLevel = GameLevel.allCases.randomElement()!
Why would you need it that way? :(
Assign numbers to your start and final levels and implement a function, which will return random in that range as Lvl(int)