MongoDB Stitch iOS(Swift):如何通过skip实现分页?
Answer
Great question! I’ve dealt with this exact issue when building pagination with MongoDB Stitch on iOS using Swift, so I can help break this down for you.
First, you’re absolutely right: the RemoteFindOptions in the Stitch iOS SDK doesn’t include a direct skip parameter. This isn’t an oversight though—MongoDB actually recommends keyset pagination (cursor-based pagination) over traditional skip/limit for better performance, especially with large datasets (since skip forces the database to scan and discard all the skipped documents, which gets slow as the page number increases).
Option 1: Use Cursor-Based Pagination (Recommended)
This approach relies on filtering documents based on the last item from the previous page, paired with a stable sort order. Here’s how to implement it:
- Choose a stable sort field: Use a unique, ordered field like
_id(MongoDB’s built-in unique ID, which is time-ordered) or a custom field likecreatedAt. - Fetch the first page: Use
limitto get your page size, then store the last document’s sort field value. - Fetch subsequent pages: Add a filter to only retrieve documents that come after (or before, depending on sort direction) the last item from the previous page, then apply the same
limit.
Swift Code Example
Suppose we’re paginating a Posts collection sorted by createdAt in descending order:
// Track the last document's createdAt value from the previous page private var lastPageEndDate: Date? // Fetch the first page of posts func fetchFirstPage() { guard let client = Stitch.defaultAppClient?.serviceClient( fromFactory: remoteMongoClientFactory, withName: "mongodb-atlas" ) else { return } let postsCollection = client.db("your-database-name").collection("Posts") let findOptions = RemoteFindOptions( sort: ["createdAt": -1], // Sort newest to oldest limit: 20 // 20 items per page ) postsCollection.find([], options: findOptions) { result in switch result { case .success(let documents): // Update the last page's end date with the last document's createdAt self.lastPageEndDate = documents.last?["createdAt"] as? Date // Handle your post data here case .failure(let error): print("Failed to fetch first page: \(error.localizedDescription)") } } } // Fetch the next page of posts func fetchNextPage() { guard let lastDate = lastPageEndDate, let client = Stitch.defaultAppClient?.serviceClient( fromFactory: remoteMongoClientFactory, withName: "mongodb-atlas" ) else { return } let postsCollection = client.db("your-database-name").collection("Posts") // Filter for posts older than the last one from the previous page (since we're sorting descending) let paginationFilter: Document = ["createdAt": ["$lt": lastDate]] let findOptions = RemoteFindOptions( sort: ["createdAt": -1], limit: 20 ) postsCollection.find(paginationFilter, options: findOptions) { result in switch result { case .success(let documents): if let lastDoc = documents.last { self.lastPageEndDate = lastDoc["createdAt"] as? Date } else { // No more pages left self.lastPageEndDate = nil } // Handle your post data here case .failure(let error): print("Failed to fetch next page: \(error.localizedDescription)") } } }
Option 2: Use a Stitch Function for skip (If You Must)
If you absolutely need to use skip (e.g., for a small dataset where performance isn’t a concern), you can create a Stitch Server Function that supports the skip parameter, then call that function from your iOS app.
Step 1: Create the Stitch Function
In your MongoDB Stitch dashboard, add a new function with this code:
exports = function(skipAmount, pageLimit) { const db = context.services.get("mongodb-atlas").db("your-database-name"); const postsCollection = db.collection("Posts"); // Use skip and limit directly in the query return postsCollection.find({}) .sort({ createdAt: -1 }) .skip(skipAmount) .limit(pageLimit) .toArray(); };
Step 2: Call the Function from iOS
func fetchPageWithSkip(skip: Int, limit: Int) { guard let stitchClient = Stitch.defaultAppClient else { return } stitchClient.callFunction( withName: "fetchPaginatedPosts", // Match your function name from Stitch arguments: [skip, limit] ) { result in switch result { case .success(let documents): // Handle the returned paginated documents case .failure(let error): print("Failed to call pagination function: \(error.localizedDescription)") } } }
Final Notes
- Stick with cursor-based pagination whenever possible—it’s faster, more reliable, and avoids issues with duplicate/missing documents if data changes between pages.
- The
skipapproach works but isn’t recommended for large datasets due to performance overhead.
内容的提问来源于stack exchange,提问作者Koh

