Skip to content
Native targets
On this page

Native iOS and Android release targets — the activation conditions, version management, build number handling, and platform constraints.

#Native targets

#Overview

The native-ios and native-android targets handle platform-specific versioning for native mobile applications without cross-platform frameworks. They manage 2 version fields each (marketing version + build number) in platform-native project files (Xcode pbxproj, Gradle build files), with auto-incremented integer build numbers on every release.

Both targets are conditionally auto-detectable — they only activate when specific manifest patterns are found and competing targets are absent.

#Comparison

Comparison
Aspectnative-iosnative-android
Manifest*.xcodeproj/project.pbxproj or Project.swiftbuild.gradle / build.gradle.kts
Version fieldMARKETING_VERSIONversionName
Build number fieldCURRENT_PROJECT_VERSIONversionCode
Build number behaviorAuto-incremented on each releaseAuto-incremented on each release
Rejection rulePackage.swift present (use swift target)com.android.library plugin (use maven target)
CI templatesNone (requires macOS runner)None (requires Android SDK)
version_file()Returns None (dynamic)Returns the detected build.gradle path

#native-ios

#Detection

The native-ios target's activation check ensures it only applies to genuine Xcode app projects, not Swift packages or other iOS-adjacent project types. The target activates when every condition below is met:

  1. No Package.swift is present in the project root (SPM projects use the swift target instead)
  2. At least one *.xcodeproj directory exists containing a project.pbxproj file
  3. The pbxproj file contains MARKETING_VERSION or CURRENT_PROJECT_VERSION build settings

Alternatively, Tuist-managed projects are detected via Project.swift containing CFBundleShortVersionString.

#Version reading

Version is extracted from the Xcode project's pbxproj file using regex-based inline parsing rather than a full plist parser, since pbxproj files use a non-standard ASCII plist variant. The pattern matches the MARKETING_VERSION build setting, which corresponds to CFBundleShortVersionString in the app bundle and is the user-visible version string displayed on the App Store:

MARKETING_VERSION\s*=\s*([^;]+);

The matched value is stripped of whitespace and quotes.

#Build number

CURRENT_PROJECT_VERSION is auto-incremented as an integer (e.g., 1, 2, 3, ...) on each release, independent of the marketing version string. This corresponds to CFBundleVersion in the app's Info.plist and is required by App Store Connect for each binary submission. Apple requires the build number to be strictly increasing within each marketing version, so rlsbl reads the current integer value and writes value + 1 during every release bump.

#Multi-target projects

When multiple *.xcodeproj directories exist in the project root, the target uses the first xcodeproj (sorted alphabetically) that contains version keys in its pbxproj for reading the current version. During a version bump, all xcodeproj files containing version keys are updated simultaneously to maintain consistency across app targets, extensions, and frameworks within the same project.

#Tuist support

For Tuist-managed projects, rlsbl detects the presence of a Project.swift manifest and reads version information from the Tuist project configuration rather than raw pbxproj files. This supports teams that generate their Xcode project from a Swift-based DSL:

  • Looks for CFBundleShortVersionString in the Swift manifest
  • Version reading and writing use the Tuist project configuration rather than raw pbxproj
  • Build number handling follows the same auto-increment pattern

#version_file()

Returns None because the actual file depends on which xcodeproj is found at runtime. This means version consistency checks rely on the target's read_version() method rather than direct file reads.

#native-android

#Detection

The native-android target uses content inspection with 3 conditions to distinguish Android application modules from library modules, preventing false positives on projects that should use the Maven target instead. The target activates when:

  1. A build.gradle or build.gradle.kts file exists in the project root
  2. The file contains the com.android.application plugin declaration
  3. The file does not contain com.android.library (library projects use the maven target)

The content-inspection step prevents false positives on Android library modules that should be published to Maven rather than versioned as standalone apps.

#Mutual exclusion with Maven

Android libraries (com.android.library plugin) are excluded from native-android detection. These projects publish to Maven Central or a private Maven repository and should use the maven target, which handles artifact publishing, POM generation, and Maven-specific versioning conventions.

#Version reading

Version is extracted from the detected build.gradle file using regex-based inline parsing that handles both Groovy DSL and Kotlin DSL syntax. The same pattern works for both DSL variants because they share the same versionName property syntax. The pattern matches:

versionName\s+["']([^"']+)["']

Both Groovy DSL (build.gradle) and Kotlin DSL (build.gradle.kts) use the same pattern.

#Build number

versionCode is auto-incremented as an integer (e.g., 1, 2, 3, ...) on each release, independent of the semver version string. This integer is required by Google Play for each APK/AAB upload and must be strictly increasing -- rlsbl reads the current value from build.gradle and writes value + 1 during every version bump.

#Version writing

Both versionName and versionCode are updated in-place using regex substitution on the same build.gradle file where they were originally found during detection. The version name is set to the new semver string and the version code is auto-incremented as an integer, ensuring both values stay synchronized in a single atomic write.

#No CI templates

Neither native target generates CI workflow templates during rlsbl scaffold, unlike npm, PyPI, Go, and other targets that include ready-to-use GitHub Actions workflows. This is intentional because native mobile builds have platform-specific requirements that vary too widely for a generic template:

No CI templates
PlatformRequirementWhy no template
iOSmacOS runner with XcodeGitHub Actions macOS runners are expensive; build configuration varies widely (certificates, provisioning profiles, Xcode version)
AndroidAndroid SDK, build tools, NDKConfiguration depends heavily on project specifics (SDK version, NDK usage, signing keystores)

Users must configure CI manually for native targets. The release flow still handles version bumping, tagging, and GitHub Release creation — only the publish/build step is absent.

#Decision table: which target to use

Decision table: which target to use
Project typeIndicatorsCorrect target
iOS app (Xcode)*.xcodeproj with MARKETING_VERSION, no Package.swiftnative-ios
iOS app (Tuist)Project.swift with CFBundleShortVersionStringnative-ios
Swift packagePackage.swift presentswift
Android appbuild.gradle with com.android.applicationnative-android
Android librarybuild.gradle with com.android.librarymaven
Cross-platform (Flutter)pubspec.yaml with flutter dependencyflutter
Cross-platform (React Native)package.json + native dirsnpm (with manual native versioning)

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search