Delivered remotely for a business based in Sydney, Australia. Names withheld by agreement.
Project Overview
Industry: Digital media and publishing — editorial and video content.
Type of solution: Two native mobile applications, one Android and one iOS, built as clients over a headless content management system with a thin auxiliary service for identity, notifications, sharing and link resolution.
Business context: The publisher had an established website, a large social following and a growing investment in original video series. Web traffic to a media property arrives from search and social, converts poorly into habit, and offers no way to bring a reader back. Video, meanwhile, was competing for attention against platforms with full-screen, swipe-through native players while being served through an article-centric web page. A mobile application was the answer to both problems, but only if it could be fed by the newsroom's existing tools rather than requiring a parallel publishing workflow.
General users: Readers and viewers, in two states — guests, who can browse and watch everything without an account, and registered users, who additionally get personalisation and account management.
General purpose: To turn an audience that visits into an audience that returns, by giving the publisher's editorial and video output a native home that the newsroom controls directly.
The Business Challenge
The newsroom cannot maintain two publishing systems. Any mobile product that required editors to publish twice, or engineers to deploy in order to change what is featured, would have been abandoned within months. The app had to consume what the newsroom already produced, in the tools it already used.
Editorial composition changes constantly, and releases do not. What leads the home screen changes several times a day. Which video series is promoted changes with the production schedule. None of that can wait on an app store review cycle.
Publisher formatting is not negotiable. Editorial output carries rich formatting, pull quotes, embedded media and third-party social embeds. Reimplementing that rendering natively — twice, once per platform — would have been expensive, permanently incomplete, and a constant source of "it looks wrong in the app" complaints.
Millions of existing web URLs had to work. The publisher's back catalogue is linked from search results, social posts and other sites. Every one of those links needed to open natively in the app where a native view existed, and to degrade gracefully where it did not — without the app needing to understand the CMS's internal URL structure.
Video needed to feel native, not embedded. A video-first audience compares the experience against dedicated video platforms. A web-embedded player inside a scrolling article does not survive that comparison.
Re-engagement was the entire point. Without push notifications that land the reader directly on the announced story, the app is just a slower website.
Two platforms, one product. Android and iOS audiences had to receive the same product, from the same content source, with the same deep-link and notification behaviour — without a cross-platform framework compromising the video and gesture experience the publisher was buying.
Our Approach
Treat the CMS as the product's control surface. Rather than defining the app's structure in code, we built each feed section, each tab and each video series against its own curated endpoint on the publisher's content system. What appears at the top of the home screen, what fills a section, and which series are promoted are all editorial decisions taken in the CMS and reflected in the app on the next refresh. Engineering is not in the loop for day-to-day composition.
Render natively around a web-rendered body. Navigation, imagery, the article pager, the video player and every gesture are native on both platforms. The article body itself is the publisher's own HTML, rendered in an embedded view with the app's viewport and typography applied. The result preserves editorial formatting and third-party embeds exactly, without maintaining two bespoke HTML renderers that would never quite match the website.
Resolve deep links on the server, not in the client. When the app receives a link to the publisher's site — from a browser, a social app, a shared message or a push notification — it hands the whole URL to a resolution endpoint and renders whatever content comes back natively. If the content has no native representation, the app opens it in an in-app browser instead. The client never encodes the CMS's routing rules, so those rules can change freely without breaking apps already installed.
Generate share links on the server too. Sharing an article asks the backend for the canonical shareable link rather than assembling one on the device. Shared links therefore stay consistent with the website's canonical URLs and campaign tagging, and the link format can change without an app release.
Build a reading experience, not a list. Article and video detail is a one-item-per-page swipeable pager with explicit next and previous affordances and a "watch next" prompt at the end of a video — so finishing one item leads into the next rather than back to a list.
Let people in before asking anything of them. All content is browsable and watchable without an account. Sign-in — through the platform's own identity providers as well as email — gates only personalisation and account management. Nothing about acquisition depends on a registration wall.
Fail one section at a time. The home screen assembles from several independent requests. A slow or failing section degrades only itself; the rest of the screen renders. Connectivity is checked before requests, with an explicit offline state rather than an empty screen.
Two native codebases, deliberately. Given a product whose value is video playback, gesture-driven reading and platform-correct deep linking, we built Kotlin on Android and Swift on iOS rather than accepting a cross-platform compromise in exactly the areas the publisher was investing in.
The Solution
Editor-composed home screen. A featured lead item, a trending collection, a themed section and a video-series collection, each independently curated in the CMS and independently fetched, assembled into a single scrollable screen with pull-to-refresh.
Sectioned browsing. Dedicated tabs for a themed content section and for the publisher's branded original video series, each with its own featured item and listing, all editor-controlled.
Branded original series. Each video series is backed by its own curated collection, so a new series becomes available to promote as soon as editorial creates it.
Swipeable article and video reader. One item per page with next/previous controls, native imagery and player, publisher-authored body content, author and publication metadata, and an end-of-video prompt leading into the next item.
Native video playback. Full-screen playback through the platform's native player — adaptive streaming capable on Android — with correct return-to-context behaviour, plus full-screen support for video embedded within article bodies.
Universal deep linking. Verified app links on Android and universal links on iOS mean any link to the publisher's site opens in the app. Resolution happens server-side, with an in-app browser fallback for anything without a native view.
Server-generated sharing. Sharing produces a canonical link supplied by the backend and hands it to the platform share sheet.
Push notifications with destination routing. Device registration on launch, and notification payloads that carry a navigation instruction so tapping a notification opens the specific article or video it announced — including from a cold start.
Accounts and federated sign-in. Email registration and password recovery alongside the platform identity providers, with the platform's own sign-in offered on iOS.
Personalisation. A preference picker across three content taxonomies, presented at first run and reachable from settings.
Account self-service. Change email, change password and delete the account in-app, with a considered final screen pointing the departing reader to the publisher's other channels.
Offline and error states. Connectivity detection with explicit offline messaging, graceful per-section failure, and crash reporting in production.
Key Features
CMS-composed content structure Every feed section, tab and video series is a curated collection in the publisher's content system. Editorial changes what readers see without an app release and without engineering involvement.
Server-resolved deep linking Inbound links are resolved by the backend into content payloads and rendered natively, with an in-app browser fallback. The app carries no knowledge of the CMS's URL structure, so that structure can evolve without breaking installed versions.
Server-generated share links Share actions request a canonical link from the backend rather than constructing one on the device, keeping shared URLs consistent with the website's canonical addressing and campaign tracking.
Hybrid article rendering Native chrome, imagery, pager and player around a publisher-authored HTML body with app typography and viewport applied — preserving rich editorial formatting and third-party embeds exactly as published.
Swipeable one-item-per-page reader A paged reading and viewing experience with explicit navigation affordances and a watch-next prompt, designed to carry a reader from one item into the next rather than back to a list.
Native adaptive video playback Full-screen native playback with adaptive streaming support on Android, plus full-screen handling for video embedded inside article bodies.
Destination-aware push notifications Device registration and data-carrying notification payloads routed through the launch path, so a notification tap opens the exact content it announced, including from a cold start.
Guest-first access Complete content access without an account; sign-in gates only personalisation and account management.
Federated identity Sign-in through established platform identity providers alongside email registration, so most users never create another password.
In-app account deletion A complete self-service deletion path meeting platform policy requirements, with a final retention moment offering the publisher's other channels.
Technical Architecture
Content source. A headless content management system with a node-based content model. Each piece of content carries an identifier, a rendered HTML body, author and publication metadata, imagery and, where present, a video URL. Content is exposed to the apps as a set of read-only, per-collection JSON endpoints, each corresponding to a curated editorial collection.
Auxiliary service layer. A thin REST service alongside the CMS handling the things a content system does not: authentication, push-token registration, canonical share-link generation and inbound deep-link resolution.
Android client. Kotlin, with an Activity and Fragment screen layer over a single central networking helper built on a standard HTTP and REST stack with JSON serialisation. Imagery is loaded and cached through a mature image pipeline. Video plays through a dedicated module wrapping an adaptive-streaming-capable player supporting both major streaming formats. Article bodies render in a custom web view extended with full-screen video support and a JavaScript bridge for playback events. Deep linking uses verified app links; notifications route through the launch activity. Release builds are minified with resource shrinking.
iOS client. Swift and UIKit with a scene-based lifecycle, a view-controller screen layer, and a shared networking singleton over a standard Swift HTTP client. Imagery is loaded and cached through a mature image pipeline with vector and animated-image support. Video plays through the system player controller; article bodies render in a web view inside an animated paging collection view. Universal links are handled at the scene layer and resolved through the backend before native presentation. Crash reporting runs in production.
Rendering model. Hybrid by design — native navigation, media and gestures wrapping publisher-authored HTML for article bodies, with viewport and typography injected by the client on each platform.
Composition model. Screen regions map one-to-one onto curated collections. Each region fetches independently; a failing region degrades alone. Launch requests are staggered on iOS to avoid a burst of concurrent traffic.
Flow: Editorial publishes in the CMS → curated collections exposed as read-only JSON endpoints → native Android and iOS clients compose screens per collection → native pager, imagery and player around publisher-authored body HTML → inbound web links and push payloads resolved server-side into native destinations → share actions request canonical links from the backend
Technology Stack
| Category | Technology |
|---|---|
| Content backend | Headless content management system, node-based content model |
| Content API | Read-only per-collection JSON endpoints |
| Auxiliary services | REST endpoints for identity, push registration, share links and deep-link resolution |
| Android language & build | Kotlin, Gradle, View Binding, R8 minification with resource shrinking |
| Android networking | Retrofit and OkHttp with JSON serialisation |
| Android media | Adaptive-streaming-capable player module (HLS and DASH), custom full-screen-capable web view with JavaScript bridge |
| Android imaging | Glide with custom module configuration |
| Android UI | Material Components, ConstraintLayout, SwipeRefreshLayout, custom paging components |
| iOS language & build | Swift, UIKit with Storyboards, scene-based lifecycle, CocoaPods |
| iOS networking | Alamofire with image extension |
| iOS media | AVKit / AVFoundation native playback, WebKit for article bodies |
| iOS imaging | SDWebImage with vector and animated-image plugins |
| iOS UI | Animated paging collection layouts, carousel and slideshow components, drawer navigation, keyboard and progress utilities |
| Identity | Google, Facebook and Apple platform sign-in alongside email registration |
| Push notifications | Firebase Cloud Messaging on both platforms, with destination-carrying data payloads |
| Deep linking | Verified app links (Android) and universal links (iOS), resolved server-side |
| Monitoring | Firebase Crashlytics |
| Connectivity | Platform reachability detection with explicit offline states |
Technical Challenges & Solutions
| Challenge | Our Approach |
|---|---|
| The newsroom could not maintain a second publishing workflow for mobile | The apps consume the publisher's existing content system directly. Every feed section, tab and video series maps to a curated collection in the CMS, so editorial publishes once and the app reflects it on the next refresh. |
| Editorial composition changes daily; app releases do not | Screen composition is data, not code. What leads the home screen, what fills a section and which series are promoted are all curated in the CMS. Engineering is not involved in day-to-day editorial decisions. |
| Rich publisher formatting and third-party embeds could not be reimplemented natively, twice | A hybrid rendering model: native chrome, imagery, pager and player around the publisher's own HTML body, with app typography and viewport injected on each platform. Editorial formatting renders exactly as published, with no parallel renderer to maintain. |
| A large back catalogue of web URLs had to open natively without the client understanding CMS routing | Deep links are resolved server-side. The app posts the inbound URL or content identifier to a resolution endpoint, renders the returned content natively, and falls back to an in-app browser when no native view exists. CMS routing can change without breaking installed apps. |
| Shared links needed to stay canonical and correctly tracked as URL conventions evolved | Share actions request the canonical link from the backend rather than assembling one on the device, so link format and campaign tagging can change server-side with no app release. |
| Video had to compete with dedicated video platforms for attention | Native full-screen playback on both platforms — adaptive streaming capable on Android — with return-to-context handling and a watch-next prompt, plus full-screen support for video embedded inside article bodies. |
| Notifications are worthless if they land the reader on a home screen | Notification payloads carry a navigation instruction routed through the launch path on both platforms, so a tap opens the specific content announced, including from a cold start. |
| A home screen assembled from several independent sources fails in several ways | Each screen region fetches independently and degrades independently, with launch requests staggered on iOS. A slow or failing section affects only itself; the rest of the screen renders normally. |
| One product, two platforms, no compromise on the parts that mattered | Two native codebases — Kotlin and Swift — rather than a cross-platform framework, precisely because video playback, gesture-driven reading and platform-correct deep linking were what the publisher was investing in. |
Security & Reliability
Delegated identity. Sign-in is offered through established platform identity providers as well as email, so most users authenticate against systems they already trust and the application handles as few credentials as possible.
Minimal on-device data. The applications store essentially nothing personal locally — a session token and a notification registration token. There is no local content database and no cached personal data to expose if a device is lost.
Guest-first access. Because all content is available without an account, the majority of usage involves no credentials and no personal data at all — the strongest available reduction in exposure surface.
Token-based API authorisation. Authenticated requests carry a bearer token applied centrally by each platform's single networking layer, so authorisation is handled in one place per codebase rather than per screen.
Self-service account deletion. A complete in-app deletion path, meeting platform policy requirements and giving users direct control over their data.
Graceful degradation. Connectivity is checked before requests, with explicit offline states rather than blank screens. Each screen region fails independently, so one unavailable collection never takes down a screen.
Production crash reporting. Crash and stability reporting in production, so real-device failures surface as data rather than as store reviews.
Server-side link resolution as a safety property. Because inbound links are resolved by the backend rather than parsed on the device, content routing can be corrected centrally and immediately — installed applications do not carry stale assumptions about the publisher's URL structure.
Scalability & Performance
Read-only, cacheable content endpoints. Every content request is a read against a curated collection, which makes the entire content surface cacheable at the edge. Publishing scale for a media property is read scale, and this architecture puts almost all of it in front of a cache.
Independent, parallel section loading. Screen regions fetch independently rather than waiting on a single composite response, so the first content appears as soon as any one collection returns. On iOS, launch requests are deliberately staggered to avoid a burst of concurrent connections at the moment the app is least responsive.
Mature image pipelines on both platforms. Imagery — the dominant payload in a publishing app — is loaded, decoded and cached in memory and on disk by established libraries on each platform, with vector and animated-image support on iOS.
Streamed, not downloaded, video. Video plays through native streaming with adaptive bitrate support on Android, so playback starts quickly and adapts to the connection rather than committing the user to a download.
Minified release builds. Android release builds run code minification and resource shrinking, keeping install size and method count down on a wide range of devices, with the application supporting a deliberately broad span of OS versions.
Composition without deployment. Because editorial composition is data rather than code, the operational cost of changing the product's structure is a CMS edit — no build, no review, no staged rollout, and no risk to installed clients.
Business Outcomes
- The newsroom publishes once. Content created in the existing content system appears in both mobile applications with no parallel workflow and no duplicated effort.
- Editorial controls the product's structure. What leads, what is featured and which video series are promoted are editorial decisions taken in the CMS, not engineering tickets waiting on a release.
- The existing web catalogue became app content. Links from search, social and messaging open natively in the application, so the publisher's accumulated back catalogue drives app engagement rather than bouncing readers to a browser.
- Re-engagement became possible. Push notifications that land readers directly on the announced content give the publisher a direct channel to its audience that a website cannot provide.
- Video got a native home. Original series are presented through a native full-screen player with a swipe-through reading and viewing flow, rather than as embedded players inside web pages.
- Editorial formatting survived the move to mobile. Rich publisher formatting and third-party embeds render as authored, with no separate mobile rendering pipeline to maintain or explain.
- Growth is not gated behind registration. A guest-first product means acquisition is never blocked by a sign-up wall, while accounts remain available for personalisation.
- Both platforms received the same product. Android and iOS readers get equivalent content, deep-linking and notification behaviour from a single content source.
Why it worked
Publishing applications look simple from the outside and are decided by a question that rarely appears in the brief: who controls what the app shows? Build the structure in code and every editorial decision becomes an engineering ticket, the newsroom stops asking, and the product ossifies within a quarter. Build it in data and the app becomes something the people who make the content can actually operate.
Our team builds for that reality. We mapped the product's structure onto curated collections in the publisher's own content system, so composition is an editorial act rather than a release. We chose a hybrid rendering model deliberately — native everywhere the experience is felt, publisher HTML where the publisher's formatting is the value — instead of committing to two bespoke renderers that would never match the website. We put deep-link resolution on the server so that the client carries no assumptions about content routing, which means the publisher can change its URL structure years from now without stranding installed applications. And we built two native codebases rather than one cross-platform one, because video playback, gesture-driven reading and platform-correct linking were precisely what the client was paying for.
Our teams work across native Android and iOS development, headless CMS integration, media and streaming playback, deep linking and attribution, push notification architecture, and federated identity — with the product judgement to know which decisions belong in code and which belong to the people using the system every day.
Final Summary
A digital publisher had an established web audience, a growing library of original video, and no way to bring either back tomorrow. Search and social sent readers who did not return. Video that deserved a full-screen native player was being served inside web pages. And any solution that asked the newsroom to publish twice, or asked engineering to deploy every time the lead story changed, was going to be abandoned.
Our team built native Android and iOS applications directly on the publisher's existing headless content management system. Every section, tab and video series maps to a curated collection in the CMS, so what readers see is an editorial decision taken in the tools the newsroom already uses — no second workflow, no release cycle in the way. Article bodies render as the publisher authored them, wrapped in native navigation, imagery, gestures and players, so rich formatting and third-party embeds survive the move to mobile without a parallel rendering pipeline to maintain. Video plays through native full-screen players with adaptive streaming, inside a swipe-through reader designed to carry attention from one item into the next.
The architectural decision that pays for itself repeatedly is server-side link resolution. Any link to the publisher's site — from a browser, a shared message, a social post or a push notification — is handed to the backend, resolved into content, and rendered natively, falling back to an in-app browser when there is nothing native to show. The applications carry no knowledge of the content system's URL structure, which means the publisher's back catalogue works today and will keep working after the URL conventions change. Combined with server-generated canonical share links and destination-aware push notifications, it turns an audience that visited into an audience that can be reached — which, for a publisher, is the only outcome that compounds.