Настройка конфигурации сборки (Debug, Release, Staging)
Классическая ситуация: приложение уходит в продакшн с захардкоженным https://api-dev.myapp.com в базовом URL. Или тестировщики получают билд, который логирует всё в консоль и падает из-за включённого StrictMode. Правильная конфигурация сборки — это когда каждое окружение имеет свои константы, поведение и настройки, а не набор #if DEBUG разбросанных по коду.
iOS: Xcconfig + Build Configurations
В Xcode по умолчанию два build configuration: Debug и Release. Staging добавляется вручную: Product → Scheme → Edit Scheme → Duplicate Release → переименовать в Staging.
Для хранения конфигурационных значений используем .xcconfig файлы:
// Config/Debug.xcconfig
API_BASE_URL = https://api-dev.myapp.com
LOG_LEVEL = verbose
BUNDLE_ID_SUFFIX = .debug
// Config/Staging.xcconfig
API_BASE_URL = https://api-staging.myapp.com
LOG_LEVEL = info
BUNDLE_ID_SUFFIX = .staging
// Config/Release.xcconfig
API_BASE_URL = https://api.myapp.com
LOG_LEVEL = error
BUNDLE_ID_SUFFIX =
В Info.plist значения подтягиваются через $(API_BASE_URL). В коде читаются через Bundle.main.infoDictionary:
enum AppConfig {
static var apiBaseURL: URL {
guard let urlString = Bundle.main.object(forInfoDictionaryKey: "API_BASE_URL") as? String,
let url = URL(string: urlString) else {
fatalError("API_BASE_URL not configured")
}
return url
}
}
Никаких #if DEBUG для URL — только Bundle.
Android: Build Types + Product Flavors
На Android конфигурации управляются через build.gradle.kts. Build Types (debug, release, staging) + Product Flavors (free, paid, enterprise) дают матрицу вариантов.
android {
buildTypes {
debug {
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
isDebuggable = true
buildConfigField("String", "API_BASE_URL", "\"https://api-dev.myapp.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "true")
}
create("staging") {
initWith(getByName("release"))
applicationIdSuffix = ".staging"
versionNameSuffix = "-staging"
buildConfigField("String", "API_BASE_URL", "\"https://api-staging.myapp.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "true")
signingConfig = signingConfigs.getByName("debug")
}
release {
isMinifyEnabled = true
buildConfigField("String", "API_BASE_URL", "\"https://api.myapp.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "false")
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
BuildConfig.API_BASE_URL доступен в коде после сборки — это генерируемый класс, не захардкоженная строка.
Иконки и названия приложений по конфигурации
Staging и Debug сборки должны визуально отличаться от релизных. На iOS это делается через разные AppIcon asset в Assets.xcassets + условие в xcconfig (ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Staging). На Android — через src/debug/res/ и src/staging/res/ с отдельными ic_launcher и strings.xml (с другим app_name).
React Native и Flutter
В React Native конфигурации по окружениям управляются через react-native-config (.env.staging, .env.production) или через native build types/schemes, описанные выше. Пакет react-native-config компилирует переменные в нативный код — значения недоступны через process.env во время выполнения JS, что безопаснее.
В Flutter — --dart-define или --dart-define-from-file:
flutter build apk --dart-define=API_URL=https://api-staging.myapp.com --flavor staging
Процесс
Аудит текущего способа управления конфигурациями → создание xcconfig/buildTypes структуры → перенос захардкоженных значений в конфиги → обновление CI для сборки нужного варианта → настройка иконок и названий → документация для команды.
Срок: 1–3 дня. Стоимость рассчитывается индивидуально.







