Zorgan
Zorgan

Reputation: 9123

How to return an enum from a function in SwiftUI?

I want to return an enum from my function. For that, I need to classify a return type.

However, what is the return type if I want to return an enum?

My code below:

class Timezone {
    
    func getTimeZoneFromCountry(country: String) -> ??? {
        switch country {
        case "AU":
            return Timezone.AU
        case "US":
            return Timezone.US
        default:
            return Timezone.AU
        }
    }
   
    enum AU: String, Identifiable, CaseIterable {
        case AEST = "AEST"
        case AWST = "AWST"
        case ACST = "ACST"
        
        var id: AU {self}
    }
    
    enum US: String, Identifiable, CaseIterable {
        case CST = "CST"
        case EST = "EST"
        case MST = "MST"
        case PST = "PST"
        
        var id: US {self}
    }

}

I'm trying to dynamically populate a Picker:

struct TimeZonePicker: View {
    @Binding var country: String // AU or US
    
    var body: some View {
        Picker("Timezone", selection: $country) {
            ForEach(*Dynamic enum here*) { i in
                Text(String(i.rawValue))
            }
        }
    }
}

Edit: New view with @Sweeper's dynamic solution:

var body: some View {
    Picker("Timezone", selection: $country) {
        ForEach(Timezone.getTimeZoneFromCountry(country: country)) { i in
            Text(String(i.rawValue))
        }
    }
}

Upvotes: 4

Views: 1169

Answers (3)

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119177

The accepted answer is a good fit but for those who are care about the quality of the code, these are extra:

First of all

You don't need to define the raw value for String based enums when the raw value and the case is exactly the same! So

case AEST = "AEST"

is exactly equal to:

case AEST

Second of all:

You can extend the Identifiable itself to have an implementation to all RawRepresentables:

extension Identifiable where Self: RawRepresentable {
    var id: Self { self }
}

Third of all:

You can use associated value in the enum for sectioning values like:

enum Timezone {
    case au([AU])
    case us([US])
,,,
}

Forth of all:

Try to avoid unknown behaviors by avoiding default assigning. You can have an optional init instead:

init?(raw: String) {
    switch raw.lowercased() {
    case "au": self = .au(AU.allCases)
    case "us": self = .us(US.allCases)
    default: return nil
    }
}

Fifth of all:

Don't forget that strings are case sensitive! Try handle the sensitivity. for example check for lowercased.


So the full code would be something like:

enum Timezone {
    init?(raw: String) {
        switch raw.lowercased() {
        case "au": self = .au(AU.allCases)
        case "us": self = .us(US.allCases)
        default: return nil
        }
    }

    case au([AU])
    case us([US])

    enum AU: String, Identifiable, CaseIterable {
        case AEST
        case AWST
        case ACST
    }

    enum US: String, Identifiable, CaseIterable {
        case CST
        case EST
        case MST
        case PST
    }
}

extension Identifiable where Self: RawRepresentable {
    var id: Self { self }
}

Upvotes: 2

Nisarg Shah
Nisarg Shah

Reputation: 754

Give return type as array of string and inside switch case return the array of strings from enum's allCase.

class Timezone {
    
    func getTimeZoneFromCountry(country: String) -> [String] {
    switch country {
    case "AU":
        return Timezone.AU.allCases.map({$0.rawValue})
    case "US":
    return Timezone.US.allCases.map({$0.rawValue})
    default:
    return Timezone.AU.allCases.map({$0.rawValue})
    }
    }
    
    enum AU: String, Identifiable, CaseIterable {
        case AEST = "AEST"
        case AWST = "AWST"
        case ACST = "ACST"
        
        var id: AU {self}
    }
    
    enum US: String, Identifiable, CaseIterable {
        case CST = "CST"
        case EST = "EST"
        case MST = "MST"
        case PST = "PST"
        
        var id: US {self}
    }
    
}

Upvotes: 0

Sweeper
Sweeper

Reputation: 270980

Use a single enum, and have getTimeZoneFromCountry return only certain cases of the enum:

enum Timezone: String, Identifiable, CaseIterable {
    var id: Timezone { self }
    
    static func getTimeZoneFromCountry(country: String) -> [Timezone] {
        switch country {
        case "AU":
            return Timezone.AU
        case "US":
            return Timezone.US
        default:
            return Timezone.AU
        }
    }
    case AEST = "AEST"
    case AWST = "AWST"
    case ACST = "ACST"

    case CST = "CST"
    case EST = "EST"
    case MST = "MST"
    case PST = "PST"
    
    static let AU: [Timezone] = [.AEST, .AWST, .ACST]
    static let US: [Timezone] = [.CST, .EST, .MST, .PST]

}

Upvotes: 8

Related Questions