I'm trying to use a value from a ViewController in enum. I'm not sure but the only thing I can think of is using a protocol/delegate, which seems like a bad idea...
Right now, the value I want is declared globally, so it works just fine in the code below... but I don't want to declare it globally and would like to declare it in a ViewController.
import Foundation
enum FIRCollectionReference: String {
case users
case chatList
case chatData
case templateReply
func goToLayer() -> String {
switch self {
case .chatData:
return "users/\(myDocId)/chatData"
case .templateReply:
return "users/\(myDocId)/chatData"
default:
return self.rawValue
}
}
}
Any suggestions?
Create a method in enum FIRCollectionReference that accepts an Int (myDocId), i.e.
enum FIRCollectionReference: String {
case users
case chatList
case chatData
case templateReply
func goToLayer(_ myDocId: Int) -> String {
switch self {
case .chatData:
return "users/\(myDocId)/chatData"
case .templateReply:
return "users/\(myDocId)/chatData"
default:
return self.rawValue
}
}
}
Usage:
let reference = FIRCollectionReference.chatData
print(reference.goToLayer(1234))
Related
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.
I have some hardcoded string, based on that i have created switch case. But instead of string in switch case i am trying to create Enum for same. but i am not sure how to do that. I dont want to use default case.
Do i need to access with raw value or any other better way to do ?
enum screens: String {
case faq = "faq", contactus = "contactus", termncondi = "termncondi", dashoboard = "dashoboard"
}
func deepLink(text: String) -> String {
switch text {
case "faq":
return (FAQ.localized())
case "contactus":
return (Contactus.localized())
case "termncondi":
return (Term.localized())
case "dashoboard":
return (Dashboard.localized())
default:
return ""
}
}
You can simply define the enum and override the rawValue property like so:
enum Link {
case faq
case contact
...
var rawValue: String {
switch self {
case .faq:
return FAQ.localized()
case .contact:
return Contact.localized()
default:
return "Unknown case"
}
}
}
And get its rawValue.
let faqLink = Link.faq.rawValue
You can also perform a switch on an enum instance just like you do with a string.
In order to get hardcoded values do something like:
public extension String {
static func getScreens(name: DeepLink) -> String {
return name.rawValue
}
public enum DeepLink: String {
case faq = "faq"
case contactus = "contactus"
// and so on
}
}
I am writing TokenManager class which has a JWT token stored in String. It can be decoded from JSON to object of type ClaimsData
struct ClaimsData: Codable {
let user_id: Int
let username: String
let exp: Int
let email: String
let orig_iat: String
}
Now I would love to have one function responsible for giving particular piece of data. But it should only return data if isAuthorised property of TokenManager is true therefor claimsData isn't nil. So I wrote this piece of code:
enum ClaimsOfInt {
case user_id
case exp
}
enum ClaimsOfString {
case username
case email
case orig_iat
}
enum Result<T> {
case success(T)
case notAuthorised
}
func get(_ claim: ClaimsOfInt) -> Result<Int> {
if let claimsData = claimsData {
switch claim {
case .user_id:
return .success(claimsData.user_id)
case .exp:
return .success(claimsData.exp)
}
} else {
return .notAuthorised
}
}
func get(_ claim: ClaimsOfString) -> Result<String> {
if let claimsData = claimsData {
switch claim {
case .username:
return . success(claimsData.username)
case .email:
return . success(claimsData.email)
case .orig_iat:
return . success(claimsData.orig_iat)
}
} else {
return .notAuthorised
}
}
As you can see I have two functions one for each type. I guess it's good enough for now, but I am wondering is it possible to somehow write this as one functions using for example generics? If so how would it look like? Using Any may be one option but as far as my knowledge goes it isn't very swifty.
You cannot have different return types, depending on the value of a function argument.
But you could write a generic function which takes a key path argument:
func get<T>(claimsData: ClaimsData?, key: KeyPath<ClaimsData, T>) -> Result<T> {
if let claimsData = claimsData {
return .success(claimsData[keyPath: key])
} else {
return .notAuthorised
}
}
Example usage:
let result = get(claimsData: claimsData, key: \.user_id)
Here the result type is inferred as Result<Int>.
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)
Swift question is there a way of having an enum type with one case that can have an associated value.
I have an API that gives me available filters, it's unlikely but possible that the API will add additional filter types. So if the API sends an unknown filter type I want to keep that information associated with the enum.
Below are the different ideas I had about doing this.
My first two tries didn't compile. My third try just feels a bit clunky.
Does anyone have a better way of doing this? Do you think I just shouldn't use an enum for this problem?
typealias APIFilterIdentifier = String
/* Does Not Compile */
enum EnumTestAssociatedValeu: APIFilterIdentifier {
case Unknown(APIFilterIdentifier)
case Everyone = "everyone"
case Team = "myteam"
}
/* Does not compile */
enum EnumTestDefaultAssociatedValues: APIFilterIdentifier {
case Unknown(APIFilterIdentifier)
case Everyone(APIFilterIdentifier = "everyone")
case Team(APIFilterIdentifier = "myteam")
}
/* Compiles but is there a better way? */
enum EnumTestWithCustomInit {
case Unknown(APIFilterIdentifier)
case Everyone
case Team
init(filterIdentifier: APIFilterIdentifier) {
let everyone: APIFilterIdentifier = EnumTestWithCustomInit.everyoneFilterIdentifier
let team: APIFilterIdentifier = EnumTestWithCustomInit.teamFilterIdentifier
switch filterIdentifier {
case everyone:
self = .Everyone
case team:
self = .Team
default:
self = .Unknown(filterIdentifier)
}
}
func asIdentifer() -> APIFilterIdentifier {
switch self {
case .Everyone:
return EnumTestWithCustomInit.everyoneFilterIdentifier
case .Team:
return EnumTestWithCustomInit.teamFilterIdentifier
case .Unknown(let filterIdentifier):
return filterIdentifier
}
}
private static var everyoneFilterIdentifier: APIFilterIdentifier {
return "everyone"
}
private static var teamFilterIdentifier: APIFilterIdentifier {
return "myteam"
}
}
I know this is a bit old, but would this work for what you want?
typealias FilterIdentifier = String
enum DefaultAPIFilters: FilterIdentifier {
case Everyone = "everyone"
case Team = "team"
}
enum APIFilters {
case Default(DefaultAPIFilters)
case Custom(FilterIdentifier)
}
let everyoneFilter = APIFilters.Default(.Everyone)
let teamFilter = APIFilters.Default(.Team)
let clownFilter = APIFilters.Custom("clowns_only")
Extending Nathan Perry's response:
You can add a
var underlyingString: String {
return getUnderlyingString(self)
}
to the enum. Then define
func getUnderlyingString(apiFilter: APIFilters) -> String {
switch apiFilter {
case .Default(let defaultAPIFilter):
return defaultAPIFilter.rawValue
case .Custom(let custom):
return custom
}
}
According to this answer: Default value for Enum in Swift
I recommend using such an approach
public enum Result {
case passed(hint: String)
case failed(message: String)
static let passed: Self = .passed(hint: "")
}
let res: Result = Result.passed