# Advanced Usage

* [Animations](#animations)
* [Inserting and Deleting Cards](#inserting-and-deleting-cards)
* [Swipe Recognition](#swipe-recognition)

## Animations
// TODO

## Inserting and Deleting Cards

If you're using an external API to retrieve your `SwipeCard` data models, chances are you'll need to update the card stack occasionally as new models come in. As of version [0.4.0](https://github.com/mac-gallagher/Shuffle/releases/tag/v0.4.0), Shuffle includes the following methods on `SwipeCardStack`:

```swift
func insertCard(atIndex index: Int, position: Int)
func appendCards(atIndices indices: [Int]) // Index refers to the index of the card in the data source
```
```swift
func deleteCards(atIndices indices: [Int])
func deleteCards(atPositions positions: [Int]) // Position refers to the position of the card in the stack
```

Using the insert methods in particular, we can give the illusion of an "infinite" card stack. Let's look at an example.

### External API Example
Suppose we have a utility which fetches raw data models and decodes them into an array of `CardModels`:

```swift
struct NetworkUtility {
  static func fetchNewCardModels(@escaping completion: ([CardModel]) -> ()) {
    // Decode network models into array of CardModels and return result in completion block
  }
}
```

The following view controller displays a `SwipeCardStack` and holds a reference to the card models. In this example, the new models are fetched and added to the card stack after every 10 swipes:

```swift
class ViewController: UIViewController: SwipeCardStackDataSource, SwipeCardStackDelegate {

  let cardStack = SwipeCardStack()
  var cardModels: [CardModel]
  var swipedCount: Int = 0
	
  func viewDidLoad() {
    super.viewDidLoad()
    cardStack.dataSource = self
    cardStack.delegate = self
    
    // Layout cardStack on view
    
    addCards()
  }
	
  // MARK: SwipeCardStackDataSource
	
  func numberOfCards(in cardStack: SwipeCardStack) -> Int {
    return cardModels.count
  }
	
  func cardStack(_ cardStack: SwipeCardStack, cardForIndexAt index: Int) -> SwipeCard {
    let card = SwipeCard()
    card.model = cardModels[index]
    return card
  }
	
  // MARK: SwipeCardStackDelegate
	
  func didSwipeCard(atIndex index: Int) {
    swipedCount += 1
    if swipedCount % 10 == 0 {
      addCards()
    }
  }
	
  private func addCards() {
    NetworkUtility.fetchNewCardModels { [weak self] newModels in
      guard let strongSelf = self else { return }
        
      let oldModelsCount = strongSelf.cardModels.count
      let newModelsCount = oldModelCount + newModels.count
        
      DispatchQueue.main.async {
        strongSelf.cardModels.append(contentsOf: newModels)
          
        let newIndices = Array(oldModelsCount..<newModelsCount)
        strongSelf.cardStack.appendCards(atIndices: newIndices)
      }
    }
  }
}
```

Here, we use the `appendCards:` method to append the new cards to the bottom of the card stack. If instead we wanted to add the cards to the top of the stack, we could do something like

```swift
NetworkUtility.fetchNewCardModels { [weak self] newModels in
  guard let strongSelf = self else { return }
	
  DispatchQueue.main.async {
    // Insert in reverse order so that newModels.first is the model for the topmost card
    for model in newModels.reversed() {
      strongSelf.cardModels.insert(model, at: 0)
      strongSelf.cardStack.insertCard(atIndex: 0, position: 0)
    }
  }
}
```

### Common pitfalls
If you are familar with the common pitfalls of the insert/delete row methods on `UITableView`, the exact same pitfalls apply here. There is, however, one crucial difference between the two insert methods:

```swift
// UITableView
func insertRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation)
```

```swift
// SwipeCardStack
func insertCard(atIndex: Int, position: Int)
```

The `insertCard` method has an additional `position` parameter which represents the inserted position in the card stack, whereas the `index` parameter represents the index of the card/model in the data source. The `position` is dynamic based on the number of cards remaining (not swiped) in the stack. **Note**: For a `UITableView`, the `indexPath` and `index/position` parameters are equavalent.

Since the number of remaining cards is subject to change, be sure to calculate the `position` based on the value returned from the `numberOfRemainingCards:` method on `SwipeCardStack`.

## Swipe Recognition
// TODO