Guides7 min read

Targeting iOS vs Android with OTA updates

Sometimes a fix only applies to one platform. When and how to ship platform-specific Capacitor live updates for iOS and Android separately using channels and runtime versions.

Most of the time you ship one web bundle to both platforms and it just works — that's the whole appeal of Capacitor. But occasionally a fix or a feature only applies to iOS or only to Android: a platform-specific CSS quirk, a workaround for one WebView, a feature gated to one store. This guide covers when and how to target iOS vs Android with Capacitor live updates using OtaKit.

Reach for platform-specific updates rarely. If you can solve it with a runtime Capacitor.getPlatform() check inside one shared bundle, do that first — it's simpler and keeps both platforms on one release.

Option 1: branch inside one bundle (preferred)

Ship the same bundle everywhere and branch at runtime. This keeps a single release stream and is almost always the right answer for small differences:

import { Capacitor } from '@capacitor/core';

if (Capacitor.getPlatform() === 'ios') {
  applyIosWorkaround();
} else if (Capacitor.getPlatform() === 'android') {
  applyAndroidWorkaround();
}

The change ships over the air to both platforms; each device runs its own branch. No extra channels, no divergence to manage.

Option 2: separate channels per platform

When the platforms genuinely diverge — different feature sets, different release cadences — give each its own channel. Configure iOS builds to follow production-ios and Android builds production-android, then release to each independently:

otakit upload --release production-ios
otakit upload --release production-android

This is more to manage — two streams to keep in sync — so use it only when the divergence is real. See Channels & runtime version.

Watch the native compatibility boundary

Platform differences often trace back to different native shells. If your iOS and Android binaries ship different plugin versions, that's a runtime version concern, not just a channel one — a bundle should only reach shells it's compatible with. See semantic versioning for bundles.

The maintenance cost of two channels is the same as the cost of any fork: every future change has to consider both. Merge them back to a single channel as soon as the platform-specific reason goes away.

Where to go next

See targeting users with channels for the broader channel patterns and Plugin API for platform detection.

Related docs