try! Swift 1日目 "Blending Cultures"の内容をサンプルアプリに起こしてみた

***(2016/03/22 追記)*** : Swift2.2でも問題なくプロジェクトを動かすことができます。

私はtry! Swiftに残念ながら3日とも行けないのですが、皆さんや有志の情報共有のおかげで、ぼちぼち情報を拾うことができてます！感謝です。。

1日目のプレゼンの中で、[Daniel H Steinberg](https://twitter.com/dimsumthinking)さんが発表した"Blending Cultures"のスライドに登場したコードを、
自分の学習を兼ねて、サンプルアプリとして起こしてみました。

- [Blending Cultures Example](https://github.com/sgr-ksmt/BlendingCulturesExample)

サンプル自体はプレゼンで披露されていたものと同様、とても簡単なもので、TableViewControllerで、セル(トランプ)を追加/削除/並び替え ができる、といったものです。
ただ、UITableViewControllerだけであれこれ書いてしまうと肥大化してしまうので、標準的なMVCの構成から、色々な手法(プロトコル指向とか、関数型プログラミングだとか)を用いて(blendして)、かなりクリーンに、それぞれを分離できるようにアプローチしています。
なので、それぞれのファイルのコードの記述量は __とても少ないです__ 。

実際のアプリ開発では他の動作、状態が複雑に絡んできますが、この例をベースに組めれば、MVVMベースだとしてもそれぞれにしっかり分離して見通しの良いコードになるんじゃないかなと思います。


外部ライブラリは使っていないので、クローンしてくればすぐに試すことが出来るかと思います。
ほとんど、スライドで発表されていたコード通りですが、

- Deckクラスの実装はせず、代わりにランダムでCardクラスを生成するようにしている
- Cardクラスが保持する、`rank`,`suit`は、なんとなく予想してenumで作成

といった点があるのでご注意ください。

あとは、生で講演を聞いておらず、下記の講演内容をブロクに書き起こしてくださった方のサイトを参考にしていますので、もし間違い等ありましたらお知らせください！
復習するときとかにでも使ってもらえればと思います!

## 追記(2016/03/06)
アップしたサンプルですが、 **トランプのマークが切り替わらない不具合** があったので、修正したものを *master* ブランチにpushしました。
もしcloneしたりした方が居ましたら、最新のものをpullしてください。

また、 *[original_approach](https://github.com/sgr-ksmt/BlendingCulturesExample/tree/original_approach)* ブランチにて、

- Handを返す関数に、`@warn_unused_result` を追加
- `conditionForDeleting`を追加し、フラグが立ってない時は削除できないように制御
- 手持ちのカードが1枚の時は並び替えができないように制御
- 追加、削除時の`rowAnimation`を指定できるように **SourceType** に *insertRowAnimationType/deleteRowAnimationType* を追加
- Deckクラスの追加

を行いました。そちらも併せて参考にしてもらえればと思います！

ちなみに、Deckクラスは、こんな感じで実装しています。
`Suit.allSuits`と`Rank.allRanks`は全てのマーク、番号を返すようにenum側に実装しています。

```swift
class Deck {
    private var cards: [Card]
    
    init() {
        self.cards = Suit.allSuits
            .map{ suit in
                Rank.allRanks.map { Card($0, suit) }
            }
            .flatMap { $0 }
    }
    
    var isEmpty: Bool {
        return cards.isEmpty
    }
    
    func nextCard() -> Card {
        cards = cards.sort { _ in arc4random() < arc4random() }
        return cards.removeLast()
    }
}
```

雑多ですが、`Rank`、`Suit`の実装も載せておきます。

```swift
private func randomValue(bound: Int) -> Int {
    return Int(arc4random_uniform(UInt32(bound)))
}

enum Rank: Int, CustomStringConvertible {
    case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
    
    static let numberOfRank = 13
    
    static func randomRank() -> Rank {
        return Rank(rawValue: randomValue(numberOfRank))!
    }
    
    static var allRanks: [Rank] {
        return (0..<numberOfRank).map { Rank(rawValue: $0)! }
    }
    
    var description: String {
        switch self {
        case .Ace: return "A"
        case .Jack: return "J"
        case .Queen: return "Q"
        case .King: return "K"
        default: return String(self.rawValue)
        }
    }
}

enum Suit: Int, CustomStringConvertible {
    case Heart, Diamonds, Clubs, Spade
    
    static let numberOfSuit = 4
    
    static func randomSuit() -> Suit {
        return Suit(rawValue: randomValue(numberOfSuit))!
    }
    
    static var allSuits: [Suit] {
        return (0..<numberOfSuit).map { Suit(rawValue: $0)! }
    }
    
    var description: String {
        switch self {
        case Heart: return "♥"
        case Diamonds: return "♦"
        case Clubs: return "♣"
        case Spade: return "♠"
        }
    }
}
```

## 参考

- [try! Swift 文化を調和させる #tryswiftconf Day1-8](http://niwatako.hatenablog.jp/entry/2016/03/02/160011)

本当に感謝です。ありがとうございます。

- [try! Swift](http://www.tryswiftconf.com)

----

もし本人がサンプルアプリを公開したり、問題がある場合は取り下げます...(その辺りよくわからなくて。。