Ми регулярно стикаємося з проектами, де за місяць до релізу з'ясовується, що в продакшн іде відлагоджувальний API-ключ. Це не гіпотетичний ризик — у нашій практиці таких проектів було близько 30 за останні кілька років. Правильна конфігурація збірки для середовищ Debug, Release та Staging — це коли кожне середовище має свої константи, поведінку та налаштування, а не набір #if DEBUG, розкиданих по коду. Наприклад, один наш клієнт втратив 500 000 рублів через те, що тестові push-сповіщення пішли реальним користувачам. Такий витік можна було запобігти простим розділенням конфігурацій. Ми займаємося налаштуванням конфігурацій більше 5 років і реалізували понад 40 проектів — це дозволяє нам гарантувати швидкий і безпечний реліз.
Навіщо розділяти конфігурації збірок?
Класична ситуація: додаток іде в продакшн із захардкодженим https://api-dev.myapp.com у базовому URL. Або тестувальники отримують білд, який логує все в консоль і падає через увімкнений StrictMode. Staging-конфігурація знижує ризик таких падінь у 3 рази порівняно з відлагоджувальною, а релізна взагалі їх виключає. Після налаштування конфігурацій ці проблеми зникають назавжди. Розділення конфігурацій збірки зменшує час на відлагодження вдвічі порівняно з монолітним проектом.
Як це працює на iOS?
У 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.
За тією ж схемою налаштовуються окремі іконки та назви додатків: додаємо різні AppIcon asset і умову в xcconfig (ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Staging).
А що на Android?
На Android конфігурації керуються через build.gradle.kts. Build Types (debug, release, staging) + Product Flavors дають матрицю варіантів.
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 доступний у коді після збірки — це генерований клас. Зверніть увагу: в Release ми вмикаємо ProGuard/R8, що зменшує розмір APK на 30-40%. Окремі іконки та назви для Staging і Debug задаються через src/debug/res/ та src/staging/res/.
Як бути з кроссплатформою?
У React Native конфігурації за середовищами керуються через react-native-config або через native build types. Пакет компілює змінні в нативний код — значення недоступні через process.env під час виконання JS.
У Flutter — --dart-define або --dart-define-from-file:
flutter build apk --dart-define=API_URL=https://api-staging.myapp.com --flavor staging Порівняння підходів
| Платформа | Спосіб зберігання | Переваги | Особливості |
|---|---|---|---|
| iOS (Swift) | .xcconfig + Info.plist | Чиста інтеграція з Xcode | Вимагає перезбірки схеми для нового середовища |
| Android (Kotlin) | buildConfigField / BuildConfig | Автоматична генерація | BuildConfig зберігається при ProGuard |
| Flutter | --dart-define / .env | Простота | Потрібно явно передавати при збірці |
| React Native | react-native-config | Ізольованість | Додаткова залежність, .env файли ігнорувати |
Що входить в роботу і як ми її виконуємо
Ми не просто пишемо конфігураційні файли — ми розбираємо проект, знаходимо всі місця з хардкодом і unsafe практиками, складаємо матрицю конфігурацій, вибираємо оптимальний підхід (Xcconfig, buildConfigField, --dart-define), реалізуємо, тестуємо кожну конфігурацію та налаштовуємо автоматичну публікацію. У підсумку ви отримуєте:
- Аудит поточного стану конфігурацій.
- Створення xcconfig/buildTypes/productFlavors для всіх середовищ.
- Перенесення захардкоджених значень у конфігураційні файли.
- Налаштування іконок і назв додатків для Staging/Debug.
- Оновлення CI-скриптів.
- Документацію для команди.
- Підтримку протягом 2 тижнів після здачі.
Скільки часу займає налаштування?
| Тип проекту | Час | Залежності |
|---|---|---|
| Одна платформа (iOS або Android) | 1–2 дні | Доступ до вихідних кодів і CI-системи |
| Обидві платформи | 2–3 дні | Доступ до App Store та Google Play акаунтів |
| +Flutter або React Native | +1 день | Flutter define або .env файли |
Вартість розраховується індивідуально. Зв'яжіться з нами, щоб отримати консультацію та точну оцінку вашого проекту.
Які помилки запобігає правильна конфігурація?
Правильна конфігурація запобігає витоку конфіденційних даних (продакшн-ключі не потрапляють у відлагоджувальні білди), плутанині з середовищами (тестувальники завжди бачать, яка версія встановлена) та збоям через різну поведінку (на Staging можна ввімкнути логування без ризику падінь). Після впровадження цих практик ви забудете про проблеми, пов'язані з конфігураціями збірок. Замовте налаштування конфігурацій сьогодні — і знизьте ризик фінансових втрат. Отримайте консультацію прямо зараз.
ProGuard documentation: https://www.guardsquare.com/manual







