Хранение и доступ к настройкам приложения
В 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.
Каждый раз при запуске приложения Настройки в iPhone производится сканирование папок всех установленных программ на предмет поиска в них setting bundle. Последний предоставляет информацию о том, как устроено окно настроек приложения и какие значения доступны к изменению.
Структура нового setting bundle представляет собой корневой plist-файл Root.plist, описывающий главное окно настроек, и папку локализации (больше о локализации можно узнать в предыдущей статье).
Приложение может иметь лишь одну страницу настроек, но если количество изменяемых параметров велико, то их можно разбить на несколько страниц, информация о каждой из которых заносится в отдельный property list файл. В состав setting bundle могут также входить файлы изображений. В частности, если среди них присутствует файл с названием Icon-Settings.png размером 29х29 пикселей, то эта иконка будет использоваться в приложении Настройки в iPhone, в противном случае в ее роли будет выступать иконка приложения, отмасштабированная до нужного размера.
Каждый property list файл содержит два корневых элемента:
- PreferenceSpecifiers — массив, описывающий элементы интерфейса
- StringsTable — имя файла перевода без расширения для строк, используемых на данной странице настроек
Раздел PreferenceSpecifiers уже содержит несколько значений в качестве примера. Вы можете попробовать запустить приложение и войти в Настройки, чтобы увидеть панель настроек приложения в действии.
В качестве названия раздела настроек для нашего приложения используется имя, указанное в файле Info.plist, в параметре Bundle Display Name.
Файл plist можно редактировать как property list редактором в XCode, так и в виде обычного XML-файла. Выбирайте более удобный для вас вариант, а я далее буду давать детальную информацию как для того, так и для другого. Уверен, что с системой редактирования property list вы разберетесь сами и описание этой операции не привожу.
Каждый блок, описывающий отдельный элемент интерфейса, имеет тип dictionary, включающий несколько ключей. В качестве элементов интерфейса можно использовать следующие варианты.
Текстовое поле ввода
Ключ: 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 — автокоррекция вводимого текста
Текстовое поле только для чтения
Ключ: Type (обязательный)
Тип: String
Значение: PSTitleValueSpecifier
Ключ: Title (обязательный)
Тип: String
Значение: Подпись к полю ввода
Ключ: Key (обязательный)
Тип: String
Значение: Название переменной
Ключ: DefaultValue (обязательный)
Тип: String или, в случае присутствия элементов Titles и Values, того же типа, что и переменные входящие в состав Values
Значение: Значение по умолчанию или одно из Values
Ключ: Titles
Тип: Array
Значение: Список отображаемых значений
Ключ: Values
Тип: Array
Значение: Список присваиваемых значений при выборе одного из элементов Titles. Количество элементов массивов Titles и Values должно совпадать
Переключатель
Ключ: Type (обязательный)
Тип: String
Значение: PSToggleSwitchSpecifier
Ключ: Title (обязательный)
Тип: String
Значение: Подпись к полю
Ключ: Key (обязательный)
Тип: String
Значение: Имя переменной
Ключ: DefaultValue (обязательный)
Тип: Boolean (или в случае присутвия двух следующих ключей, того же типа, что и TrueValue и FalseValue)
Значение: Значение по умолчанию
Ключ: TrueValue
Тип: Любого типа, кроме array и dictionary, но одинакового с FalseValue
Значение: Значение переменной при включеном переключателе
Ключ: FalseValue
Тип: Любого типа, кроме array и dictionary, но одинакового с TrueValue
Значение: Значение переменной при выключеном перелючателе
Слайдер
Ключ: Type (обязательный)
Тип: String
Значение: PSSliderSpecifier
Ключ: Key (обязательный)
Тип: String
Значение: Имя переменной
Ключ: DefaultValue (обязательный)
Тип: Number (вещественное)
Значение: Значение по умолчанию
Ключ: MinimumValue (обязательный)
Тип: Number (вещественное)
Значение: Минимальное значение
Ключ: MaximumValue (обязательный)
Тип: Number (вещественное)
Значение: Максимальнео значение
Ключ: MinimumValueImage
Тип: String
Значение: Картинка размером 21x21 пиксель, отображаемая слева от слайдера, и входящая в состав settings bundle
Ключ: MaximumValueImage
Тип: String
Значение: Картинка размером 21x21 пиксель, отображаемая справа от слайдера, и входящая в состав settings bundle
Список значений
Ключ: Type (обязательный)
Тип: String
Значение: PSMultiValueSpecifier
Ключ: Title (обязательный)
Тип: String
Значение: Подпись к полю
Ключ: Key (обязательный)
Тип: String
Значение: Имя переменной
Ключ: DefaultValue (обязательный)
Тип: Того же типа, что и элементы Values
Значение: Значение по умолчанию, одно из Values
Ключ: Titles (обязательный)
Тип: Array
Значение: Список отображаемых значений
Ключ: Values (обязательный)
Тип: Array
Значение: Список присваиваемых значений при выборе одного из элементов Titles. Количество элементов массивов Titles и Values должно совпадать
Группа элементов
Ключ: Type (обязательный)
Тип: String
Значение: PSGroupSpecifier
Ключ: Title (обязательный)
Тип: String
Значение: Название группы
Группирует элементы по одному признаку, определяемому разработчиком. Элементы объединяются в одну группу пока не встретится описание новой группы.
Ссылка на другую страницу настроек
Ключ: 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 для этих целей не подходит), поэтому ответственность за их создание и поддержание в актуальном состоянии ложиться на наши плечи, благо ничего сложного в этом нет.
Примеры кода:
Дополнительная информация:
Комментарии
А можно как-то обратиться к настройкам из самого приложения. Чтобы пользователь мог нажать на кнопку и попасть в экран настроек, а после изменения вернуться и продолжить работу?
А можно узнать про хранение данных в базе sqlite? Хотя бы в паре абзацев?
Можно, и даже не в паре слов, а в целой статье
Спасибо. Как-то я пропустил её.
Форма комментирования для «Хранение и доступ к настройкам приложения»
искал материалов на эту тему
нашел у вас
спасибо