Ответ
Невозможно изменить оригинальный Info.plist в бандле (read-only), но можно читать и работать с копией или альтернативными хранилищами.
1. Чтение значений (только для чтения):
// Базовое чтение
let bundle = Bundle.main
// Чтение строки
if let appName = bundle.object(forInfoDictionaryKey: "CFBundleName") as? String {
print("App name: (appName)")
}
// Чтение массива
if let urlSchemes = bundle.object(forInfoDictionaryKey: "CFBundleURLSchemes") as? [String] {
print("URL schemes: (urlSchemes)")
}
// Типизированные методы
let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String
let build = bundle.infoDictionary?["CFBundleVersion"] as? String
2. Эмуляция изменяемого Info.plist:
// Создание изменяемой копии в Documents
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let configURL = documentsURL.appendingPathComponent("RuntimeConfig.plist")
// Инициализация при первом запуске
if !FileManager.default.fileExists(atPath: configURL.path) {
let originalURL = Bundle.main.url(forResource: "Info", withExtension: "plist")!
try FileManager.default.copyItem(at: originalURL, to: configURL)
}
// Чтение/запись изменяемой версии
var config = NSDictionary(contentsOf: configURL) as? [String: Any] ?? [:]
config["CustomAPIEndpoint"] = "https://api.example.com/v2"
(config as NSDictionary).write(to: configURL, atomically: true)
3. Альтернативы для динамических настроек:
| Способ | Использование |
|---|---|
| UserDefaults | Простые настройки пользователя |
| Keychain | Безопасное хранение токенов, ключей |
| Property List в Documents | Конфигурационные файлы |
| Core Data / SQLite | Сложные структурированные данные |
| Remote Config (Firebase) | Удаленное управление настройками |
Важные ограничения:
- Изменения в
Info.plistне влияют на системные вызовы (разрешения, URL schemes) - App Store Review проверяет значения в оригинальном
Info.plist - Кэшированные значения могут не обновляться сразу
Ответ 18+ 🔞
Да ты посмотри, какая дичь! Вроде бы Info.plist — это же святая святых, конфиг твоего приложения, там всё прописано, от названия до разрешений на камеру. А он, сука, read-only! Как будто его в бетон залили и говорят: «Смотри, но не трогай, а то сломаешь». Ну ёпта, как так-то?
Ладно, читать-то хоть можно, это не отнимают. Вот смотри, как это выглядит, если по-простому:
let bundle = Bundle.main
// Достаём оттуда имя приложения
if let appName = bundle.object(forInfoDictionaryKey: "CFBundleName") as? String {
print("App name: (appName)") // Выведет что-то вроде "МоёКривоеПриложение"
}
Версию и билд тоже выковырить можно, это стандартный финт ушами:
let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String // 1.2.3
let build = bundle.infoDictionary?["CFBundleVersion"] as? String // 456
Но вот если тебе вдруг приспичило что-то туда записать во время работы — скажем, поменять API эндпоинт на лету — то тут тебе сразу пиздец. Нельзя. Система не даст. Руки отрубит.
Что же делать, спросишь ты? А делать надо хитро, с подвохом. Как настоящий распиздяй.
Способ номер раз — создать свою, отдельную, блядь, копию. Засунуть её в папку Documents и там уже делать с ней что душе угодно.
// Находим папку Documents, куда можно писать
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let configURL = documentsURL.appendingPathComponent("RuntimeConfig.plist") // Вот наш новый, рукотворный файл
// Если файла ещё нет — спиздим оригинальный Info.plist и скопируем
if !FileManager.default.fileExists(atPath: configURL.path) {
let originalURL = Bundle.main.url(forResource: "Info", withExtension: "plist")!
try FileManager.default.copyItem(at: originalURL, to: configURL) // Украли, блядь, конфиг!
}
// Теперь это наш личный черновик, пишем что хотим
var config = NSDictionary(contentsOf: configURL) as? [String: Any] ?? [:]
config["CustomAPIEndpoint"] = "https://api.example.com/v2" // Меняем URL на лету
(config as NSDictionary).write(to: configURL, atomically: true) // Сохраняем
Выглядит как будто мы что-то поменяли, да? Но это иллюзия, ёпта! Это просто наша личная бумажка в Documents. Настоящий Info.plist в бандле остался нетронутым, как скала. Система про нашу подделку ничего не знает и знать не хочет.
Поэтому, если тебе нужно что-то динамическое — токены, настройки, флаги — то есть куча других, нормальных мест. Смотри, держи табличку, чтобы не пиздеть попусту:
| Куда лезть | Для чего сгодится |
|---|---|
| UserDefaults | Для простой хуйни вроде настроек тёмной темы или громкости. |
| Keychain | Для паролей, токенов и прочей секретной лабуды, которую нельзя светить. |
| Property List в Documents | Для своих конфигов, как мы только что и намутили. |
| Core Data / SQLite | Если данных овердохуища и они сложные. |
| Remote Config (типа Firebase) | Чтобы менять настройки на лету с бэкенда, не выпуская апдейт. |
И запомни главное, чтобы не облажаться:
- Система всё равно будет смотреть в оригинальный
Info.plistдля всяких разрешений (NSCameraUsageDescription) или URL-схем. Нашу подделку вDocumentsона проигнорирует с таким видом, будто там пусто. - Ребята из App Store Review тоже проверяют оригинальный файл. Не пытайся их обмануть, они это уже видели.
- Даже если ты что-то поменял, значения могут закэшироваться. Перезапуск приложения — твой лучший друг в этом случае.
Короче, суть в чём: оригинальный Info.plist — это как паспорт. Его можно посмотреть, но исправлять шариковой ручкой — плохая идея. Для всего остального есть другие, более подходящие инструменты. Не усложняй, блядь!