具有内部函数和属性的Swift公共协议

Saj*_*jon 11 access-modifiers ios swift swift-protocols swift3

我想知道什么是最好的做法,当我想要一些功能公开,一些内部使用协议时我.

我写的AudioManager斯威夫特3包装AVPlayer作为一个框架.

我想要一些方法是公共的,所以例如使用AudioManager的ViewController可以访问一些方法,但是一些方法不会暴露在框架之外
- >即使用访问修饰符internal而不是public.

我正在用协议驱动设计编写框架,几乎每个部分都应该有一个协议.
因此协议正在与框架内的协议进行通信.
例如,主类 - AudioManager有一个AudioPlayer,并且应该能够internal在其上调用一些函数,
例如,pause(reason:)但该方法应该internal在框架之外暴露.

这是一个例子.

internal enum PauseReason {
    case byUser
    case routeChange
}

// Compilation error: `Public protocol cannot refine an internal protocol`
public protocol AudioPlayerProtocol: InternalAudioPlayerProtocol { 
   func pause() // I want 
}

internal protocol InternalAudioPlayerProtocol {
    func pause(reason: PauseReason) // Should only be accessible within the framework
}

public class AudioPlayer: AudioPlayerProtocol {
    public func pause() {
        pause(reason: .byUser)
    }

    // This would probably not compile because it is inside a public class...
    internal func pause(reason: PauseReason) { //I want this to be internal
        // save reason and to stuff with it later on
    }
}

public protocol AudioManagerProtocol {
    var audioPlayer: AudioPlayerProtocol { get }
}

public class AudioManager: AudioManagerProtocol {
    public let audioPlayer: AudioPlayerProtocol

    init() {
        audioPlayer = AudioPlayer()
        NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange(_:)), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
    }

    func handleRouteChange(_ notification: Notification) {
        guard
        let userInfo = notification.userInfo,
        let reasonRaw = userInfo[AVAudioSessionRouteChangeReasonKey] as? NSNumber,
        let reason = AVAudioSessionRouteChangeReason(rawValue: reasonRaw.uintValue)
        else { print("what could not get route change") }
        switch reason {
        case .oldDeviceUnavailable:
            pauseBecauseOfRouteChange()
        default:
            break
        }
    }
}

private extension AudioManager {
    func pauseBecauseOfRouteChange() {
        audioPlayer.pause(reason: .routeChange)
    }
}

// Outside of Audio framework
class PlayerViewController: UIViewController {
    fileprivate let audioManager: AudioManagerProtocol 
    @IBAction didPressPauseButton(_ sender: UIButton) {
        // I want the `user of the Audio framwwork` (in this case a ViewController)
        // to only be able to `see` `pause()` and not `pause(reason:)` 
        audioManager.audioPlayer.pause()
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以通过将方法更改为以下内容来使其工作pauseBecauseOfRouteChange:

func pauseBecauseOfRouteChange() {
    guard let internalPlayer = audioPlayer as? InternalAudioPlayerProtocol else { return }
    internalPlayer.pause(reason: .routeChange)
}
Run Code Online (Sandbox Code Playgroud)

但我想知道是否有更优雅的解决方案?
像标记AudioPlayerProtocol精炼的东西InternalAudioPlayerProtocol......

或者你们的程序员如何做到这一点?
如果它不公开供内部使用的方法和变量,那么框架会更漂亮!

谢谢!

Wla*_*ala 2

不,至少在考虑协议时,没有更优雅的解决方案,原因如下:

想象一下这样一个场景,有人使用你的框架想要为 编写一个扩展AudioPlayerProtocolpause(reason:)如果 then 方法是内部的,那么如何实现它?

您可以通过子类化来实现它,并且此代码实际上可以编译:

public class AudioPlayer: AudioPlayerProtocol {
    public func pause() {
        pause(reason: .byUser)
    }

    internal func pause(reason: PauseReason) {
    }
}
Run Code Online (Sandbox Code Playgroud)

对于协议,情况并非如此,因为如果具有公共访问级别的人想要使用您的混合公共/内部协议,您根本无法保证内部功能的实现。