Enum Case with Object - swift

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 { ... }

Related

Swift Enum associated values conforming to single protocol

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
}
}

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.

Generic class using some class returned from a function cause an error

I am confused using generics.
I want to implement something like this:
let assume WantedClass as
class WantedClass<T>: NSObject where T: SomeBasicProtocol {
....
}
and an enum
enum Provider: Int {
case one = 1
case two
case three
var providerClass: SomeBasicProtocol.Type {
switch self {
case .one:
return SomeClass1.self
case .two:
return SomeClass2.self
case .three:
return SomeClass3.self
}
}
}
when I try to define an instance
let provider: Provider = .one
let GenericClassIWantToInject = provider.providerClass
let wantedInstance = WantedClass<GenericClassIWantToInject>()
it yields this error:
Use of undeclared type 'GenericClassIWantToInject'
Why? What have I misunderstood? Is it possible to code this way?
class SomeClass1: SomeBasicProtocol { }
class SomeClass2: SomeBasicProtocol { }
class SomeClass3: SomeBasicProtocol { }
To make things clearer, you have done everything correct except for the last part where you did this let wantedInstance = WantedClass<GenericClassIWantToInject>(). Lets break down and see what you are trying to do with this line of code. You are trying to tell the compiler at compile time, that WantedClass has a generic parameter that has to be GenericClassIWantToInject which is computed at run time. Doesn't that sound wrong to you? Can the compiler infer the Type of a generic parameter at compile time with something that is computed at run time? So to answer one of your questions, no you can not use the code this way.
What you can do is something like this:
protocol SomeBasicProtocol {}
class WantedClass<T>: NSObject where T: SomeBasicProtocol {
let generic: T
init(generic: T) {
self.generic = generic
super.init()
}
}
class SomeClass1: SomeBasicProtocol {}
class SomeClass2: SomeBasicProtocol {}
class SomeClass3: SomeBasicProtocol {}
enum Provider: Int {
case one = 1
case two
case three
var providerClass: SomeBasicProtocol {
switch self {
case .one:
return SomeClass1()
case .two:
return SomeClass2()
case .three:
return SomeClass3()
}
}
}
let provider: Provider = .one
let classToInject = provider.providerClass
switch classToInject {
case let class1 as SomeClass1:
let wantedClass = WantedClass(generic: class1)
case let class2 as SomeClass2:
let wantedClass = WantedClass(generic: class2)
case let class3 as SomeClass3:
let wantedClass = WantedClass(generic: class3)
default:
return
}
Now I'm not sure what you want to achieve, but following your example I came up with the code above and that's what I'm guessing you want to do.
Moreover do these changes if you don't need a variable of your generic Parameter in WantedClass:
class WantedClass<T>: NSObject where T: SomeBasicProtocol {
override init() {
super.init()
}
}
switch classToInject {
case let class1 as SomeClass1:
let wantedClass = WantedClass<SomeClass1>()
case let class2 as SomeClass2:
let wantedClass = WantedClass<SomeClass2>()
case let class3 as SomeClass3:
let wantedClass = WantedClass<SomeClass3>()
default:
return
}
Thanks to #Serj
I've changed my code to this one:
let providerClass = provider.providerClass
switch providerClass {
case is SomeClass1.Type:
let wantedInstance = WantedClass<SomeClass1>()
case is SomeClass2.Type:
let wantedInstance = WantedClass<SomeClass2>()
case is SomeClass3.Type:
let wantedInstance = WantedClass<SomeClass3>()
default: break
}
Now it works.

How to check Swift nested enums for equality of the outer enum only?

I'm trying to use nested enums to describe my model, in a way that makes illegal states impossible and provides categorisation at the top level. Right now, my (simplified) code is:
enum SportsEvent {
enum RunningEvent {
case sprint
case marathon
}
enum ThrowingEvent {
case hammer
case javelin
case discus
}
case running(event: RunningEvent)
case throwing(event: ThrowingEvent)
func isSameCategory(as other: SportsEvent) -> Bool {
return false
}
}
let sprint = SportsEvent.running(event: .sprint)
let javelin = SportsEvent.throwing(event: .javelin)
let hammer = SportsEvent.throwing(event: .hammer)
sprint.isSameCategory(as: javelin) // False
hammer.isSameCategory(as: javelin) // True
It feels like it should be trivial with an if case ... and a wildcard, but I'm not seeing how to achieve that. I'm hoping a giant switch statement isn't necessary, as my actual model is more complex.
I think you need a switch-statement, with a “compound case” listing all
possible “same value combinations” of the outer enumeration,
plus a default case:
func isSameCategory(as other: SportsEvent) -> Bool {
switch (self, other) {
case (.running, .running),
(.throwing, .throwing):
return true
default:
return false
}
}
Or (attribution goes to #Hamish):
func isSameCategory(as other: SportsEvent) -> Bool {
switch (self, other) {
case (.running, .running),
(.throwing, .throwing):
return true
case (.running, _),
(.throwing, _):
return false
}
}
which has the advantage that the compiler checks that all cases are covered. For an enumeration with n cases that makes 2 * n
cases in the switch statement (which is better than n * n if you checked all possible combinations).
Depending on your use case, you might be able to convert SportEvent to a protocol:
enum RunningEvent {
case sprint
case marathon
}
enum ThrowingEvent {
case hammer
case javelin
case discus
}
enum SportEventCategory {
case running
case throwing
}
protocol SportEvent {
var category: SportEventCategory { get }
}
extension RunningEvent: SportEvent {
var category: SportEventCategory {
return .running
}
}
extension ThrowingEvent: SportEvent {
var category: SportEventCategory {
return .throwing
}
}
let sportEvent1: SportEvent = RunningEvent.sprint
let sportEvent2: SportEvent = ThrowingEvent.hammer
print(sportEvent1.category == sportEvent2.category)
or even as one flat enum:
enum SportEvent {
enum Category {
case running
case throwing
}
case sprint
case marathon
case hammer
case javelin
case discus
var category: Category {
switch self {
case .sprint, .marathon, .hammer:
return .running
case .javelin, .discus:
return .throwing
}
}
}
let sportEvent1: SportEvent = .sprint
let sportEvent2: SportEvent = .marathon
print(sportEvent1.category == sportEvent2.category)

Can I group two functions with Generics into one function in Swift?

I have two bellow functions with Generics.
func objectFunc<T:SomeProtocol>(obj:T)
func arrayFunc<T:SomeProtocol>(obj:[T])
Can I group these functions into one function?
I found the link Checking if an object is a given type in Swift, but this is a little different.
Added.
For example, I want to do like bellow.
func objectAndArrayFunc<T>(arg:T, someEnum:SomeEnum){
switch someEnum {
case A:
// something
case B:
// something
}
if let items = arg as? [T] {
for item in items {
// something
}
} else if let item = arg as? T {
// something
}
// I want to do something [T] and T common processing
}
enum SomeEnum {
case A
case B
}
Also, SomeEnum count might increase.
Generally speaking, depends on what // something is. There are many ways ...
Private common processor
protocol Property {
var name: String{ get }
}
enum SomeEnum {
case A, B
}
func process<T:Property>(object:T, someEnum:SomeEnum) {
process(object, nil, someEnum)
}
func process<T:Property>(objects:[T], someEnum:SomeEnum) {
process(nil, objects, someEnum)
}
private func process<T:Property>(object: T?, objects:[T]?, someEnum:SomeEnum) {
switch someEnum {
case .A:
// something
break
case .B:
// something
break
}
// holds all items for common processing
var itemsToProcess: [T] = []
if let items = objects {
// process items
itemsToProcess = items
for item in items {
println("\(item.name)")
}
} else if let item = object {
// process single item
itemsToProcess = [item]
println("\(item.name)")
}
// iterate over single/all items and process them
for item in itemsToProcess {
println("\(item.name)")
}
}
Wrap it to Enum
protocol Property {
var name: String{ get }
}
enum SomeEnum {
case A, B
}
enum Objects<T> {
case Single(T)
case Multiple([T])
}
private func process<T:Property>(objects: Objects<T>, someEnum:SomeEnum) {
switch someEnum {
case .A:
// something
break
case .B:
// something
break
}
// holds all items for common processing
var itemsToProcess: [T] = []
switch objects {
case .Multiple(let items):
// process items
itemsToProcess = items
for item in items {
println("\(item.name)")
}
case .Single(let item):
// process single item
itemsToProcess = [item]
println("\(item.name)")
}
// iterate over single/all items and process them
for item in itemsToProcess {
println("\(item.name)")
}
}
struct Prop: Property {
var name: String {
return "hi"
}
}
let prop = Prop()
process(.Single(prop), .A)
process(.Multiple([prop]), .B)
Unfortunately, the 2nd example segfaults Swift 1.2 compiler.
Anyway, it really depends on what your goal is. Why you do not want pass even single item as [T], ...
To answer your question - no, you can't pass T or [T] in one argument, different types. Unless you wrap it to Enum or whatever, unless you do want to use AnyObject and make as dances, ...