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 scoreprofile(Reference): Link to user's profileachievedAt(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
-
Is there a CloudKit API I'm missing that can efficiently count records matching a predicate without fetching all the records and paginating?
-
Is this approach acceptable for a leaderboard with 1K-10K users? Does fetching with
desiredKeys: []help significantly with performance? -
Are there any optimizations I should consider to make this more efficient?
-
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!
The following flow should work:
- Add a record field to
LeaderboardScoreto hold the curent user record ID. - Every time you generate a
LeaderboardScorerecord, put the current user record ID there. You can get the information by calling fetchUserRecordID. - Use the current user record ID to fetch the
LeaderboardScorerecord, and retrieve the rank of the current user from there.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.