NCMBのSwift SDKを使ったランキング機能に日付絞り込み機能を追加する

先日、[NCMBのSwift SDKを使ってゲームのランキング機能を実装する - Qiita](https://qiita.com/goofmint/items/46a6ebdc3ad7a73c0ad9)という記事をアップしましたが、その後こんなツイートを拝見しました。

https://twitter.com/tadapy88/status/1432755009205063682

ランキングを時間軸で絞り込むことで、今月のランキングや先週のランキングなどを実現します。確かに、圧倒的な順位の人が君臨してしまうとやる気をなくしそうですが、週間ランキングなどがあれば順位変動が激しくて良さそうです。

ということで、Swift SDKを使ったランキング機能にも、過去30日と7日間でランキングを絞り込めるようにしてみました。この実装は[NCMBのSwift SDKを使ってゲームのランキング機能を実装する - Qiita](https://qiita.com/goofmint/items/46a6ebdc3ad7a73c0ad9)を前提としています。また、コードは[GitHub - NCMBMania/Swift_Ranking_Demo: Swift SDKを使って実装したランキング機能のデモです](https://github.com/NCMBMania/Swift_Ranking_Demo)にアップしてあります。

![ScreenShot_ 2021-09-07 15.18.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/e52a32b2-f76c-0280-b242-4af4f248c6f8.png)

## 変更点について

まずEnumで時間のレンジを定義しています。

```swift
enum RankingRange {
    case All, Month, Week
}
```

そしてボタンを追加して、それぞれレンジを引数に送るようにします。

```swift
Button("過去30日", action: {
    getRanking(range: RankingRange.Month)
})
Button("過去7日", action: {
    getRanking(range: RankingRange.Week)
})
```


```swift
if rankings.count > 0 {
    List {
        ForEach(Array(rankings.enumerated()), id: \.offset) { i, ranking in
            Text(listString(index: i, ranking: ranking))
        }
    }
}
```

```swift
func listString(index: Int, ranking: NCMBObject) -> String {
    let displayName: String = ranking["displayName"] ?? ""
    let score: Int = ranking["score"] ?? 0
    return "\(index + 1)位 \(displayName)さん (\(score)点)"
}
```

## ランキングの取得

ランキングの取得時には、指定されたレンジに応じて日時オブジェクトを作成しています。

```swift
func getRanking(range: RankingRange = .All) {
    var date: Date?
    let calendar = Calendar.current
    let today = Date()
    
    switch range {
    case .Month:
        date = calendar.date(byAdding: .day, value: -30, to: today)
        break
    case .Week:
        date = calendar.date(byAdding: .day, value: -7, to: today)
        break
    default:
        break
    }
    // 下に続く
```

そして、日付が指定されている場合のみ `greaterThanOrEqualTo` を使って指定された日付以降のデータだけを対象とする絞り込みを行っています。

```swift
    // ランキングデータ検索用のクエリオブジェクトを作成
    var query = NCMBQuery.getQuery(className: "Ranking")
    // 並び順はスコアの高い順です
    query.order = ["-score"]
    // 100件のデータを取得します
    query.limit = 100
    // 日付が指定されていれば、その日以降のデータに限定する
    if date != nil {
        query.where(field: "createDate", greaterThanOrEqualTo: date!)
    }
    // 検索実行
    let results = query.find()
    switch results {
    case let .success(ary):
        // 検索成功したら、それをrankingsに適用します
        rankings = ary
    case .failure(_): break
    }
}
```

## 表示時の注意

ランキングの内容が変わるので、 `rankings.indice` だとうまく反映されませんでした（最初に10件表示していると、次に2件に変わった時にも最初にi = 10になってしまう）。そこで、以下のようにForEachの内容を変えています。

```swift
// 元
List {
  ForEach(rankings.indices) { i in
      Text(listString(index: i))
  }
}

// 変更後
List {
    ForEach(Array(rankings.enumerated()), id: \.offset) { i, ranking in
        Text(listString(index: i, ranking: ranking))
    }
}
```

これに伴ってlistStringも多少変わっています。

```swift
// ランキングの順位、ゲームユーザ名、スコアを返します
func listString(index: Int, ranking: NCMBObject) -> String {
    let displayName: String = ranking["displayName"] ?? ""
    let score: Int = ranking["score"] ?? 0
    return "\(index + 1)位 \(displayName)さん (\(score)点)"
}
```

## まとめ

NCMBの以上、以下と言った検索のオペランドを使うことで、指定日以降のランキングを作ったり、先月だけのランキングを作ると言ったことも簡単に実現できます。ぜひSwiftを使ったゲーム開発に活かしてください。

[ドキュメント : 開発者向けドキュメント | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/#/Swift)