Skip to content

Commit

Permalink
Adjusts behavior of next() and previous()
Browse files Browse the repository at this point in the history
Adjusts behavior of next() and previous() to match that of KotlinAudio

- (breaking) next and previous no longer throw errors
- (breaking) QueuedAudioPlayer#next(): when there is no next item, the last item in the queue (which is also the current item) is played again
- (breaking) QueuedAudioPlaye#previous()r: when there is no previous item, the first item in the queue (which is also the current item) is played again
- (breaking) QueueManager#previous(): when there is no previous item, the first item in the queue is returned
- (breaking) QueueManager#next(): when there is no next item, the last item in the queue is returned
- Adjust results of tests to match new behavior
  • Loading branch information
puckey committed Sep 13, 2022
1 parent 728d188 commit ea7f4d4
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 110 deletions.
4 changes: 2 additions & 2 deletions Example/SwiftAudio/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ class ViewController: UIViewController {
}

@IBAction func previous(_ sender: Any) {
try? controller.player.previous()
controller.player.previous()
}

@IBAction func next(_ sender: Any) {
try? controller.player.next()
controller.player.next()
}

@IBAction func startScrubbing(_ sender: UISlider) {
Expand Down
54 changes: 20 additions & 34 deletions Example/Tests/QueueManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class QueueManagerTests: QuickSpec {

context("calling next, causing currentIndex to become 1, then adding at index 1") {
beforeEach {
try! queue.next()
queue.next()
try! queue.add([5], at: queue.currentIndex)
}
it("should cause the current item to be shifted to index 2") {
Expand Down Expand Up @@ -155,38 +155,24 @@ class QueueManagerTests: QuickSpec {
}

context("then calling next") {
var error: Error?
var item: Int?
beforeEach {
do {
item = try queue.next()
}
catch let err {
error = err
}
item = queue.next()
}

it("should throw, because there was no currentItem") {
expect(error).toNot(beNil())
expect(item).to(beNil())
it("should noop") {
expect(item).to(equal(0))
}
}

context("then calling previous") {
var error: Error?
var item: Int?
beforeEach {
do {
item = try queue.previous()
}
catch let err {
error = err
}
item = queue.previous()
}

it("should throw, because there was no currentItem") {
expect(error).toNot(beNil())
expect(item).to(beNil())
it("should noop") {
expect(item).to(equal(0))
}
}

Expand All @@ -196,7 +182,7 @@ class QueueManagerTests: QuickSpec {

beforeEach {
try! queue.jump(to: 0)
nextIndex = try! queue.next(wrap: true)
nextIndex = queue.next(wrap: true)
}

it("should wrap to itself") {
Expand All @@ -210,7 +196,7 @@ class QueueManagerTests: QuickSpec {

beforeEach {
try! queue.jump(to: 0)
previousIndex = try? queue.previous(wrap: true)
previousIndex = queue.previous(wrap: true)
}

it("should wrap to itself") {
Expand Down Expand Up @@ -245,7 +231,7 @@ class QueueManagerTests: QuickSpec {
context("then calling next") {
var nextItem: Int?
beforeEach {
nextItem = try? queue.next()
nextItem = queue.next()
}

it("should return the next item") {
Expand All @@ -264,7 +250,7 @@ class QueueManagerTests: QuickSpec {
context("then calling previous") {
var index: Int?
beforeEach {
index = try? queue.previous()
index = queue.previous()
}
it("should return the first item") {
expect(index).to(equal(0))
Expand All @@ -275,16 +261,16 @@ class QueueManagerTests: QuickSpec {
context("then calling previous at the start of the queue") {
var index: Int?
beforeEach {
index = try? queue.previous()
index = queue.previous()
}
it("should return nil because an error was thrown") {
expect(index).to(beNil())
it("should noop and return the first item") {
expect(index).to(equal(0))
}
}
context("then calling previous(wrap: true)") {
var index: Int?
beforeEach {
index = try? queue.previous(wrap: true)
index = queue.previous(wrap: true)
}
it("should return the last item") {
expect(index).to(equal(queue.items.count - 1))
Expand All @@ -295,17 +281,17 @@ class QueueManagerTests: QuickSpec {
context("then calling next again at the end of the queue") {
var index: Int?
beforeEach {
index = try? queue.next()
index = queue.next()
}
it("should return nil because an error was thrown") {
expect(index).to(beNil())
it("should noop and return the last item") {
expect(index).to(equal(6))
}
}

context("then calling next(wrap: true)") {
var index: Int?
beforeEach {
index = try? queue.next(wrap: true)
index = queue.next(wrap: true)
}
it("should return the first item") {
expect(index).to(equal(0))
Expand Down Expand Up @@ -607,7 +593,7 @@ class QueueManagerTests: QuickSpec {
}
}

fcontext("moving to a too large index") {
context("moving to a too large index") {
var error: Error?
beforeEach {
do {
Expand Down
45 changes: 23 additions & 22 deletions Example/Tests/QueuedAudioPlayerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ class QueuedAudioPlayerTests: QuickSpec {

context("then calling next()") {
beforeEach {
try? audioPlayer.next()
audioPlayer.next()
}
it("should contain 0 items") {
expect(audioPlayer.nextItems.count).to(equal(0))
}

context("then calling previous()") {
beforeEach {
try? audioPlayer.previous()
audioPlayer.previous()
}
it("should contain 1 item") {
expect(audioPlayer.nextItems.count).to(equal(1))
Expand Down Expand Up @@ -191,7 +191,7 @@ class QueuedAudioPlayerTests: QuickSpec {

context("then calling next()") {
beforeEach {
try? audioPlayer.next()
audioPlayer.next()
}
it("should contain one item") {
expect(audioPlayer.previousItems.count).to(equal(1))
Expand Down Expand Up @@ -229,7 +229,7 @@ class QueuedAudioPlayerTests: QuickSpec {

context("then calling next()") {
beforeEach {
try? audioPlayer.next()
audioPlayer.next()
}

it("should go to next item and play") {
Expand All @@ -248,7 +248,7 @@ class QueuedAudioPlayerTests: QuickSpec {

context("then calling next()") {
beforeEach {
try? audioPlayer.next()
audioPlayer.next()
}

it("should go to next item and not play") {
Expand All @@ -264,12 +264,12 @@ class QueuedAudioPlayerTests: QuickSpec {
context("player was playing") {
beforeEach {
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()], playWhenReady: true)
try? audioPlayer.next()
audioPlayer.next()
}

context("then calling previous()") {
beforeEach {
try? audioPlayer.previous()
audioPlayer.previous()
}

it("should go to previous item and play") {
Expand All @@ -283,14 +283,14 @@ class QueuedAudioPlayerTests: QuickSpec {
context("player was paused") {
beforeEach {
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()])
try? audioPlayer.next()
audioPlayer.next()
audioPlayer.pause()

}

context("then calling previous()") {
beforeEach {
try? audioPlayer.previous()
audioPlayer.previous()
}

it("should go to previous item and not play") {
Expand Down Expand Up @@ -362,17 +362,18 @@ class QueuedAudioPlayerTests: QuickSpec {
let eventListener = CurrentItemEventListener()
audioPlayer.event.currentItem.addListener(eventListener, eventListener.handleEvent)

try? audioPlayer.next()
audioPlayer.next()
expect(audioPlayer.nextItems.count).toEventually(equal(0))
expect(audioPlayer.currentIndex).toEventually(equal(1))
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
expect(eventListener.lastIndex).toEventually(equal(1))
}

context("then calling next() again") {
it("should fail") {
try? audioPlayer.next()
expect(try audioPlayer.next()).to(throwError())
context("then calling next() twice") {
it("should noop") {
audioPlayer.next()
audioPlayer.next()
expect(audioPlayer.currentIndex).to(equal(1))
}
}
}
Expand Down Expand Up @@ -404,7 +405,7 @@ class QueuedAudioPlayerTests: QuickSpec {
let eventListener = CurrentItemEventListener()
audioPlayer.event.currentItem.addListener(eventListener, eventListener.handleEvent)

try? audioPlayer.next()
audioPlayer.next()
expect(audioPlayer.nextItems.count).to(equal(0))
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
expect(eventListener.lastIndex).toEventually(equal(1))
Expand Down Expand Up @@ -454,7 +455,7 @@ class QueuedAudioPlayerTests: QuickSpec {
let eventListener = CurrentItemEventListener()
audioPlayer.event.currentItem.addListener(eventListener, eventListener.handleEvent)

try? audioPlayer.next()
audioPlayer.next()
expect(audioPlayer.nextItems.count).to(equal(0))
expect(audioPlayer.currentIndex).to(equal(1))
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
Expand All @@ -463,14 +464,14 @@ class QueuedAudioPlayerTests: QuickSpec {

context("then calling next() again") {
beforeEach {
try? audioPlayer.next()
audioPlayer.next()
}

it("should move to first track and should play") {
let eventListener = CurrentItemEventListener()
audioPlayer.event.currentItem.addListener(eventListener, eventListener.handleEvent)

try? audioPlayer.next()
audioPlayer.next()
expect(audioPlayer.nextItems.count).to(equal(1))
expect(audioPlayer.currentIndex).to(equal(0))
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
Expand Down Expand Up @@ -506,9 +507,9 @@ class QueuedAudioPlayerTests: QuickSpec {
}

context("then calling next()") {
it("should fail") {
try? audioPlayer.next()
expect(try audioPlayer.next()).to(throwError())
it("should noop") {
audioPlayer.next()
expect(audioPlayer.currentIndex).to(equal(0))
}
}
}
Expand Down Expand Up @@ -574,7 +575,7 @@ class QueuedAudioPlayerTests: QuickSpec {

// workaround: seek not to beggining, for 0 expecations to correctly fail if necessary.
audioPlayer.seekWithExpectation(to: 0.05)
try? audioPlayer.next()
audioPlayer.next()
expect(audioPlayer.currentTime).toEventually(equal(0))
expect(audioPlayer.currentIndex).toEventually(equal(0))
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
Expand Down
2 changes: 0 additions & 2 deletions SwiftAudioEx/Classes/APError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ public struct APError {
}

enum QueueError: Error {
case noPreviousItem
case noNextItem
case noCurrentItem
case invalidIndex(index: Int, message: String)
case empty
Expand Down
47 changes: 18 additions & 29 deletions SwiftAudioEx/Classes/QueueManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,48 +118,37 @@ class QueueManager<T> {
case previous = -1
}

private func skip(direction: SkipDirection, wrap: Bool) throws -> T {
try throwIfQueueEmpty();
if (currentIndex == -1) {
throw APError.QueueError.noCurrentItem
}
var index = currentIndex + direction.rawValue
if (wrap) {
index = (items.count + index) % items.count;
}
guard items.count > index else {
throw APError.QueueError.noNextItem
}
guard index >= 0 else {
throw APError.QueueError.noPreviousItem
private func skip(direction: SkipDirection, wrap: Bool) -> T? {
if (items.count > 0) {
var index = currentIndex + direction.rawValue
if (wrap) {
index = (items.count + index) % items.count;
}
currentIndex = max(0, min(items.count - 1, index))
updateCurrentItem()
}
currentIndex = index
updateCurrentItem()
return current!
return current
}

/**
Get the next item in the queue, if there are any.
Will update the current item.
- throws: `APError.QueueError`
- returns: The next item.
Makes the next item in the queue active, or the last item when already at the end of the queue. When wrap is true and at the end of the queue, the first track in the queue is made active.
- parameter wrap: Whether to wrap to the start of the queue
- returns: The next (or current) item.
*/
@discardableResult
public func next(wrap: Bool = false) throws -> T {
return try skip(direction: SkipDirection.next, wrap: wrap);
public func next(wrap: Bool = false) -> T? {
return skip(direction: SkipDirection.next, wrap: wrap);
}

/**
Get the previous item in the queue, if there are any.
Will update the current item.
Makes the previous item in the queue active, or the first item when already at the start of the queue. When wrap is true and at the start of the queue, the last track in the queue is made active.
- throws: `APError.QueueError`
- parameter wrap: Whether to wrap to the end of the queue
- returns: The previous item.
*/
@discardableResult
public func previous(wrap: Bool = false) throws -> T {
return try skip(direction: SkipDirection.previous, wrap: wrap);
public func previous(wrap: Bool = false) -> T? {
return skip(direction: SkipDirection.previous, wrap: wrap);
}

/**
Expand Down
Loading

0 comments on commit ea7f4d4

Please sign in to comment.