swiftenumsassociated-value

Nested enums to allow multiples of timeframes


I'm hoping to achieve a certain style of enum syntax/functionality, however I'm not sure how to achieve it. Currently I have the following:

internal enum Timeframe: Equatable {

    // MARK: - Cases

    case hour(count: Int)
    case day(count: Int)
    case week(count: Int)
    case month(count: Int)
    case year(count: Int)
    case all
    case exact(date: Date)
    case unspecified
}

I would like to eliminate the count associated value, unless it's required. For example:

let oneDayTimeframe: Timeframe = .day
let twoDayTimeframe: Timeframe = .day.multiplied(by: 2)

Is this possible? Even if there isn't a way to achieve exactly what I'm looking for, I'd appreciate suggestions for potential improvements. In most cases, I end up using (count: 1), which seems a bit verbose. If default values were available with associated values, I would have used

case hour(count: Int = 1)

Any suggestions?


Solution

  • Iterating a bit on the answer you provided:

    enum TimeFrame: Equatable {
    
        case all
        case countable(timeFrame: CountableTimeFrame)
        case exact(date: Date)
    }
    
    enum CountableTimeFrame: Equatable {
    
        case hour
        case day
        case week
        case month
        case year
    
        indirect case multiple(CountableTimeFrame, Int)
    
        var timeFrame: TimeFrame {
            return .countable(timeFrame: self)
        }
    
        static func * (left: CountableTimeFrame, right: Int) -> CountableTimeFrame {
            switch left {
            case .multiple(let timeFrame, let count):
                return .multiple(timeFrame, count * right)
            default:
                return .multiple(left, right)
            }
        }
    
        static func * (left: Int, right: CountableTimeFrame) -> CountableTimeFrame {
            return right * left
        }
    }
    

    Would disallow misuse e.g. disallow:

    let timeFrame: TimeFrame = .multiple(.exact(date: someDate), 666)
    let timeFrame: TimeFrame = .multiple(unspecified, 40)
    let timeFrame: TimeFrame = .multiple(all, -1) 
    

    And allow multiplication using the * operator, e.g.

    let timeFrame: CountableTimeFrame = 4 * .hour
    print(timeFrame) // multiple(__lldb_expr_5.CountableTimeFrame.hour, 4)
    print(timeFrame * 2) // multiple(__lldb_expr_5.CountableTimeFrame.hour, 8)
    

    And .unspecified:

    let optionalTimeFrame: TimeFrame? = nil