Swift 中的嵌套间接枚举

Nested indirect enums in Swift

我正在写一个 AudioManager in Swift 3(请帮我起一个比 "AudioManager" 更好的名字)来包装 AVPlayer。目的是让播放器处理中断、路由更改并支持自定义播放速率、睡眠计时器、命令中心、正在播放信息等。由于 AVFoundation 大量 事件驱动 ,利用 KVONSNotification 让我的项目事件驱动也很有意义。我合并了一个消息系统,应用程序的不同部分将事件向上发送链,一直到根节点 AudioManager。所以我想要一个 enum 的事件表示,因为这是最有意义的。但是,我希望我的事件按例如分组RouteChangeBufferingEventInterruptionEvent 等。所以我终于使用 nested 让它工作了枚举.

我正在编写一个示例 GUI,然后它可以 调入 来自 AudioManager 的事件,而不必使用 NSNotification,或 闭包

EDITTED 使用@andyvn22

提供的答案
enum AudioError: Error {
    indirect case buffering(Buffering)
    enum Buffering {
        case unknown
    }

    indirect case playback(Playback)
    enum Playback {
        case failedToSetupAVAsset
        case failedToSetupAVItem
    }

    init(_ buffering: Buffering) {
        self = .buffering(buffering)
    }

    init(_ playback: Playback) {
        self = .playback(playback)
    }
}

enum Event {
    case failure(AudioError)

    init(_ error: AudioError) {
        self = .failure(error)
    }

    init(_ bufferingError: AudioError.Buffering) {
        self = .failure(AudioError.buffering(bufferingError))
    }

    init(_ playbackError: AudioError.Playback) {
        self = .failure(AudioError.playback(playbackError))
    }

    indirect case buffering(Buffering)
    enum Buffering {
        case idle, started, finished
    }

    indirect case playback(Playback)
    enum Playback {
        case tick, wasPaused
    }

    indirect case route(RouteChange)
    enum RouteChange {
        case unavailable, available
    }

    indirect case interruption(Interruption)
    enum Interruption {
        case interrupted, interruptionEnded
    }
}

您可以将所有内容粘贴到 Swift Playground,然后添加 handle 方法并使用下面的调用示例调用它:

func handle(_ error: AudioError.Buffering) {
    handle(AudioError.buffering(error))
}

func handle(_ error: AudioError) {
    handle(Event.failure(error))
}

func handle(_ event: Event) {
    switch event {
    case .failure(let errorType):
        print("failure", terminator: " ")
        switch errorType {
        case .playback(let error):
            print("playback", terminator: " ")
            switch error {
            case .failedToSetupAVAsset:
                print("setupAVAsset")
            case .failedToSetupAVItem:
                print("setupAVItem")
            }
        case .buffering(let error):
            print("buffering", terminator: " ")
            switch error {
            case .unknown:
                print("unknown")
            }
        }
    case .buffering(let buffering):
        print("buffering", terminator: " ")
        switch buffering {
        case .idle:
            print("idle")
        case .started:
            print("started")
        case .finished:
            print("finished")
        }
    case .playback(let playback):
        print("playback", terminator: " ")
        switch playback {
        case .tick:
            print("tick")
        case .wasPaused:
            print("wasPaused")
        }
    default:
        print("unhandled case")
    }
}

/* All these are equivalent */
handle(Event.failure(.buffering(.unknown)))
handle(Event(.buffering(.unknown)))
handle(Event(AudioError(.unknown)))
handle(Event(.unknown))
handle(.unknown)

原始问题
然而,写 handle(Event(.buffering(.unknown))) 有点乏味,它已经是 handle(Event.failure(.buffering(.unknown))) 的简写版本。

我的问题:
是否可以仅使用 AudioError.BufferingAudioError.Playback 中的内壳来创建带有 case .failureEvent ?

允许做这样的事情:

handle(Event(.unknown))

假设 AudioError.BufferingAudioError.Playback 不共享同名案例...

也许我错过了一些很棒的部分 Swift 3 什么允许这样做?

是——通过重载初始化器,您可以允许任何子类型进行初始化。例如:

enum AudioError: Error {
    indirect case buffering(Buffering)
    enum Buffering {
        case unknown
    }

    indirect case playback(Playback)
    enum Playback {
        case failedToSetupAVAsset
        case failedToSetupAVItem
    }

    init(_ buffering: Buffering) {
        self = .buffering(buffering)
    }

    init(_ playback: Playback) {
        self = .playback(playback)
    }
}

let example = AudioError(.failedToSetupAVAsset) //this works...
let other = AudioError(.unknown) //but so does this.

通过为每个子类型创建许多类似这样的初始值设定项,但在 Event 而不是 AudioError 上,您可以根据需要嵌套任意数量,而不会使语法复杂化。