CloudKit: Efficient way to get user's rank in leaderboard without fetching all records?

CloudKit: Efficient way to get user's rank in leaderboard without fetching all records?

I'm building a leaderboard feature using CloudKit's public database and need advice on the best approach to calculate a user's rank efficiently.

Current Setup

Record Structure:

  • Record Type: LeaderboardScore
  • Fields:
    • period (String): "daily", "weekly", "monthly", "allTime"
    • score (Int): User's score
    • profile (Reference): Link to user's profile
    • achievedAt (Date): Timestamp

Leaderboard Display:

  • Initially fetch first 15 users (sorted by score descending)
  • Paginate to load more as user scrolls
  • Show total player count
  • Show current user's rank (even if not in top 15)

The Challenge

I can fetch the first 15 users easily with a sorted query, but I need to display the current user's rank regardless of their position. For example:

  • User could be ranked #1 (in top 15) ✅ Easy
  • User could be ranked #247 (not in top 15) ❌ How to get this efficiently?

My Current Approach

Query records with scores higher than the user's score and count them:

// Count how many users scored higher
let predicate = NSPredicate(
    format: "period == %@ AND score > %d",
    period, userScore
)
// Rank = count + 1

Concerns

  • For 1000+ users with better scores, this requires multiple paginated queries
  • Even with desiredKeys: [], I'm concerned about performance and CloudKit request limits

Questions

  1. Is there a CloudKit API I'm missing that can efficiently count records matching a predicate without fetching all the records and paginating?

  2. Is this approach acceptable for a leaderboard with 1K-10K users? Does fetching with desiredKeys: [] help significantly with performance?

  3. Are there any optimizations I should consider to make this more efficient?

  4. What's the recommended approach for calculating user rank in CloudKit at this scale?

Current Scale

  • Expected: 1,000-10,000 active users per leaderboard period
  • Platform: iOS 17+, SwiftUI

Any guidance on best practices for leaderboards usecase in CloudKit would be greatly appreciated!

Answered by DTS Engineer in 880559022

The following flow should work:

  • Add a record field to LeaderboardScore to hold the curent user record ID.
  • Every time you generate a LeaderboardScore record, put the current user record ID there. You can get the information by calling fetchUserRecordID.
  • Use the current user record ID to fetch the LeaderboardScore record, and retrieve the rank of the current user from there.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

The following flow should work:

  • Add a record field to LeaderboardScore to hold the curent user record ID.
  • Every time you generate a LeaderboardScore record, put the current user record ID there. You can get the information by calling fetchUserRecordID.
  • Use the current user record ID to fetch the LeaderboardScore record, and retrieve the rank of the current user from there.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Thank you for the response! I think there may be a slight misunderstanding of my question.

I am already linking each LeaderboardScore record to a user via a reference field. My challenge is how to efficiently calculate and determine the user’s rank, since I am not storing a rank field in the records and instead compute it client-side.

Please see attached photo for visual reference.

Current Implementation

  1. Fetch LeaderboardScore records sorted by score (descending):
let query = CKQuery(recordType: "LeaderboardScore", predicate: predicate)
query.sortDescriptors = [NSSortDescriptor(key: "score", ascending: false)]
  1. Assign rank based on position in the sorted results:
entries = scores.enumerated().map { index, score in
    score.asLeaderboardEntry(
        rank: index + 1,  // Rank assigned client-side
        isCurrentUser: score.profile.id == currentProfileID
    )
}

This works well when the current user is within the fetched results (e.g., top 15–20). In that case, I can determine their rank directly from the local data.

However, if the user is ranked further down (e.g., #247), I would need to paginate through results until reaching their record to determine the rank. This does not seem efficient or scalable.

Core Question

Given this setup:

  • What is the recommended way to determine a user's rank when they are not within the initially fetched results?
  • Is there an efficient way to compute rank without paginating through all higher-scoring records?
  • Alternatively, is storing and maintaining a rank field the expected approach in CloudKit for this use case?

Additional Concern

If rank were to be stored, I'm unsure how it could be maintained efficiently. When a user submits a new score, assigning a rank would require knowing the relative position of all other users and potentially updating their ranks as well, which seems expensive and difficult to keep consistent.

Do you think this use case is not well-suited for CloudKit and would require a custom backend? I chose CloudKit to avoid building backend infrastructure, but I’m open to reconsidering that approach if it’s the recommended solution for implementing leaderboards with such features.

CloudKit: Efficient way to get user's rank in leaderboard without fetching all records?
 
 
Q