jorgenhenrichsen / SwiftAudio

Audio player for iOS
MIT License
226 stars 86 forks source link

Allow inheritance from AudioPlayer and QueuedAudioPlayer #109

Open curiousdustin opened 4 years ago

curiousdustin commented 4 years ago

Is your feature request related to a problem? Please describe. In my project I would like to override some portions of QueuedAudioPlayer. I cannot because is it marked as public, and not open.

I want to override because I want to send events to react native when certain things happen. Most of these can be achieved alternatively by listening to the events emitted by SwiftAudio, however, these are asynchronous, and I have had a lot of trouble getting the proper data to pass on in my events to react because things like the currentItem have changed by the time the SwiftAudio event is handled. Hope that makes sense.

Describe alternatives you've considered I do not know swift well enough yet to know if there is a better alternative. Any guidance would be greatly appreciated.

Additional context Here is an example of what I would like to do. This works if I include SwiftAudio codebase directly in my Xcode project. But I would like to achieve similar by installing SwiftAudio via CocoaPods.

import Foundation
import MediaPlayer
import SwiftAudio

public class MyAudioPlayer: QueuedAudioPlayer {

  public var reactEventEmitter: RCTEventEmitter

  // Override _currentItem so that we can send an event when it changes.
  override var _currentItem: AudioItem? {
    willSet(newCurrentItem) {
      if ((newCurrentItem as? Track) === (_currentItem as? Track)) {
        return
      }

      self.reactEventEmitter.sendEvent(withName: "playback-track-changed", body: [
        "track": (_currentItem as? Track)?.id ?? nil,
        "position": self.currentTime,
        "nextTrack": (newCurrentItem as? Track)?.id ?? nil,
        ])
    }
  }

  // Override init to include a reference to the React Event Emitter.
  public init(reactEventEmitter: RCTEventEmitter) {
        self.reactEventEmitter = reactEventEmitter
    super.init()
    }

  // MARK: - Player Actions

  public override func load(item: AudioItem, playWhenReady: Bool = true) throws {
        let url: URL
        switch item.getSourceType() {
        case .stream:
            if let itemUrl = URL(string: item.getSourceUrl()) {
                url = itemUrl
            }
            else {
                throw APError.LoadError.invalidSourceUrl(item.getSourceUrl())
            }
        case .file:
            url = URL(fileURLWithPath: item.getSourceUrl())
        }

        wrapper.load(from: url,
                     playWhenReady: playWhenReady,
                     initialTime: (item as? InitialTiming)?.getInitialTime(),
                     options:(item as? AssetOptionsProviding)?.getAssetOptions())

        self._currentItem = item

        if (automaticallyUpdateNowPlayingInfo) {
            self.loadNowPlayingMetaValues()
        }

    // This is the only change from the original AudioPlayer
        if (item is RemoteCommandable) {
            enableRemoteCommands(forItem: item)
        }
    }

  // MARK: - AVPlayerWrapperDelegate

    override func AVWrapper(didChangeState state: AVPlayerWrapperState) {
        super.AVWrapper(didChangeState: state)
    self.reactEventEmitter.sendEvent(withName: "playback-state", body: ["state": state.rawValue])
    }

    override func AVWrapper(failedWithError error: Error?) {
        super.AVWrapper(failedWithError: error)
        self.reactEventEmitter.sendEvent(withName: "playback-error", body: ["error": error?.localizedDescription])
    }

    override func AVWrapperItemDidPlayToEndTime() {
        if self.nextItems.count == 0 {
      // For consistency sake, send an event for the track changing to nothing
      self.reactEventEmitter.sendEvent(withName: "playback-track-changed", body: [
        "track": (self.currentItem as? Track)?.id ?? nil,
        "position": self.currentTime,
        "nextTrack": nil,
        ])

      // fire an event for the queue ending
      self.reactEventEmitter.sendEvent(withName: "playback-queue-ended", body: [
        "track": (self.currentItem as? Track)?.id,
        "position": self.currentTime,
        ])
    } 
    super.AVWrapperItemDidPlayToEndTime()
    }
}
curiousdustin commented 4 years ago

I should have included the error:

Cannot inherit from non-open class 'QueuedAudioPlayer' outside of its defining module