Skip to content

Commit

Permalink
add deleteAtIndex method (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
mac-gallagher authored Jul 12, 2020
1 parent 58cbf76 commit 9a21773
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 180 deletions.
4 changes: 2 additions & 2 deletions Shuffle-iOS.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "Shuffle-iOS"
s.version = "0.3.1"
s.version = "0.3.2"
s.platform = :ios, "9.0"
s.summary = "A multi-directional card swiping library inspired by Tinder"

Expand All @@ -13,7 +13,7 @@ s.homepage = "https://github.com/mac-gallagher/Shuffle"
s.documentation_url = "https://github.com/mac-gallagher/Shuffle/tree/master/README.md"
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { "Mac Gallagher" => "[email protected]" }
s.source = { :git => "https://github.com/mac-gallagher/Shuffle.git", :tag => "v0.3.1" }
s.source = { :git => "https://github.com/mac-gallagher/Shuffle.git", :tag => "v0.3.2" }

s.swift_version = "5.0"
s.source_files = "Sources/**/*.{h,swift}"
Expand Down
4 changes: 4 additions & 0 deletions Shuffle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
ADEE86E822ADF3C200AAE7A1 /* CardStackLayoutProviderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADEE86E722ADF3C200AAE7A1 /* CardStackLayoutProviderSpec.swift */; };
ADFAAC0A23FA66540084323B /* CardStackStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADFAAC0923FA66540084323B /* CardStackStateManager.swift */; };
ADFAAC0C23FB75D30084323B /* MockCardStackStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADFAAC0B23FB75D30084323B /* MockCardStackStateManager.swift */; };
ADFDBA0524B9B94C0069E256 /* SwipeCardStackSpec_StateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADFDBA0424B9B94C0069E256 /* SwipeCardStackSpec_StateManager.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -203,6 +204,7 @@
ADEE86E722ADF3C200AAE7A1 /* CardStackLayoutProviderSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardStackLayoutProviderSpec.swift; sourceTree = "<group>"; };
ADFAAC0923FA66540084323B /* CardStackStateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardStackStateManager.swift; sourceTree = "<group>"; };
ADFAAC0B23FB75D30084323B /* MockCardStackStateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCardStackStateManager.swift; sourceTree = "<group>"; };
ADFDBA0424B9B94C0069E256 /* SwipeCardStackSpec_StateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeCardStackSpec_StateManager.swift; sourceTree = "<group>"; };
CF2ADEA1F05DDE3996336C1F /* Pods-ShuffleExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShuffleExample.release.xcconfig"; path = "Target Support Files/Pods-ShuffleExample/Pods-ShuffleExample.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -507,6 +509,7 @@
AD4170A724034D4200BD40C8 /* CardStackStateManagerSpec.swift */,
ADEE86DE22ADED3600AAE7A1 /* SwipeCardStackSpec_Base.swift */,
AD9F44BB2468D04600725A8D /* SwipeCardStackSpec_MainMethods.swift */,
ADFDBA0424B9B94C0069E256 /* SwipeCardStackSpec_StateManager.swift */,
AD9F44BD2468D08B00725A8D /* SwipeCardStackSpec_SwipeCardDelegate.swift */,
);
path = Specs;
Expand Down Expand Up @@ -796,6 +799,7 @@
AD439DF92285FB7E009DE2F7 /* TestableSwipeView.swift in Sources */,
AD4170A824034D4200BD40C8 /* CardStackStateManagerSpec.swift in Sources */,
ADC3622D2466B23E00FB9F1C /* SwipeCardSpec_Base.swift in Sources */,
ADFDBA0524B9B94C0069E256 /* SwipeCardStackSpec_StateManager.swift in Sources */,
AD9F44BC2468D04600725A8D /* SwipeCardStackSpec_MainMethods.swift in Sources */,
AD52EF8B22E532B90063AE5D /* TestableNotificationCenter.swift in Sources */,
AD1098382287CAD4008CB197 /* TestableSwipeCard.swift in Sources */,
Expand Down
42 changes: 24 additions & 18 deletions Sources/Shuffle/SwipeCardStack/CardStackStateManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ protocol CardStackStateManagable {
var totalIndexCount: Int { get }

func insert(_ index: Int, at position: Int)
func delete(_ index: Int)

func swipe(_ direction: SwipeDirection)
func undoSwipe() -> Swipe?
Expand All @@ -57,29 +58,34 @@ class CardStackStateManager: CardStackStateManagable {
}

func insert(_ index: Int, at position: Int) {
if position < 0 {
fatalError("Attempt to insert card at position \(position)")
}
precondition(index >= 0, "Attempt to insert card at index \(index)")
//swiftlint:disable:next line_length
precondition(index <= totalIndexCount, "Attempt to insert card at index \(index), but there are only \(totalIndexCount + 1) cards after the update")
precondition(position >= 0, "Attempt to insert card at position \(position)")
//swiftlint:disable:next line_length
precondition(position <= remainingIndices.count, "Attempt to insert card at position \(position), but there are only \(remainingIndices.count + 1) cards remaining in the stack after the update")

// Increment all stored indices greater than or equal to index by 1
remainingIndices = remainingIndices.map { $0 >= index ? $0 + 1 : $0 }
swipes = swipes.map { $0.index >= index ? Swipe($0.index + 1, $0.direction) : $0 }

if position > remainingIndices.count {
//swiftlint:disable:next line_length
fatalError("Attempt to insert card at position \(position), but there are only \(remainingIndices.count + 1) cards remaining in the stack after the update")
}
remainingIndices.insert(index, at: position)
}

if index < 0 {
fatalError("Attempt to insert card at data source index \(index)")
}
func delete(_ index: Int) {
precondition(index >= 0, "Attempt to delete card at index \(index)")
//swiftlint:disable:next line_length
precondition(index < totalIndexCount, "Attempt to delete card at index \(index), but there are only \(totalIndexCount) cards before the update")

if index > totalIndexCount {
//swiftlint:disable:next line_length
fatalError("Attempt to insert card at index \(index), but there are only \(totalIndexCount + 1) cards after the update")
}
swipes.removeAll { return $0.index == index }

// Increment all stored indices in the range [0, index] by 1
remainingIndices = remainingIndices.map { $0 >= index ? $0 + 1 : $0 }
swipes = swipes.map { $0.index >= index ? Swipe($0.index + 1, $0.direction) : $0 }
if let position = remainingIndices.firstIndex(of: index) {
remainingIndices.remove(at: position)
}

remainingIndices.insert(index, at: position)
// Decrement all stored indices greater than or equal to index by 1
remainingIndices = remainingIndices.map { $0 >= index ? $0 - 1 : $0 }
swipes = swipes.map { $0.index >= index ? Swipe($0.index - 1, $0.direction) : $0 }
}

func swipe(_ direction: SwipeDirection) {
Expand Down
22 changes: 21 additions & 1 deletion Sources/Shuffle/SwipeCardStack/SwipeCardStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegat

private var animator: CardStackAnimatable = CardStackAnimator.shared
private var layoutProvider: CardStackLayoutProvidable = CardStackLayoutProvider.shared
private var notificationCenter = NotificationCenter.default
private var notificationCenter = NotificationCenter()
private var stateManager: CardStackStateManagable = CardStackStateManager()
private var transformProvider: CardStackTransformProvidable = CardStackTransformProvider.shared

Expand Down Expand Up @@ -351,6 +351,26 @@ open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegat
reloadVisibleCards()
}

/// Deletes the card at the specified index. Removes swipes
/// - Parameter index: The index of the card in the data source
public func deleteCard(atIndex index: Int) {
guard let dataSource = dataSource else { return }

let oldNumberOfCards = stateManager.totalIndexCount
let newNumberOfCards = dataSource.numberOfCards(in: self)

stateManager.delete(index)

if newNumberOfCards != oldNumberOfCards - 1 {
let errorString = StringUtils.createInvalidUpdateErrorString(newCount: newNumberOfCards,
oldCount: oldNumberOfCards,
deletedCount: 1)
fatalError(errorString)
}

reloadVisibleCards()
}

func reloadVisibleCards() {
visibleCards.forEach { $0.card.removeFromSuperview() }
visibleCards.removeAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ class MockCardStackStateManager: CardStackStateManagable {
insertPositions.append(position)
}

var deleteCalled: Bool = false
var deleteIndex: Int?

func delete(_ index: Int) {
deleteCalled = true
deleteIndex = index
}

var swipeCalled = false
var swipeDirection: SwipeDirection?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class CardStackStateManagerSpec: QuickSpec {
}

it("should throw a fatal error") {
expect(subject.insert(4, at: position)).to(throwAssertion())
expect(subject.insert(0, at: position)).to(throwAssertion())
}
}

Expand All @@ -73,7 +73,7 @@ class CardStackStateManagerSpec: QuickSpec {
}

it("should throw a fatal error") {
expect(subject.insert(4, at: position)).to(throwAssertion())
expect(subject.insert(0, at: position)).to(throwAssertion())
}
}

Expand Down Expand Up @@ -118,7 +118,7 @@ class CardStackStateManagerSpec: QuickSpec {
expect(subject.remainingIndices[position]) == index
}

it("should increment all stored indices greater than index by one") {
it("should increment all stored indices greater than or equal to index by one") {
expect(subject.remainingIndices[3]) == oldRemainingIndices[2] + 1
expect(subject.remainingIndices[4]) == oldRemainingIndices[3] + 1
expect(subject.swipes[1].index) == oldSwipes[1].index + 1
Expand All @@ -127,6 +127,81 @@ class CardStackStateManagerSpec: QuickSpec {
}
}

// MARK: - Delete

describe("When calling delete") {
context("and index is less than zero") {
let index: Int = -1

it("should throw a fatal error") {
expect(subject.delete(index)).to(throwAssertion())
}
}

context("and index is greater than the number of remaining indices - 1") {
let index: Int = 3

beforeEach {
subject.remainingIndices = [1, 2, 3]
}

it("should throw a fatal error") {
expect(subject.delete(index)).to(throwAssertion())
}
}

context("and index is at least zero and at most the number of remaining indices - 1") {
let oldRemainingIndices: [Int] = [3, 2, 5, 6, 0]
let oldSwipes = [Swipe(1, .left), Swipe(4, .left), Swipe(7, .left)]

beforeEach {
subject.remainingIndices = oldRemainingIndices
subject.swipes = oldSwipes
}

context("and the index has already been swiped") {
let index: Int = 4

beforeEach {
subject.delete(index)
}

it("should remove any swipes with the index") {
expect(subject.swipes.contains { $0.index == index }) == false
expect(subject.swipes.count) == oldSwipes.count - 1
}

it("should decrement all stored indices greater than or equal to index by one") {
expect(subject.remainingIndices[2]) == oldRemainingIndices[2] - 1
expect(subject.remainingIndices[3]) == oldRemainingIndices[3] - 1
expect(subject.swipes[1].index) == oldSwipes[2].index - 1
}
}

context("and the index has not been swiped") {
let index: Int = 2

beforeEach {
subject.delete(index)
}

it("should remove the index from remainingIndiecs") {
expect(subject.remainingIndices.count) == oldRemainingIndices.count - 1
}

it("should decrement all stored indices greater than or equal to index by one") {
expect(subject.remainingIndices[0]) == oldRemainingIndices[0] - 1
expect(subject.remainingIndices[1]) == oldRemainingIndices[2] - 1
expect(subject.remainingIndices[2]) == oldRemainingIndices[3] - 1
expect(subject.swipes[1].index) == oldSwipes[1].index - 1
expect(subject.swipes[2].index) == oldSwipes[2].index - 1
}
}
}
}

// MARK: - Delete At Position

// MARK: - Swipe

describe("When calling swipe") {
Expand Down
Loading

0 comments on commit 9a21773

Please sign in to comment.