Developing CMS for Mobile App Content
Mobile app with hardcoded content — constant releases for text fixes. Change promotional banner, update prices, add new category — every time App Store and Google Play review, 24-72 hour wait. CMS (content management system) moves editable content outside app: marketer changes text in browser, users see changes within a minute.
What to Manage Through CMS
Not all content makes sense to move to CMS. Simple rule: if it changes less than once per quarter and requires release — keep in code. If it changes often and by non-technical staff — CMS.
Typically managed:
- Banners and promo blocks on main screen
- Onboarding and splash content
- Push notification and in-app message texts
- Product/service catalog (if not from ERP)
- Static pages (FAQ, terms, contacts)
- Feature flags (show/hide sections)
- Localized content (different text for regions)
Approach Choice
Headless CMS — delivers content via API, no frontend. Contentful, Strapi, Directus, Sanity. Mobile app calls API and renders data with own UI.
Backend + Admin Panel — custom CMS on own backend. Laravel Nova, Rails ActiveAdmin, custom React SPA. Full control over data structure and logic.
Firebase Remote Config — for feature flags and simple configuration data. Not for structured content with images.
Strapi and Directus — open source, self-hosted, free for content API. Contentful — paid hosted service, fast start, has CDN.
API Contract for Mobile Client
CMS should deliver data in format mobile client can render without additional transformation. Common mistake — CMS returns arbitrary structure, mobile developer spends days parsing.
Contract for home screen:
{
"homeScreen": {
"version": 12,
"updatedAt": "2025-03-15T10:30:00Z",
"banners": [
{
"id": "banner-spring-sale",
"imageUrl": "https://cdn.example.com/banners/spring-sale-2x.webp",
"imageUrl2x": "https://cdn.example.com/banners/spring-sale-3x.webp",
"deepLink": "myapp://promo/spring-sale",
"title": "Spring Sale",
"subtitle": "Up to -40% on everything",
"backgroundColor": "#FF5733",
"textColor": "#FFFFFF",
"expiresAt": "2025-04-01T00:00:00Z",
"targetAudience": "all"
}
],
"featuredCategories": [...],
"announcements": [...]
}
}
Field version — for client cache. If version hasn't changed since last request, can return 304 or use cache without re-loading.
Client Side: Caching and Updates
class CmsRepository(
private val api: CmsApi,
private val dao: CmsContentDao,
private val prefs: CmsPreferences
) {
fun observeHomeScreen(): Flow<HomeScreenContent> = flow {
// Immediately from cache
val cached = dao.getHomeScreen()
if (cached != null) emit(cached.toContent())
// Check for updates
try {
val response = api.getHomeScreen(
ifNoneMatch = prefs.homeScreenEtag
)
if (response.code() == 304) return@flow // cache current
val fresh = response.body()!!
dao.upsertHomeScreen(fresh.toEntity())
prefs.homeScreenEtag = response.headers()["ETag"]
emit(fresh)
} catch (e: IOException) {
// Network unavailable — cache already delivered
}
}
}
ETag caching: server generates hash of content, client sends it in If-None-Match. If content unchanged — 304 no body. At 100K DAU and screen loading 50 times a day — significant traffic and server load savings.
Content Localization
CMS should support multiple locales. Strapi v5 and Contentful have built-in i18n support — each field can have different values for different languages.
API takes Accept-Language: ru-RU header and returns content in needed language. Mobile client passes user locale:
// iOS
var request = URLRequest(url: cmsEndpoint)
request.setValue(Locale.current.identifier, forHTTPHeaderField: "Accept-Language")
Feature Flags Through CMS
CMS — convenient for feature flags. Not as A/B test (better Firebase Remote Config or Unleash), but for visibility management:
{
"featureFlags": {
"showCryptoPayments": true,
"enableDarkMode": true,
"referralProgramEnabled": false,
"maxCartItems": 50,
"minimumAppVersion": "2.4.0"
}
}
minimumAppVersion — CMS can softly block old app versions: show banner "Update app", not allowing new features. Without release.
Admin UI for Non-Technical Editors
Custom admin panel should be maximally simple for editors without technical background: WYSIWYG for texts, drag-and-drop for banner order, preview as in app, change history with rollback.
Strapi out of box gives good admin UI. For custom scenarios — React with additional design.
Headless CMS development on Strapi or custom backend with mobile API, caching and admin UI: 3–6 weeks. Cost calculated individually.







