Developing a Wishlist in a Mobile App
Imagine a user added 10 products to favorites on one device, then opened the app on another a week later — the list is empty. This is a classic sync problem where local storage is not tied to the server. According to statistics, 73% of users do not return to the app after such data loss. Even worse if a guest transitions to authorized: their 10 items vanish, and merge is not implemented. We solve this with hybrid storage: MMKV for offline access and server for sync. With over 7 years in mobile development and over 50 projects with a favorites feature, we guarantee stable operation under load up to 1000 concurrent requests. Investing in a wishlist pays off by increasing conversion to cart by 15–20%. Contact us to assess your project — we analyze requirements and propose the optimal solution.
Choosing Storage for Favorites: Local or Cloud?
The choice depends on whether cross-device sync is needed. Compare the main options:
| Storage | Read Speed | Sync | Offline Access | Implementation Complexity |
|---|---|---|---|---|
| Local (MMKV) | <0.1 ms | No | Yes (always) | Low |
| Cloud (Firestore) | 10-50 ms | Yes | Limited | Medium |
| Hybrid (local cache + server) | <0.1 ms locally | Yes | Yes | High (merge) |
For most projects, the hybrid approach is optimal. For authorized users only: Firestore, PostgreSQL, any server DB. List tied to userId. Guest users + sync upon registration: MMKV for guests, on login — merge with server. The merge variant is more complex. At registration, a guest might have added 10 items, while their new account already has 5 (imported from another service). Merge strategy: union of two sets, no duplicates per productId. We use this scheme in 80% of projects.
Why Optimistic UI Matters?
The wishlist add button should react instantly — without waiting for server response. A classic mistake: show loading on press and block the button during the request. The user sees a delay and thinks the tap didn't register. Our solution:
const useWishlist = () => { const [wishlistIds, setWishlistIds] = useAtom(wishlistAtom); const toggle = useCallback(async (productId: string) => { const isAdding = !wishlistIds.has(productId); // Immediate UI change setWishlistIds(prev => { const next = new Set(prev); isAdding ? next.add(productId) : next.delete(productId); return next; }); try { if (isAdding) { await api.wishlist.add(productId); } else { await api.wishlist.remove(productId); } } catch { // Rollback on error setWishlistIds(prev => { const next = new Set(prev); isAdding ? next.delete(productId) : next.add(productId); return next; }); Toast.show('Failed to update favorites'); } }, [wishlistIds]); return { wishlistIds, toggle }; }; This approach delivers responsiveness and prevents data loss during network failures. According to App Store Review Guidelines (Section 4.2), minimal functionality should include content personalization, and a wishlist is one such element.
How to Implement Merge on User Registration?
On registration, the guest wishlist (local) merges with the server one. We use union-merge by productId: if an item exists in either list, it appears in the result. This ensures the user loses no items. Algorithm:
- Get guest list from MMKV.
- Get server list by
userId. - Union sets: all unique
productId. - Save to server, clear local cache.
We use this strategy on every project with guest functionality. You save up to 40% of development time, which equates to substantial budget savings.
MMKV for Local Cache
For the wishlist, which is read on every product card render, we use MMKV — synchronous read without await. Comparison with AsyncStorage:
| Parameter | MMKV | AsyncStorage |
|---|---|---|
| Read Speed | <0.1 ms | 1-5 ms |
| Synchronous access | Yes | No |
| Thread Safety | Yes | No |
Code for working with MMKV:
import { MMKV } from 'react-native-mmkv'; const storage = new MMKV({ id: 'wishlist' }); const getLocalWishlist = (): Set<string> => { const raw = storage.getString('ids'); return raw ? new Set(JSON.parse(raw)) : new Set(); }; const saveLocalWishlist = (ids: Set<string>) => { storage.set('ids', JSON.stringify([...ids])); }; Synchronous MMKV read on the main thread is safe; the operation takes <0.1 ms. That is 10 times faster than AsyncStorage.
Wishlist Badge Counter
The badge showing the number of wishlist items is a derivative of wishlistIds.size. Do not make a separate request for the counter. If the wishlist is synced, the set size is already known. We guarantee the counter updates instantly without extra requests.
Typical Mistakes in Wishlist Development
- Blocking the add button — user cannot add multiple items quickly. Solution: optimistic UI with a queue.
- Data loss during offline operations — if a user adds an item without internet and then closes the app. Solution: save to MMKV and sync when connected.
- No merge on registration — guest loses items after authorization. Solution: union-merge by productId.
- Duplicate push notifications — discount pushes sent per item individually. Solution: group changes, send one notification with count.
What's Included in Wishlist Development
- Analysis and data schema design (guest/authorized, merge strategy)
- API implementation (REST/GraphQL) or Firestore setup
- UI components (button, list, badge) with optimistic updates
- Local caching with MMKV and synchronization
- Push notifications (APNs/FCM) for price or availability changes
- Testing (unit, UI, load up to 1000 requests)
- Code documentation and deploy instructions
- Post-launch support (2 weeks free)
How We Work on the Wishlist
- Analysis — study requirements, determine data volume and usage scenarios.
- Design — choose stack (Firestore/PostgreSQL, MMKV), draw sync schema.
- Implementation — write code with optimistic UI and caching.
- Testing — check offline, concurrent access, load.
- Deploy — publish to App Store/Google Play, set up monitoring.
Estimation
Wishlist with optimistic UI, MMKV cache, and cloud sync (Firestore or REST): 1–2 weeks. Timelines may increase for complex merge schemes or offline queue support. Order wishlist development and get stable synchronization without data loss — we'll find the optimal solution for your project.







