Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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))
        }
    }
}
like image 736
Zorgan Avatar asked Dec 30 '22 16:12

Zorgan


2 Answers

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]

}
like image 58
Sweeper Avatar answered Jan 04 '23 05:01

Sweeper


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 }
}
like image 30
Mojtaba Hosseini Avatar answered Jan 04 '23 04:01

Mojtaba Hosseini