Настройка Managed App Configuration для iOS-додатка
Managed App Configuration—механізм Apple MDM, який дозволяє IT-адміністратору передавати конфігурацію в додаток без участі користувача та без хардкода параметрів у коді. MDM-сервер відправляє plist-словник, додаток читає його з UserDefaults. Це стандарт для будь-якого корпоративного iOS-додатка—працює з Jamf, Intune, Workspace ONE, MobileIron та будь-яким іншим MDM.
Як додаток читає Managed Config
Все зберігається в UserDefaults під ключем com.apple.configuration.managed:
func loadManagedConfiguration() {
guard let config = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed") else {
// Пристрій неуправляємий або конфіг ще не доставлений
applyDefaultConfiguration()
return
}
let backendURL = config["BackendURL"] as? String ?? AppDefaults.backendURL
let tenantID = config["TenantID"] as? String
let sessionTimeout = config["SessionTimeoutMinutes"] as? Int ?? 30
let enableDebugLogs = config["EnableDebugLogs"] as? Bool ?? false
AppConfig.shared.apply(
backendURL: backendURL,
tenantID: tenantID,
sessionTimeout: sessionTimeout,
debugLogs: enableDebugLogs
)
}
Одна ловушка: конфігурація може не прийти відразу при першому запуску, а через кілька секунд після MDM-checkin. Додаток не повинен блокуватися в очікуванні конфіга—застосовуємо дефолти, потім оновлюємо.
Реакція на зміни конфігурації
MDM може оновити конфіг в будь-який момент—напр., змінити URL бекенду при міграції інфраструктури. Потрібно реагувати без перезапуску:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(managedConfigChanged),
name: UserDefaults.didChangeNotification,
object: nil
)
}
@objc private func managedConfigChanged() {
guard let newConfig = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed") else { return }
let newBackendURL = newConfig["BackendURL"] as? String
if newBackendURL != AppConfig.shared.backendURL {
// Переініціалізуємо мережневий шар з новим URL
NetworkManager.shared.reconfigure(baseURL: newBackendURL)
}
}
UserDefaults.didChangeNotification спрацьовує при будь-якій зміні UserDefaults—фільтруємо лише потрібний ключ, щоб не перезавантажувати конфігурацію при кожній дрібній зміні.
Структура конфігураційного словника
Рекомендований підхід—JSON Schema для документування ключів конфігурації. AppConfig Manager на стороні додатка:
struct ManagedConfig: Decodable {
let backendURL: String
let tenantID: String?
let sessionTimeoutMinutes: Int
let allowBiometricAuth: Bool
let supportedLanguages: [String]
let featureFlags: [String: Bool]?
enum CodingKeys: String, CodingKey {
case backendURL = "BackendURL"
case tenantID = "TenantID"
case sessionTimeoutMinutes = "SessionTimeoutMinutes"
case allowBiometricAuth = "AllowBiometricAuth"
case supportedLanguages = "SupportedLanguages"
case featureFlags = "FeatureFlags"
}
}
func decodeManagedConfig() -> ManagedConfig? {
guard let dict = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed"),
let data = try? JSONSerialization.data(withJSONObject: dict),
let config = try? JSONDecoder().decode(ManagedConfig.self, from: data) else {
return nil
}
return config
}
Типізована структура краща, ніж ручне приведення типів з [AnyHashable: Any]—помилки конфігурації виявляються раніше.
AppConfig у MDM-консолях
У Jamf Pro конфігурація задається в Apps → App Configuration → XML Profile:
<dict>
<key>BackendURL</key>
<string>https://api.corp.example.com/v2</string>
<key>TenantID</key>
<string>CORP-EU-001</string>
<key>SessionTimeoutMinutes</key>
<integer>20</integer>
<key>AllowBiometricAuth</key>
<true/>
<key>SupportedLanguages</key>
<array>
<string>en</string>
<string>de</string>
</array>
</dict>
У Intune—через Apps → App Configuration Policies → Managed Devices → iOS/iPadOS → General. Значення вводяться як key-value або XML. Intune передає через Apple MDM протокол—той же com.apple.configuration.managed ключ.
Тестування без MDM-сервера
Для розробки конфігурацію можна емулювати через UserDefaults.standard.set() у launch arguments або окремий debug-екран:
#if DEBUG
func injectTestManagedConfig() {
let testConfig: [String: Any] = [
"BackendURL": "https://staging-api.corp.example.com",
"TenantID": "TEST-001",
"SessionTimeoutMinutes": 5,
"AllowBiometricAuth": true
]
UserDefaults.standard.set(testConfig, forKey: "com.apple.configuration.managed")
}
#endif
Правильно також тестувати в Simulator з Managed Preferences через defaults write у терміналі—це імітує реальну доставку конфіга через MDM без необхідності реального MDM-сервера.
Feedback Channel: отчет стану в MDM
MDM може не лише передавати конфіг, але й читати стан додатка через com.apple.configuration.managed.feedback. Додаток записує туди статус:
UserDefaults.standard.set([
"LastSyncTime": ISO8601DateFormatter().string(from: Date()),
"ConfigVersion": "2.1",
"EnrollmentStatus": "active"
], forKey: "com.apple.feedback.managed")
MDM-сервер (Jamf, Intune) читає цей ключ при checkin та відображає в інвентарі пристрою. Зручно для діагностики без звернення до користувача.
Етапи настройки
Визначення параметрів конфігурації → розробка схеми словника → реалізація чтення + реакції на зміни → тестування без MDM → публікація документації ключів для IT-відділу → настройка профілів у MDM-консолі → пілот → rollout.
Терміни: реалізація Managed App Configuration у готовому додатку—1–2 тижні. Вартість розраховується індивідуально.







