Как изменять данные из Info.plist во время выполнения приложения?

Ответ

Невозможно изменить оригинальный 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) Чтобы менять настройки на лету с бэкенда, не выпуская апдейт.

И запомни главное, чтобы не облажаться:

  1. Система всё равно будет смотреть в оригинальный Info.plist для всяких разрешений (NSCameraUsageDescription) или URL-схем. Нашу подделку в Documents она проигнорирует с таким видом, будто там пусто.
  2. Ребята из App Store Review тоже проверяют оригинальный файл. Не пытайся их обмануть, они это уже видели.
  3. Даже если ты что-то поменял, значения могут закэшироваться. Перезапуск приложения — твой лучший друг в этом случае.

Короче, суть в чём: оригинальный Info.plist — это как паспорт. Его можно посмотреть, но исправлять шариковой ручкой — плохая идея. Для всего остального есть другие, более подходящие инструменты. Не усложняй, блядь!