Swift Enum associated values conforming to single protocol - swift

How can I get associated value of current enum's case as Refreshable not using exhaustive switch?
In condition I only need it retrieved as protocol, which is used for each case associated type.
class Type1: Refreshable {}
class Type2: Refreshable {}
class Type3: Refreshable {}
protocol Refreshable {
func refresh()
}
enum ContentType {
case content1(Type1 & Refreshable)
case content2(Type2 & Refreshable)
case content3(Type3 & Refreshable)
func refreshMe() {
//self.anyValue.refresh() //Want simple solution to get to the refresh() method not knowing actual current state
}
}

I've found the solution in case anyone will need this too.
enum ContentType {
case content1(Type1 & Refreshable)
case content2(Type2 & Refreshable)
case content3(someLabel: Type3 & Refreshable)
func refreshMe() {
let caseReflection = Mirror(reflecting: self).children.first!.value
(caseReflection as? Refreshable)?.refresh() //If associated type doesn't have label
let refreshable = Mirror(reflecting: caseReflection).children.first?.value as? Refreshable
refreshable?.refresh() //If associated type has label
}
}

Related

SOLVED - Swift Enum - Casting Nested Enums to String Enum to allow .rawValue

SOLVED
Thank you #New Dev and #Joakim Danielson for your help. I used #Joakim Danielson's answer to improve my code.
I have an extension method to assign accessibilityIdentifiers to views based on a given String Enum. I updated the method to directly accept String Enum Cases as a parameter, thus COMPLETELY eliminating the need for the AccessibilityId enum class as shown below, awesome!
Changes
Before:
.accessibility(identifier: .home(.clickButton))
// Simplified for StackOverflow.
// Imagine 20 more cases..
enum AccessibilityId {
case home(HomeId)
var rawValue: String {
switch self {
case .home(let id):
return id.rawValue
}
}
}
extension View {
func accessibility(identifier: AccessibilityId) -> ModifiedContent<Self, AccessibilityAttachmentModifier> {
self.accessibility(identifier: identifier.rawValue)
}
}
After:
.accessibility(identifier: HomeId.clickButton)
extension View {
func accessibility<T: RawRepresentable>(identifier: T) -> ModifiedContent<Self, AccessibilityAttachmentModifier> where T.RawValue == String {
self.accessibility(identifier: identifier.rawValue)
}
}
---------------------------------------------------------------
Original Question
What I have
enum Item {
case animal(AnimalId)
case vehicle(VehicleId)
case food(FoodId)
var rawValue: String {
switch self {
case .animal(let id):
return id.rawValue
case .vehicle(let id):
return id.rawValue
case .food(let id):
return id.rawValue
}
}
}
enum AnimalId: String {
case cat
case dog
}
// etc.
// Imagine more cases and more enums.
What I want
enum Item {
case animal(AnimalId)
case vehicle(VehicleId)
case food(FoodId)
var rawValue: String {
switch self {
case self as StringEnum:
return id.rawValue
default:
return ""
}
}
}
Usage
func test() {
foo(.animal(.cat))
foo(.vehicle(.plane))
foo(.food(.tacos))
}
func foo(_ item: Item) {
print(item.rawValue)
}
I am happy with the usage, but I'd like to reduce the amount of duplicate cases in the given switch statement. Notice how they all have return id.rawValue. The above is just an example, in reality I have around 30 cases.
My Question
Is there a way for me to catch all Nested String Enums in a single switch or let case to reduce the duplicate code I have to write without losing the intended usage?
Thank you for your efforts, I hope to find an improvement for my code!
Here is a solution that is not based on Item being an enum but instead a generic struct
struct Item<T: RawRepresentable> where T.RawValue == String {
let thing: T
var rawValue: String {
thing.rawValue
}
}
With this solution you don't need to change your other enums.
Example
let item1 = Item(thing: AnimalId.cat)
let item2 = Item(thing: VehicleId.car)
print(item1.rawValue, item2.rawValue)
outputs
cat car
You need something common between all these associated values, like a conformance to a shared protocol, e.g. protocol RawStringValue:
protocol RawStringValue {
var rawValue: String { get }
}
// String enums already conform without any extra implementation
extension AnimalId: RawStringValue {}
extension VehicleId: RawStringValue {}
extension FoodId: RawStringValue {}
Then you could create a switch self inside like so:
var rawValue: String {
switch self {
case .animal (let id as RawStringValue),
.vehicle (let id as RawStringValue),
.food (let id as RawStringValue):
return id.rawValue
}
}
That being said, enum with associated values isn't the most convenient type to work with, so be sure that it's the right choice.

Expression pattern of type 'Roll' cannot match values of type 'Requests.RawValue' (aka 'Roll')

I am trying to create an enum that returns a struct and I am not succeeding . I have checked a few questions similar to mine e.g here, and this here and they do not address the issue I have. This is my code:
import Foundation
struct Roll {
let times: String
init(with times: String) {
self.times = times
}
}
fileprivate enum Requests {
case poker
case cards
case slots
}
extension Requests: RawRepresentable {
typealias RawValue = Roll
init?(rawValue: RawValue) {
switch rawValue {
case Roll(with: "once"): self = .poker
case Roll(with: "twice"): self = .cards
case Roll(with: "a couple of times"): self = .slots
default: return nil
}
}
var rawValue: RawValue {
switch self {
case .poker: return Roll(with: "once")
case .cards: return Roll(with: "twice")
case .slots: return Roll(with: "a couple of times")
}
}
}
Then I would like to use it like : Requests.cards.rawValue
That's because of your structure Roll doesn't conform Equatable protocol and you're trying to make a comparison of it, just change it to
struct Roll: Equatable {
let times: String
init(with times: String) {
self.times = times
}
}

Swift Struct Property with Generic Enum

I am currently creating settings page. I am trying to dynamically assign an Enum into my struct property.
What I want to achieve is that whenever Settings struct is being called, it will automatically append all respective settings together. Below are the enums
protocol BaseSettings {}
enum SettingsCategory: String, CaseIterable {
case sales = "Sales"
case payment = "Payment"
}
struct SettingsSection {
var settingsNameArray: [BaseSettings]
var settingsCategoryName: SettingsCategory
init(settingsCategory: SettingsCategory) {
self.settingsCategoryName = settingsCategory
switch settingsCategory {
case .sales:
self.settingsNameArray = SalesSettings.allCases
case .payment:
self.settingsNameArray = PaymentSettings.allCases
}
}
}
struct Settings {
var sections = [SettingsSection]()
init() {
for eachSettingCategory in SettingsCategory.allCases {
self.sections.append(SettingsSection(settingsCategory: eachSettingCategory))
}
}
}
enum SalesSettings: String, BaseSettings, CaseIterable {
case testSettings = "Test Sales Settings"
}
enum PaymentSettings: String, BaseSettings, CaseIterable {
case testSettings = "Test Payment Settings"
}
Above codes are working fine. The settings are grouped by section. However, when I want to populate the String raw value of each enum case, I am facing a problem. I couldn't get the rawValue because of the Protocol BaseSettings is not an enum
let settingsDataSource = SettingsConstant.Settings()
let settingsName = settingsDataSource.sections[indexPath.section].settingsNameArray[indexPath.row]
cell.textLabel?.text = "\(settingsName)"
Instead of displaying the rawValue of each enum case, the tableView is displaying each of the enum case.
How can I fix this data structure? Can anyone guide me?
Thanks
Specify in the protocol declaration that you need a raw value!
protocol BaseSettings {
var rawValue: String { get }
}
Enums such as SalesSettings would automatically conform to this new requirement because it is exactly the same as RawRepresentable.rawValue when RawValue is String.

Enum Case with Object

How can one get the object inside an emun case? Is it possible without a switch statement?
enum ItemType {
case person(Person)
case dog(Dog)
case cat(Cat)
}
var items = [ItemType]()
var dog = items[index] // Would like the actual dog object
You need to check that you have the right case (since items[index] might be a Cat or a Person, instead).
let item = items[index]
if case .dog(let dog) = item {
// use `dog`
}
If you plan on accessing this a lot, you could add a computed property on ItemType.
extension ItemType {
var dog: Dog? {
switch self {
case .dog(let dog): return dog
default: return nil
}
}
}
Note that this is optional (since not every ItemType has a dog). But then you could say:
if let dog = items[index].dog { ... }

Is there some workaround to cast to a generic base class without knowing what the defined element type is?

I am trying to achieve a design where I can have a base class that has a generic property that I can change values on by conforming to a protocol.
protocol EnumProtocol {
static var startValue: Self { get }
func nextValue() -> Self
}
enum FooState: EnumProtocol {
case foo1, foo2
static var startValue: FooState { return .foo1 }
func nextValue() -> FooState {
switch self {
case .foo1:
return .foo2
case .foo2:
return .foo1
}
}
}
enum BarState: EnumProtocol {
case bar
static var startValue: BarState { return .bar }
func nextValue() -> BarState {
return .bar
}
}
class BaseClass<T: EnumProtocol> {
var state = T.startValue
}
class FooClass: BaseClass<FooState> {
}
class BarClass: BaseClass<BarState> {
}
Is it possible to end up with a solution similar to this where the element type is unknown and the value relies on the nextValue() method.
let foo = FooClass()
let bar = BarClass()
if let test = bar as? BaseClass {
test.state = test.state.nextValue()
}
This works but BarState will be unknown in my case and a lot of classes will be subclasses of BaseClass and have different state types.
let bar = BarClass()
if let test = bar as? BaseClass<BarState> {
test.state = test.state.nextValue()
}
This is a simplified example. In my case I will get a SKNode subclass that has a state property that is an enum with a nextvalue method that have defined rules to decide what the next value will be. I am trying to have a generic implementation of this that only relies on what is returned from the nextValue method. Is there a better pattern to achieve this?
This will not work for this exact scenario because EnumProtocol can not be used as concrete type since it has a Self type requirement, however, in order to achieve this type of behavior in other cases you can create a protocol that the base class conforms to and try to cast objects to that type when you are trying to determine if an object is some subclass of that type.
Consider the following example
class Bitcoin { }
class Ethereum { }
class Wallet<T> {
var usdValue: Double = 0
}
class BitcoinWallet: Wallet<Bitcoin> {
}
class EthereumWallet: Wallet<Ethereum> {
}
let bitcoinWallet = BitcoinWallet() as Any
if let wallet = bitcoinWallet as? Wallet {
print(wallet.usdValue)
}
This will not work, due to the same error that you are referring to:
error: generic parameter 'T' could not be inferred in cast to 'Wallet<_>'
However, if you add the following protocol
protocol WalletType {
var usdValue: Double { get set }
}
and make Wallet conform to that
class Wallet<T>: WalletType
then you can cast values to that protocol and use it as expected:
if let wallet = bitcoinWallet as? WalletType {
print(wallet.usdValue)
}