From c06b8ce64c48f5ddc8d7d9bbaffcc524d1ccd2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Henrichsen?= Date: Sat, 11 May 2019 17:34:24 +0200 Subject: [PATCH 1/7] Made the player observer not retain the AVPlayer. They will also stop observing the current AVPlayer if a new one is going to be set. --- Example/Tests/AVPlayerObserverTests.swift | 3 +- Example/Tests/AVPlayerTimeObserverTests.swift | 3 +- .../AVPlayerWrapper/AVPlayerWrapper.swift | 8 ++-- .../Classes/Observer/AVPlayerObserver.swift | 39 +++++++++++-------- .../Observer/AVPlayerTimeObserver.swift | 35 ++++++++++------- 5 files changed, 54 insertions(+), 34 deletions(-) diff --git a/Example/Tests/AVPlayerObserverTests.swift b/Example/Tests/AVPlayerObserverTests.swift index 3b1dbf6..06ef803 100644 --- a/Example/Tests/AVPlayerObserverTests.swift +++ b/Example/Tests/AVPlayerObserverTests.swift @@ -20,7 +20,8 @@ class AVPlayerObserverTests: QuickSpec, AVPlayerObserverDelegate { beforeEach { player = AVPlayer() player.volume = 0.0 - observer = AVPlayerObserver(player: player) + observer = AVPlayerObserver() + observer.player = player observer.delegate = self } diff --git a/Example/Tests/AVPlayerTimeObserverTests.swift b/Example/Tests/AVPlayerTimeObserverTests.swift index 4311120..947a1da 100644 --- a/Example/Tests/AVPlayerTimeObserverTests.swift +++ b/Example/Tests/AVPlayerTimeObserverTests.swift @@ -17,7 +17,8 @@ class AVPlayerTimeObserverTests: QuickSpec { player = AVPlayer() player.automaticallyWaitsToMinimizeStalling = false player.volume = 0 - observer = AVPlayerTimeObserver(player: player, periodicObserverTimeInterval: TimeEventFrequency.everyQuarterSecond.getTime()) + observer = AVPlayerTimeObserver(periodicObserverTimeInterval: TimeEventFrequency.everyQuarterSecond.getTime()) + observer.player = player } context("has started boundary time observing", { diff --git a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index b79d399..5c7617c 100755 --- a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -26,7 +26,7 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { // MARK: - Properties - let avPlayer: AVPlayer + var avPlayer: AVPlayer let playerObserver: AVPlayerObserver let playerTimeObserver: AVPlayerTimeObserver let playerItemNotificationObserver: AVPlayerItemNotificationObserver @@ -48,8 +48,10 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { public init(avPlayer: AVPlayer = AVPlayer()) { self.avPlayer = avPlayer - self.playerObserver = AVPlayerObserver(player: avPlayer) - self.playerTimeObserver = AVPlayerTimeObserver(player: avPlayer, periodicObserverTimeInterval: timeEventFrequency.getTime()) + self.playerObserver = AVPlayerObserver() + self.playerObserver.player = avPlayer + self.playerTimeObserver = AVPlayerTimeObserver(periodicObserverTimeInterval: timeEventFrequency.getTime()) + self.playerTimeObserver.player = avPlayer self.playerItemNotificationObserver = AVPlayerItemNotificationObserver() self.playerItemObserver = AVPlayerItemObserver() diff --git a/SwiftAudio/Classes/Observer/AVPlayerObserver.swift b/SwiftAudio/Classes/Observer/AVPlayerObserver.swift index 3812a29..1ec1dea 100644 --- a/SwiftAudio/Classes/Observer/AVPlayerObserver.swift +++ b/SwiftAudio/Classes/Observer/AVPlayerObserver.swift @@ -36,39 +36,46 @@ class AVPlayerObserver: NSObject { static let timeControlStatus = #keyPath(AVPlayer.timeControlStatus) } - let player: AVPlayer + private let statusChangeOptions: NSKeyValueObservingOptions = [.new, .initial] private let timeControlStatusChangeOptions: NSKeyValueObservingOptions = [.new] var isObserving: Bool = false weak var delegate: AVPlayerObserverDelegate? - - init(player: AVPlayer) { - self.player = player + weak var player: AVPlayer? { + willSet { + self.stopObserving() + } } deinit { if self.isObserving { - self.player.removeObserver(self, forKeyPath: AVPlayerKeyPath.status, context: &AVPlayerObserver.context) - self.player.removeObserver(self, forKeyPath: AVPlayerKeyPath.timeControlStatus, context: &AVPlayerObserver.context) + self.stopObserving() } } /** Start receiving events from this observer. - - - Important: If this observer is already receiving events, it will first be removed. Never remove this observer manually. */ func startObserving() { - main.async { - if self.isObserving { - self.player.removeObserver(self, forKeyPath: AVPlayerKeyPath.status, context: &AVPlayerObserver.context) - self.player.removeObserver(self, forKeyPath: AVPlayerKeyPath.timeControlStatus, context: &AVPlayerObserver.context) - } - self.isObserving = true - self.player.addObserver(self, forKeyPath: AVPlayerKeyPath.status, options: self.statusChangeOptions, context: &AVPlayerObserver.context) - self.player.addObserver(self, forKeyPath: AVPlayerKeyPath.timeControlStatus, options: self.timeControlStatusChangeOptions, context: &AVPlayerObserver.context) + guard let player = player else { + return } + if self.isObserving { + self.stopObserving() + } + self.isObserving = true + player.addObserver(self, forKeyPath: AVPlayerKeyPath.status, options: self.statusChangeOptions, context: &AVPlayerObserver.context) + player.addObserver(self, forKeyPath: AVPlayerKeyPath.timeControlStatus, options: self.timeControlStatusChangeOptions, context: &AVPlayerObserver.context) + } + + func stopObserving() { + guard let player = player else { + return + } + player.removeObserver(self, forKeyPath: AVPlayerKeyPath.status, context: &AVPlayerObserver.context) + player.removeObserver(self, forKeyPath: AVPlayerKeyPath.timeControlStatus, context: &AVPlayerObserver.context) + } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { diff --git a/SwiftAudio/Classes/Observer/AVPlayerTimeObserver.swift b/SwiftAudio/Classes/Observer/AVPlayerTimeObserver.swift index c30a0e0..44819b5 100644 --- a/SwiftAudio/Classes/Observer/AVPlayerTimeObserver.swift +++ b/SwiftAudio/Classes/Observer/AVPlayerTimeObserver.swift @@ -27,7 +27,12 @@ class AVPlayerTimeObserver { var boundaryTimeStartObserverToken: Any? var periodicTimeObserverToken: Any? - private let player: AVPlayer + weak var player: AVPlayer? { + willSet { + unregisterForBoundaryTimeEvents() + unregisterForPeriodicEvents() + } + } /// The frequence to receive periodic time events. /// Setting this to a new value will trigger a re-registering to the periodic events of the player. @@ -41,8 +46,7 @@ class AVPlayerTimeObserver { weak var delegate: AVPlayerTimeObserverDelegate? - init(player: AVPlayer, periodicObserverTimeInterval: CMTime) { - self.player = player + init(periodicObserverTimeInterval: CMTime) { self.periodicObserverTimeInterval = periodicObserverTimeInterval } @@ -50,9 +54,11 @@ class AVPlayerTimeObserver { Will register for the AVPlayer BoundaryTimeEvents, to trigger start and complete events. */ func registerForBoundaryTimeEvents() { - + guard let player = player else { + return + } + unregisterForBoundaryTimeEvents() let startBoundaryTimes: [NSValue] = [AVPlayerTimeObserver.startBoundaryTime].map({NSValue(time: $0)}) - boundaryTimeStartObserverToken = player.addBoundaryTimeObserver(forTimes: startBoundaryTimes, queue: nil, using: { [weak self] in self?.delegate?.audioDidStart() }) @@ -62,10 +68,11 @@ class AVPlayerTimeObserver { Unregister from the boundary events of the player. */ func unregisterForBoundaryTimeEvents() { - if let boundaryTimeStartObserverToken = boundaryTimeStartObserverToken { - player.removeTimeObserver(boundaryTimeStartObserverToken) - self.boundaryTimeStartObserverToken = nil + guard let player = player, let boundaryTimeStartObserverToken = boundaryTimeStartObserverToken else { + return } + player.removeTimeObserver(boundaryTimeStartObserverToken) + self.boundaryTimeStartObserverToken = nil } /** @@ -73,6 +80,9 @@ class AVPlayerTimeObserver { Will trigger unregisterForPeriodicEvents() first to avoid multiple subscriptions. */ func registerForPeriodicTimeEvents() { + guard let player = player else { + return + } unregisterForPeriodicEvents() periodicTimeObserverToken = player.addPeriodicTimeObserver(forInterval: periodicObserverTimeInterval, queue: nil, using: { (time) in self.delegate?.timeEvent(time: time) @@ -83,12 +93,11 @@ class AVPlayerTimeObserver { Unregister for periodic events. */ func unregisterForPeriodicEvents() { - if let periodicTimeObserverToken = periodicTimeObserverToken { - self.player.removeTimeObserver(periodicTimeObserverToken) - self.periodicTimeObserverToken = nil + guard let player = player, let periodicTimeObserverToken = periodicTimeObserverToken else { + return } + player.removeTimeObserver(periodicTimeObserverToken) + self.periodicTimeObserverToken = nil } - - } From 17166093c2e17ce1d454d29013cbda402cb97e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Henrichsen?= Date: Sat, 11 May 2019 17:46:27 +0200 Subject: [PATCH 2/7] Correctly set isObserving when stopping the observation. --- Example/Tests/AVPlayerObserverTests.swift | 15 +++++++++++++++ .../Classes/Observer/AVPlayerObserver.swift | 11 ++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Example/Tests/AVPlayerObserverTests.swift b/Example/Tests/AVPlayerObserverTests.swift index 06ef803..7626bb9 100644 --- a/Example/Tests/AVPlayerObserverTests.swift +++ b/Example/Tests/AVPlayerObserverTests.swift @@ -25,6 +25,10 @@ class AVPlayerObserverTests: QuickSpec, AVPlayerObserverDelegate { observer.delegate = self } + it("should not be observing", closure: { + expect(observer.isObserving).to(beFalse()) + }) + context("when observing has started", { beforeEach { observer.startObserving() @@ -55,6 +59,17 @@ class AVPlayerObserverTests: QuickSpec, AVPlayerObserverDelegate { expect(observer.isObserving).toEventually(beTrue()) }) }) + + context("when stopping observing", closure: { + + beforeEach { + observer.stopObserving() + } + + it("should not be observing", closure: { + expect(observer.isObserving).to(beFalse()) + }) + }) }) } diff --git a/SwiftAudio/Classes/Observer/AVPlayerObserver.swift b/SwiftAudio/Classes/Observer/AVPlayerObserver.swift index 1ec1dea..0fb33c9 100644 --- a/SwiftAudio/Classes/Observer/AVPlayerObserver.swift +++ b/SwiftAudio/Classes/Observer/AVPlayerObserver.swift @@ -49,9 +49,7 @@ class AVPlayerObserver: NSObject { } deinit { - if self.isObserving { - self.stopObserving() - } + self.stopObserving() } /** @@ -61,20 +59,19 @@ class AVPlayerObserver: NSObject { guard let player = player else { return } - if self.isObserving { - self.stopObserving() - } + self.stopObserving() self.isObserving = true player.addObserver(self, forKeyPath: AVPlayerKeyPath.status, options: self.statusChangeOptions, context: &AVPlayerObserver.context) player.addObserver(self, forKeyPath: AVPlayerKeyPath.timeControlStatus, options: self.timeControlStatusChangeOptions, context: &AVPlayerObserver.context) } func stopObserving() { - guard let player = player else { + guard let player = player, self.isObserving else { return } player.removeObserver(self, forKeyPath: AVPlayerKeyPath.status, context: &AVPlayerObserver.context) player.removeObserver(self, forKeyPath: AVPlayerKeyPath.timeControlStatus, context: &AVPlayerObserver.context) + self.isObserving = false } From 386dc5202c101e16078c2f7a2088b8aa9fff27ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Henrichsen?= Date: Sat, 11 May 2019 17:46:55 +0200 Subject: [PATCH 3/7] Recreate the AVPlayer when loading an item, if the previous item failed. --- .../Classes/AVPlayerWrapper/AVPlayerWrapper.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index 5c7617c..cee469a 100755 --- a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -169,6 +169,10 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { func load(from url: URL, playWhenReady: Bool) { reset(soft: true) _playWhenReady = playWhenReady + + if currentItem?.status == .failed { + recreateAVPlayer() + } // Set item let currentAsset = AVURLAsset(url: url) @@ -201,6 +205,15 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { } } + /// Will recreate the AVPlayer instance. Used when the current one fails. + private func recreateAVPlayer() { + let player = AVPlayer() + playerObserver.player = player + playerTimeObserver.player = player + playerTimeObserver.registerForPeriodicTimeEvents() + avPlayer = player + } + } extension AVPlayerWrapper: AVPlayerObserverDelegate { From 46022ef0d6ef4045ba190460969917c190e18c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Henrichsen?= Date: Sat, 11 May 2019 17:56:43 +0200 Subject: [PATCH 4/7] Add event when AVPlayer is recreated. The AudioSession category will need to be set again when this event is emitted. --- Example/SwiftAudio/ViewController.swift | 5 +++++ Example/Tests/AVPlayerWrapperTests.swift | 4 ++++ SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift | 1 + .../Classes/AVPlayerWrapper/AVPlayerWrapperDelegate.swift | 1 + SwiftAudio/Classes/AudioPlayer.swift | 4 ++++ SwiftAudio/Classes/Event.swift | 8 ++++++++ 6 files changed, 23 insertions(+) diff --git a/Example/SwiftAudio/ViewController.swift b/Example/SwiftAudio/ViewController.swift index d301316..794fa11 100644 --- a/Example/SwiftAudio/ViewController.swift +++ b/Example/SwiftAudio/ViewController.swift @@ -31,6 +31,7 @@ class ViewController: UIViewController { controller.player.event.secondElapse.addListener(self, handleAudioPlayerSecondElapsed) controller.player.event.seek.addListener(self, handleAudioPlayerDidSeek) controller.player.event.updateDuration.addListener(self, handleAudioPlayerUpdateDuration) + controller.player.event.didRecreateAVPlayer.addListener(self, handleAVPlayerRecreated) } @IBAction func togglePlay(_ sender: Any) { @@ -116,4 +117,8 @@ class ViewController: UIViewController { } } + func handleAVPlayerRecreated() { + try? controller.audioSessionController.set(category: .playback) + } + } diff --git a/Example/Tests/AVPlayerWrapperTests.swift b/Example/Tests/AVPlayerWrapperTests.swift index 4b1a90f..8c306be 100644 --- a/Example/Tests/AVPlayerWrapperTests.swift +++ b/Example/Tests/AVPlayerWrapperTests.swift @@ -235,6 +235,10 @@ class AVPlayerWrapperTests: QuickSpec { } class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate { + func AVWrapperDidRecreateAVPlayer() { + + } + func AVWrapperItemDidPlayToEndTime() { } diff --git a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index cee469a..883955e 100755 --- a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -212,6 +212,7 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { playerTimeObserver.player = player playerTimeObserver.registerForPeriodicTimeEvents() avPlayer = player + delegate?.AVWrapperDidRecreateAVPlayer() } } diff --git a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapperDelegate.swift b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapperDelegate.swift index ee7bee8..14e82a2 100644 --- a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapperDelegate.swift +++ b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapperDelegate.swift @@ -16,5 +16,6 @@ protocol AVPlayerWrapperDelegate: class { func AVWrapper(seekTo seconds: Int, didFinish: Bool) func AVWrapper(didUpdateDuration duration: Double) func AVWrapperItemDidPlayToEndTime() + func AVWrapperDidRecreateAVPlayer() } diff --git a/SwiftAudio/Classes/AudioPlayer.swift b/SwiftAudio/Classes/AudioPlayer.swift index 0c5f18f..e132bdd 100755 --- a/SwiftAudio/Classes/AudioPlayer.swift +++ b/SwiftAudio/Classes/AudioPlayer.swift @@ -341,4 +341,8 @@ public class AudioPlayer: AVPlayerWrapperDelegate { self.event.playbackEnd.emit(data: .playedUntilEnd) } + func AVWrapperDidRecreateAVPlayer() { + self.event.didRecreateAVPlayer.emit(data: ()) + } + } diff --git a/SwiftAudio/Classes/Event.swift b/SwiftAudio/Classes/Event.swift index 99fd66c..abd1d00 100644 --- a/SwiftAudio/Classes/Event.swift +++ b/SwiftAudio/Classes/Event.swift @@ -15,6 +15,7 @@ extension AudioPlayer { public typealias FailEventData = (Error?) public typealias SeekEventData = (seconds: Int, didFinish: Bool) public typealias UpdateDurationEventData = (Double) + public typealias DidRecreateAVPlayerEventData = () public struct EventHolder { @@ -54,6 +55,13 @@ extension AudioPlayer { */ public let updateDuration: AudioPlayer.Event = AudioPlayer.Event() + /** + Emitted when the underlying AVPlayer instance is recreated. Recreation happens if the current player fails. + - Important: Remember to dispatch to the main queue if any UI is updated in the event handler. + - Note: It can be necessary to set the AVAudioSession's category again when this event is emitted. + */ + public let didRecreateAVPlayer: AudioPlayer.Event<()> = AudioPlayer.Event() + } public typealias EventClosure = (EventData) -> Void From addebef7f952e255b5524c9af0c1905c9c8a473a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Henrichsen?= Date: Sat, 11 May 2019 18:14:54 +0200 Subject: [PATCH 5/7] Add more detailed description on the fail event. --- SwiftAudio/Classes/Event.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SwiftAudio/Classes/Event.swift b/SwiftAudio/Classes/Event.swift index abd1d00..4caf52d 100644 --- a/SwiftAudio/Classes/Event.swift +++ b/SwiftAudio/Classes/Event.swift @@ -38,7 +38,8 @@ extension AudioPlayer { public let secondElapse: AudioPlayer.Event = AudioPlayer.Event() /** - Emitted when the player encounters an error. + Emitted when the player encounters an error. This will ultimately result in the AVPlayer instance to be recreated. + If this event is emitted, it means you will need to load a new item in some way. Calling play() will not resume playback. - Important: Remember to dispatch to the main queue if any UI is updated in the event handler. */ public let fail: AudioPlayer.Event = AudioPlayer.Event() From af4635d047d80c9b8b900964dff9f258b56ad211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Henrichsen?= Date: Sat, 11 May 2019 19:29:36 +0200 Subject: [PATCH 6/7] Remove AVPlayer parameter from init in both AudioPlayer and AVPlayerWrapper. --- Example/Tests/AVPlayerWrapperTests.swift | 7 +++---- SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift | 4 ++-- SwiftAudio/Classes/AudioPlayer.swift | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Example/Tests/AVPlayerWrapperTests.swift b/Example/Tests/AVPlayerWrapperTests.swift index 8c306be..aca8c1d 100644 --- a/Example/Tests/AVPlayerWrapperTests.swift +++ b/Example/Tests/AVPlayerWrapperTests.swift @@ -14,10 +14,9 @@ class AVPlayerWrapperTests: QuickSpec { var wrapper: AVPlayerWrapper! beforeEach { - let player = AVPlayer() - player.automaticallyWaitsToMinimizeStalling = false - player.volume = 0.0 - wrapper = AVPlayerWrapper(avPlayer: player) + wrapper = AVPlayerWrapper() + wrapper.automaticallyWaitsToMinimizeStalling = false + wrapper.volume = 0.0 wrapper.bufferDuration = 0.0001 } diff --git a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift index 883955e..915ba9e 100755 --- a/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift +++ b/SwiftAudio/Classes/AVPlayerWrapper/AVPlayerWrapper.swift @@ -46,8 +46,8 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol { } } - public init(avPlayer: AVPlayer = AVPlayer()) { - self.avPlayer = avPlayer + public init() { + self.avPlayer = AVPlayer() self.playerObserver = AVPlayerObserver() self.playerObserver.player = avPlayer self.playerTimeObserver = AVPlayerTimeObserver(periodicObserverTimeInterval: timeEventFrequency.getTime()) diff --git a/SwiftAudio/Classes/AudioPlayer.swift b/SwiftAudio/Classes/AudioPlayer.swift index e132bdd..bc9c9fd 100755 --- a/SwiftAudio/Classes/AudioPlayer.swift +++ b/SwiftAudio/Classes/AudioPlayer.swift @@ -127,10 +127,9 @@ public class AudioPlayer: AVPlayerWrapperDelegate { - parameter infoCenter: The InfoCenter to update. Default is `MPNowPlayingInfoCenter.default()`. */ - public init(avPlayer: AVPlayer = AVPlayer(), - nowPlayingInfoController: NowPlayingInfoControllerProtocol = NowPlayingInfoController(), + public init(nowPlayingInfoController: NowPlayingInfoControllerProtocol = NowPlayingInfoController(), remoteCommandController: RemoteCommandController = RemoteCommandController()) { - self._wrapper = AVPlayerWrapper(avPlayer: avPlayer) + self._wrapper = AVPlayerWrapper() self.nowPlayingInfoController = nowPlayingInfoController self.remoteCommandController = remoteCommandController From 92a804e9e4ade3829940dbdb2e1229033e5f0067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Henrichsen?= Date: Sat, 11 May 2019 19:30:59 +0200 Subject: [PATCH 7/7] Update Readme. Update podspec. Bump version to v0.9.0. --- README.md | 4 ++-- SwiftAudio.podspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3d5b8eb..985c12d 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,13 @@ SwiftAudio is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile: ```ruby -pod 'SwiftAudio', '~> 0.8.0' +pod 'SwiftAudio', '~> 0.9.0' ``` ### Carthage SwiftAudio supports [Carthage](https://github.com/Carthage/Carthage). Add this to your Cartfile: ```ruby -github "jorgenhenrichsen/SwiftAudio" ~> 0.8.0 +github "jorgenhenrichsen/SwiftAudio" ~> 0.9.0 ``` Then follow the rest of Carthage instructions on [adding a framework](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). diff --git a/SwiftAudio.podspec b/SwiftAudio.podspec index 163d24e..fec35d9 100644 --- a/SwiftAudio.podspec +++ b/SwiftAudio.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SwiftAudio' - s.version = '0.8.0' + s.version = '0.9.0' s.summary = 'Easy audio streaming for iOS' # This description is used to generate tags and improve search results.