Главная iPhone Mac OS X Форум О себе

Хранение и доступ к настройкам приложения

Application PreferencesВ Mac OS существует глобальная система для хранения настроек и предпочтений операционной системы и приложений — defaults system. Вся система подразделена на домены, отвечающие за хранение настроек, сгруппированных по области ответственности. Например, существует домен с настройками вашего приложения и домен со значениями, доступными всем программам. Программистам от Windows может показаться, что эта система похожа на реестр, но на деле сходство имеется лишь отдаленное.

Данные в defaults system хранятся, как и plist-файлы, в XML-подобном формате, что накладывает ограничения на тип хранимых величин. Каждая запись в базе состоит из трех компонентов:

  • домен, для которого сохраняется значение
  • имя переменной (NSString)
  • значение одного из перечисленных типов данных: NSData, NSString, NSNumber, NSDate, NSArray или NSDictionary (NSArray и NSDictionary также должны содержать значения только тех типов, что указаны выше)

Каждый раз, когда приложение обращается в defaults system, поиск значения запрашиваемой переменной в доменах осуществляется в последовательности, приведенной ниже. При этом значения, полученные от вышестоящих доменов, перекрывают значения переменных из последующих.

  • Agrument Domain. Этот домен хранит переменные, значения которых были указаны при запуске приложения из командной строки. Имя переменной указывается через дефис, а значение через пробел, например: Xcode.app/Contents/MacOS/Xcode -IndexOnOpen NO. Данные в этом домене хранятся только до момента выхода из приложения.
  • Application Domain. Хранит настройки программы и доступен до момента удаления приложения из системы. Как раз об этих настройках и доступе к ним пойдет речь в сегодняшней статье.
  • Global Domain. Хранит настройки, доступные всем приложениям. Например, он хранит значения скорости анимации окон, клавиатурные сокращения и масштаб элементов интерфейса.
  • Languages Domain. Хранит специфические языковые настройки. Эти значения хранятся до момента выхода из программы.
  • Registration Domain. Позволяет задать значения по умолчанию для настроек приложения. Эти значения также доступны только до момента выхода из программы, а это значит, что их нужно инициализировать заново каждый раз при старте приложения.

Для каждой пользовательской учетной записи в Mac OS создается своя база defaults system. За программный доступ к ней отвечает класс NSUserDefaults. Стандартный шаблон использования ограничивается получением доступа к хранилищу настроек через вызов standardUserDefaults, а затем получение (objectForKey:) или установку (setObject:forKey:) новых значений переменным из базы.

Но перед тем как приступать к плотной работе с defaults system, для всех переменных, значения которых планируется хранить в базе, рекомендуется установить значения по умолчанию, т.е. сделать запись в Registration Domain. За этот функционал ответственен метод registerDefaults класса NSUserDefaults. Для выполнения начальной инициализации переменных следует создать объект NSDictionary, где ключ будет определять имя переменной, а значение — ее величину по умолчанию для нашего приложения. Код, выполняющий описанную последовательность действий, может выглядеть следующим образом:


NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"YES" forKey:@"AutoSave"];
[defaults registerDefaults:appDefaults];

Не забудьте, что этот процесс нужно выполнять до момента обращения к переменным из defaults system, каждый раз при старте приложения, так как эти значения относятся к Registration Domain, который при выходе из программы очищается. По мере работы программы, вы будете записывать новые значения для объявленных переменных в defaults system, которые будут перекрывать значения по умолчанию из Registration Domain (вспомните порядок чтения значения из defaults system согласно доменам, в которых они хранятся, о котором я написал выше). Запись нового значения при помощи метода setObject:forKey: из класса NSUserDefaults производится в Application Domain, который сохраняет свое состояние между запусками приложения.


[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"AutoSave"];

Обращение к переменной происходит похожим образом.


[[NSUserDefaults standardUserDefaults] objectForKey:@"AutoSave"];

Чтобы не приводить полученный объект к нужному типу, можно воспользоваться готовыми методами, пытающимися произвести преобразование самостоятельно.


[[NSUserDefaults standardUserDefaults] boolForKey:@"AutoSave"];

Также в запасе у NSUserDefaults есть методы для каждого варианта допустимых к хранению типов значений: dictionaryForKey:, stringForKey:, integerForKey: и т.д.

Постоянным читателям моего сайта могла прийти мысль, что использование defaults system могло стать хорошим подспорьем в решении задачи сохранения данных между запусками приложения, поставленной в статье “Пишем правильный текстовый редактор”. Этот ход верен лишь частично. Следует остерегаться от хранения большого объема данных в defaults system — оно не предназначено для этих целей. А вот для хранения параметров запуска и состояния приложения такой способ подходит как нельзя лучше.

Так как переменные в defaults system могут быть изменены вне приложения, было бы неплохо иметь доступ к новым значениям в NSUserDefaults. Синхронизация данных класса достигается путем вызова метода synchronize, который, кроме чтения, делает еще и запись новых значений в defaults system. Прямой вызов данного метода в большинстве приложений не требуется, так как синхронизация проходит в автоматическом режиме в течении всей работы приложения. Этот метод находит применение лишь в многопоточных программах.

Менять значения в defaults system можно не только программных образом, к этим значениям можно подступиться и из консоли — командой defaults. Например, следующей командой можно включить показ скрытых файлов в Finder.


defaults write com.apple.finder AppleShowAllFiles TRUE

Или прочесть список настроек для выбранной программы.


defaults read com.apple.calculator

Если вы хотите сделать настройки приложения доступными к изменению пользователем, следует обратить свое внимание на уже готовые решения, которые предоставляет Cocoa, позволяющие создать панель настроек с минимальными затраченными усилиями. Для демонстрации ее возможностей создайте новый проект для iPhone. В окне создания нового файла в XCode есть раздел, позволяющий добавить setting bundle.

Окно добавления файла Settings.bundle в проект

Окно добавления файла Settings.bundle в проект

Каждый раз при запуске приложения Настройки в iPhone производится сканирование папок всех установленных программ на предмет поиска в них setting bundle. Последний предоставляет информацию о том, как устроено окно настроек приложения и какие значения доступны к изменению.

Структура нового setting bundle представляет собой корневой plist-файл Root.plist, описывающий главное окно настроек, и папку локализации (больше о локализации можно узнать в предыдущей статье).

Состав пакета Settings.bundle

Приложение может иметь лишь одну страницу настроек, но если количество изменяемых параметров велико, то их можно разбить на несколько страниц, информация о каждой из которых заносится в отдельный property list файл. В состав setting bundle могут также входить файлы изображений. В частности, если среди них присутствует файл с названием Icon-Settings.png размером 29х29 пикселей, то эта иконка будет использоваться в приложении Настройки в iPhone, в противном случае в ее роли будет выступать иконка приложения, отмасштабированная до нужного размера.

Каждый property list файл содержит два корневых элемента:

  • PreferenceSpecifiers — массив, описывающий элементы интерфейса
  • StringsTable — имя файла перевода без расширения для строк, используемых на данной странице настроек

Root.plist по умолчанию

Раздел PreferenceSpecifiers уже содержит несколько значений в качестве примера. Вы можете попробовать запустить приложение и войти в Настройки, чтобы увидеть панель настроек приложения в действии.

Панель настроек приложения

В качестве названия раздела настроек для нашего приложения используется имя, указанное в файле Info.plist, в параметре Bundle Display Name.

Файл plist можно редактировать как property list редактором в XCode, так и в виде обычного XML-файла. Выбирайте более удобный для вас вариант, а я далее буду давать детальную информацию как для того, так и для другого. Уверен, что с системой редактирования property list вы разберетесь сами и описание этой операции не привожу.

Способы редактирования XML-файлов

Каждый блок, описывающий отдельный элемент интерфейса, имеет тип dictionary, включающий несколько ключей. В качестве элементов интерфейса можно использовать следующие варианты.


Текстовое поле ввода

PSTextFieldSpecifier

PSTextFieldSpecifier

Ключ: Type (обязательный)
Тип: String
Значение: PSTextFieldSpecifier

Ключ: Title (обязательный)
Тип: String
Значение: Подпись к полю ввода

Ключ: Key (обязательный)
Тип: String
Значение: Название переменной, ассоциированной с полем ввода

Ключ: DefaultValue
Тип: String
Значение: Текст в поле ввода по умолчанию

Ключ: IsSecure
Тип: Boolean
Значение: YES/NO — используется для полей ввода паролей

Ключ: KeyboardType
Тип: String
Значение: Alphabet (по умолчанию), NumbersAndPunctuation, NumberPad, URL или EmailAddress — описывает ограничение набора символов, доступных для ввода в текстовое поле, и тип вводимого значения

Ключ: AutocapitalizationType
Тип: String
Значение: None (по умолчанию), Sentences, Words или AllCharacters — определяет стиль выделения заглавными буквами вводимого текста

Ключ: AutocorrectionType
Тип: String
Значение: Default (по умолчанию), No или Yes — автокоррекция вводимого текста


Текстовое поле только для чтения

PSTitleValueSpecifier

PSTitleValueSpecifier

Ключ: Type (обязательный)
Тип: String
Значение: PSTitleValueSpecifier

Ключ: Title (обязательный)
Тип: String
Значение: Подпись к полю ввода

Ключ: Key (обязательный)
Тип: String
Значение: Название переменной

Ключ: DefaultValue (обязательный)
Тип: String или, в случае присутствия элементов Titles и Values, того же типа, что и переменные входящие в состав Values
Значение: Значение по умолчанию или одно из Values

Ключ: Titles
Тип: Array
Значение: Список отображаемых значений

Ключ: Values
Тип: Array
Значение: Список присваиваемых значений при выборе одного из элементов Titles. Количество элементов массивов Titles и Values должно совпадать


Переключатель

PSToggleSwitchSpecifier

PSToggleSwitchSpecifier

Ключ: Type (обязательный)
Тип: String
Значение: PSToggleSwitchSpecifier

Ключ: Title (обязательный)
Тип: String
Значение: Подпись к полю

Ключ: Key (обязательный)
Тип: String
Значение: Имя переменной

Ключ: DefaultValue (обязательный)
Тип: Boolean (или в случае присутвия двух следующих ключей, того же типа, что и TrueValue и FalseValue)
Значение: Значение по умолчанию

Ключ: TrueValue
Тип: Любого типа, кроме array и dictionary, но одинакового с FalseValue
Значение: Значение переменной при включеном переключателе

Ключ: FalseValue
Тип: Любого типа, кроме array и dictionary, но одинакового с TrueValue
Значение: Значение переменной при выключеном перелючателе


Слайдер

PSSliderSpecifier

PSSliderSpecifier

Ключ: Type (обязательный)
Тип: String
Значение: PSSliderSpecifier

Ключ: Key (обязательный)
Тип: String
Значение: Имя переменной

Ключ: DefaultValue (обязательный)
Тип: Number (вещественное)
Значение: Значение по умолчанию

Ключ: MinimumValue (обязательный)
Тип: Number (вещественное)
Значение: Минимальное значение

Ключ: MaximumValue (обязательный)
Тип: Number (вещественное)
Значение: Максимальнео значение

Ключ: MinimumValueImage
Тип: String
Значение: Картинка размером 21x21 пиксель, отображаемая слева от слайдера, и входящая в состав settings bundle

Ключ: MaximumValueImage
Тип: String
Значение: Картинка размером 21x21 пиксель, отображаемая справа от слайдера, и входящая в состав settings bundle


Список значений

PSMultiValueSpecifier

PSMultiValueSpecifier

Ключ: Type (обязательный)
Тип: String
Значение: PSMultiValueSpecifier

Ключ: Title (обязательный)
Тип: String
Значение: Подпись к полю

Ключ: Key (обязательный)
Тип: String
Значение: Имя переменной

Ключ: DefaultValue (обязательный)
Тип: Того же типа, что и элементы Values
Значение: Значение по умолчанию, одно из Values

Ключ: Titles (обязательный)
Тип: Array
Значение: Список отображаемых значений

Ключ: Values (обязательный)
Тип: Array
Значение: Список присваиваемых значений при выборе одного из элементов Titles. Количество элементов массивов Titles и Values должно совпадать


Группа элементов

PSGroupSpecifier

PSGroupSpecifier

Ключ: Type (обязательный)
Тип: String
Значение: PSGroupSpecifier

Ключ: Title (обязательный)
Тип: String
Значение: Название группы

Группирует элементы по одному признаку, определяемому разработчиком. Элементы объединяются в одну группу пока не встретится описание новой группы.


Ссылка на другую страницу настроек

PSChildPaneSpecifier

PSChildPaneSpecifier

Ключ: Type (обязательный)
Тип: String
Значение: PSChildPaneSpecifier

Ключ: Title (обязательный)
Тип: String
Значение: Подпись к элементу

Ключ: File (обязательный)
Тип: String
Значение: Имя plist-файла без расширения, описывающего новую страницу настроек и входящего в состав settings bundle

Значения из полей DefaultValue устанавливаются в том случае, если отсутствует значение связанной с ними переменной в defaults system. Все значения из полей Title и Titles поддаются локализации.

Чтобы изменения, внесенные в файлы plist вступили в силу, временами требуется удалить приложение из эмулятора и заново запустить его из XCode.

Чтобы добавить новый файл (plist, изображение или файл перевода) в состав settings bundle, необходимо зайти в папку проекта, нажать правой кнопкой на файл Settings.bundle и выбрать “Показать содержание пакета”. После того, как вы включите новые файл в состав пакета, обновите список файлов Settings.bundle в XCode, свернув и развернув структуру файла настроек в списке файлов.

Напоследок хочу сказать, что информации о способе автоматического формирования файлов переводов я не нашел (genstrings для этих целей не подходит), поэтому ответственность за их создание и поддержание в актуальном состоянии ложиться на наши плечи, благо ничего сложного в этом нет.

Примеры кода:

Дополнительная информация:

Комментарий

Комментарии

Иван 27.01.2020 9:31

А можно как-то обратиться к настройкам из самого приложения. Чтобы пользователь мог нажать на кнопку и попасть в экран настроек, а после изменения вернуться и продолжить работу?

ответить
igorekk 4.02.2020 22:15

А можно узнать про хранение данных в базе sqlite? Хотя бы в паре абзацев?

ответить
Evgeniy Krysanov 4.02.2020 23:09

Можно, и даже не в паре слов, а в целой статье

ответить
igorekk 5.02.2020 9:03

Спасибо. Как-то я пропустил её.

ответить
russinow 5.10.2020 0:45

искал материалов на эту тему

нашел у вас

спасибо

ответить

Форма комментирования для «Хранение и доступ к настройкам приложения»

Обязательное поле. Не больше 30 символов.

Обязательное поле

captcha image Пожалуйста, введите символы, которые вы видите на изображении

Карта Сайта