iOS. Приемы программирования Нахавандипур Вандад
<dict>
<key>application-identifier</key>
<string>F3FU372W5M.com.pixolity.ios.cookbook.PushNotificationApp</string>
<key>aps-environment</key>
<string>development</string>
<key>get-task-allow</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>F3FU372W5M.*</string>
</array>
</dict>
</plist>
Значения, приведенные в рассматриваемых здесь листингах, относятся к профилям, которые создал я. В вашем профиле будут другие значения и, конечно же, будет другой идентификатор приложения (App ID). Поэтому внимательно выполните предыдущие шаги, чтобы правильно создать идентификатор вашего приложения и профиль. Потом возьмите разрешения из своего профиля и вставьте их в файл Enh2ments.plist вашего проекта.
14. Далее перейдите в настройки сборки вашего проекта. В разделе Code Signing Enh2ments (Разрешения для подписания кода) введите значение $(SRCROOT)/$(TARGET_NAME)/Enh2ments.plist (если создали файл разрешений в целевом каталоге проекта) или $(SRCROOT)/Enh2ments.plist (если создали файл разрешений в корневом каталоге, включающем в себя весь исходный код). Если не знаете, какой вариант выбрать, просто попробуйте оба этих значения, попытайтесь собрать проект с каждым из них. Если Xcode сообщит, что не может найти файл разрешений, поставьте второе значение, и оно сработает. Настройка сборки Code Signing Enh2ments (Разрешения для подписания кода) требует указания относительного пути, идущего к файлу разрешений от корневого каталога исходного кода. Поэтому, если вы поместите файл разрешений в какой-то другой каталог, вам потребуется выстроить этот путь вручную, а потом записать полученный путь в это поле.
15. Соберите проект и убедитесь, что Xcode не выдает никаких ошибок. Если получите сообщение о какой-либо ошибке, то, вероятно, дело в том, что вы задали неверный профиль инициализации либо указали в настройках сборки неверный путь к файлу разрешений для подписания кода.
16. В делегате вашего приложения вызовите метод registerForRemoteNotificationTypes:, относящийся к классу UIApplication, а затем передайте этому методу значения UIRemoteNotificationTypeAlert, UIRemoteNotificationTypeBadge и UIRemoteNotificationTypeSound, как показано далее:
— (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound];
self.window = [[UIWindow alloc]
initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Так вы зарегистрируете ваше приложение для получения пуш-уведомлений. Такие уведомления могут содержать информацию с предупреждениями, изменения номера ярлыка вашего приложения, а также звуки. Пока не будем вдаваться в эти подробности. Просто зарегистрируйте приложение для получения пуш-уведомлений, как показано ранее. Как только вы это сделаете, iOS отошлет запрос о регистрации к APNS. Перед тем как это делать, iOS попросит у пользователя разрешения зарегистрировать данное приложение для получения пуш-уведомлений. Пользовательский интерфейс, открывающийся в iOS при этом запросе, показан на рис. 15.7.
Рис. 15.7. iOS выдает пользователю запрос о разрешении на получение пуш-уведомлений
17. Теперь реализуйте метод application: didRegisterForRemoteNotificationsWithDeviceToken: делегата вашего приложения. Этот метод вызывается, когда iOS удается успешно зарегистрировать устройство в APNS и присвоить ему маркер. Этот маркер действует только для конкретного приложения, установленного именно на данном устройстве.
18. Далее реализуйте метод application: didFailToRegisterForRemoteNotificationsWithError: делегата вашего приложения. Этот метод вызывается, если iOS не удается зарегистрировать приложение для получения пуш-уведомлений. Это может произойти потому, что ваш профиль настроен неправильно или устройство не подключено к Интернету, а также по многим другим причинам. В параметре didFailToRegisterForRemoteNotificationsWithError этого метода вы получите ошибку типа NSError. Ее можно проанализировать и узнать, по какой причине возникла проблема.
Теперь вы знаете все необходимое, чтобы настроить ваше приложение на получение пуш-уведомлений.
См. также
Раздел 15.0.
15.8. Доставка пуш-уведомлений в приложение
Постановка задачи
Требуется отправлять пуш-уведомления на пользовательские устройства, которые зарегистрированы для получения таких уведомлений.
Решение
Убедитесь, что вы собрали маркеры-идентификаторы пуш-уведомлений этих приложений (см. раздел 15.7). Затем сгенерируйте SSL-сертификаты, которые будут использоваться вашими веб-сервисами для отправки пуш-уведомлений на устройства. Затем создайте простой веб-сервис для отправки пуш-уведомлений на зарегистрированные устройства.
Этот материал — продолжение раздела 15.7. Обязательно прочтите предыдущий раздел и полностью в нем разберитесь, прежде чем переходить к изучению данного раздела.
Обсуждение
Чтобы обмениваться информацией с серверами APNS, ваши веб-сервисы должны совершить акт квитирования[11] (handshaking). Это обмен сигналами с сервером, при котором используется выданный Apple SSL-сертификат. Чтобы сгенерировать такой сертификат, выполните следующие шаги.
1. Войдите в центр разработки для iOS.
2. Перейдите в раздел Certificates, Identifiers & Profiles (Сертификаты, идентификаторы, профили), расположенный справа.
3. В разделе идентификаторов приложений (App ID) найдите идентификатор вашего приложения, для которого задано получение пуш-уведомлений, выберите этот идентификатор и нажмите кнопку Settings (Настройки), чтобы его сконфигурировать, как показано на рис. 15.8.
Рис. 15.8. Изменение настроек имеющегося идентификатора приложения
4. В разделе настроек, называемом Push Notifications (Пуш-уведомления), найдите подраздел Development SSL Certificate (SSL-сертификат для разработки) и нажмите кнопку Create Certificate (Создать сертификат) (рис. 15.9). Далее следуйте указаниям Apple по созданию сертификата. Пока мы создаем SSL-сертификат для разработки приложения, поскольку на данном этапе нас интересует исключительно разработка. Позже, когда будем готовы отправить наше приложение в App Store, просто повторите подобный процесс и создайте SSL-сертификаты для распространения (Distribution).
Рис. 15.9. Создание SSL-сертификата, используемого для операций с пуш-уведомлениями и разработки приложения
5. Как только сертификат будет готов (рис. 15.10), скачайте его на компьютер и дважды щелкните на нем кнопкой мыши, чтобы импортировать его в вашу связку ключей.
Рис. 15.10. SSL-сертификат для обмена информацией с APNS на этапе разработки приложения готов к скачиванию
6. Теперь откройте окно Keychain Access (Доступ к связке ключей) на OS X и перейдите к связке ключей Login (если эта связка ключей задана у вас по умолчанию). В разделе My Certificates (Мои сертификаты) найдите тот сертификат, который вы только что импортировали в связку ключей, и раскройте его, щелкнув на маленькой кнопке со стрелкой слева. Так вы узнаете ассоциированный с этим приложением закрытый ключ (рис. 15.11).
Рис. 15.11. Сертификат для работы с пуш-уведомлениями на этапе разработки и его закрытый ключ
7. Щелкните на сертификате правой кнопкой мыши и экспортируйте его как. cer-сертификат (а не как файл. p12). Назовите его PushCertificate.cer.
8. Щелкните правой кнопкой мыши на закрытом ключе и экспортируйте его как файл. p12 (а не как файл сертификата). Назовите его PushKey.p12. Здесь потребуется указать пароль к закрытому ключу. Обязательно используйте такой пароль, который впоследствии сможете вспомнить.
Отлично. Теперь, чтобы упростить дальнейший материал этого раздела, перейдем к использованию PHP. Применим этот язык для отправки на наше устройство простого пуш-уведомления. В разделе 15.7 мы уже настроили получение пуш-уведомлений в этом приложении. Поскольку настройка использования PHP на сервере Apache не относится к темам этой книги, мы воспользуемся упрощенным вариантом и применим MAMP. MAMP установит у вас на компьютере Apache и PHP, если вы еще не сделали этого. На сайте MAMP даются подробные инструкции о том, какие шаги потребуется выполнить. Когда вы установите MAMP, корневой каталог со всеми вашими PHP-файлами будет находиться по адресу /Applications/MAMP/htdocs/. Если при последующей установке этот каталог изменится, откройте MAMP, перейдите в раздел Preferences (Настройки), а потом — к Apache. Вы найдете там и корневой каталог Apache.
Чтобы наш PHP-сценарий мог обмениваться информацией с APNS, потребуется задать ему SSL-сертификат, который мы сгенерировали ранее, на портале разработки для iOS. Именно поэтому мы извлекли. cer-файл сертификата и файл. p12 закрытого ключа — теперь мы должны задать эти файлы нашему PHP-сценарию. Чтобы это сработало, используем в окне терминала openssl, а затем комбинируем сертификат и закрытый ключ. p12 в едином PEM-файле. В этой книге мы не можем подробно поговорить о PEM-файлах — это тема для отдельной книги. Однако вы можете подробнее познакомиться с этой темой в документе RFC 1421.
Для создания PEM-файла выполните следующие шаги (предполагается, что вы уже экспортировали файлы PushKey.p12 и PushCertificate.cer на ПК, как было описано ранее).
1. Откройте окно терминала (Terminal) в OS X. Введите в окне терминала следующую команду:
openssl x509 — in PushCertificate.cer — inform der — out PushCertificate.pem
2. Чтобы преобразовать файл. p12 в PEM-файл, введите в окне терминала следующую команду:
openssl pkcs12 — nocerts — in PushKey.p12 — out PushKey.pem
3. Система потребует ввести пароль, который вы задали для этого закрытого ключа, когда экспортировали его из раздела Keychain Access. После того как пароль для импорта будет проверен и подтвержден, OpenSSL запросит у вас фразу-пароль для результирующего PEM-файла. Этот пароль должен содержать не менее четырех символов. Задайте такой пароль и хорошо запомните его для последующего использования.
4. Теперь у вас на ПК должно быть два PEM-файла: PushCertificate.pem и PushKey.pem. Нужно сложить их в единый PEM-файл — этот формат распознается PHP. Для этого воспользуйтесь следующей командой:
cat PushCertificate.pem PushKey.pem > PushCertificateAndKey.pem
5. Теперь проверим, сможем ли мы подключиться к песочнице (речь идет о тестовой версии, только для целей разработки) с помощью сгенерированных нами PEM-файлов. В качестве песочницы используется защищенный APNS-сервер. Выполните в окне терминала следующую команду:
openssl s_client — connect gateway.sandbox.push.apple.com:2195 \
— cert PushCertificate.pem — key PushKey.pem
Если все будет нормально, то на данном этапе потребуется ввести фразу-пароль для вашего закрытого ключа. Помните ее? Хорошо, вводите. Если соединение будет успешно установлено, откроется OpenSSL, ожидающий от вас нескольких символов в качестве ввода. После получения этого ввода соединение будет закрыто. Введите любые случайные символы и нажмите Enter (Ввод). Соединение закрыто. Вам удалось успешно связаться с APNS-сервером, воспользовавшись вашим сертификатом и закрытым ключом.
Теперь напишем несложный PHP-сценарий для отправки простого пуш-уведомления на устройство. Но прежде чем мы двинемся дальше, необходимо получить маркер пуш-уведомлений, действующий на нашем устройстве. Мы должны получить его в таком формате, который понятен PHP. iOS инкапсулирует маркер пуш-уведомления в экземпляр NSData, но PHP неизвестно, что такое NSData. Нам нужно преобразовать этот маркер в строку, которую мы сможем использовать в нашем PHP-сценарии. Для этого считываем весь маркер байт за байтом и преобразуем каждый байт в шестнадцатеричное строковое представление:
— (void) application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
/* Каждый байт данных будет преобразован в свое шестнадцатеричное
значение, например 0x01 или 0xAB. При этом часть 0x отбрасывается.
Итак, для представления 1 байта нам потребуются два символа, поэтому
здесь указано * 2 */
NSMutableString *tokenAsString = [[NSMutableString alloc]
initWithCapacity: deviceToken.length * 2];
char *bytes = malloc(deviceToken.length);
[deviceToken getBytes: bytes];
for (NSUInteger byteCounter = 0;
byteCounter < deviceToken.length;
byteCounter++){
char byte = bytes[byteCounter];
[tokenAsString appendFormat:@"%02hhX", byte];
}
free(bytes);
NSLog(@"Token = %@", tokenAsString);
}
Запустите ваше приложение и убедитесь, что маркер устройства выводится на консоль, вот так:
Token = 05924634A8EB6B84437A1E8CE02E6BE6683DEC83FB38680A7DFD6A04C6CC586E
Отметьте себе этот маркер устройства, так как мы будем пользоваться им в PHP-сценарии:
<?php
/* При разработке мы пользуемся защищенной версией APNS. При подготовке
приложения для использования в реальных условиях измените это значение
на ssl://gateway.push.apple.com:2195 */
$apnsServer = 'ssl://gateway.sandbox.push.apple.com:2195';
/* Убедитесь, что это значение совпадает с паролем, который вы задали
для закрытого ключа при экспорте в.pem-файл, когда использовали openssl
в системе OS X */
$privateKeyPassword = '1234';
/* Если хотите, запишите здесь собственное сообщение */
$message = 'Welcome to iOS 7 Push Notifications';
/* Запишите здесь маркер вашего устройства */
$deviceToken =
'05924634A8EB6B84437A1E8CE02E6BE6683DEC83FB38680A7DFD6A04C6CC586E';
/* Замените эту информацию именем файла, указанного в файле вашего
сценария. В этом файле должны содержаться сгенерированные вами ранее
сертификат и закрытый ключ */
$pushCertAndKeyPemFile = 'PushCertificateAndKey.pem';
$stream = stream_context_create();
stream_context_set_option($stream,
'ssl',
'passphrase',
$privateKeyPassword);
stream_context_set_option($stream,
'ssl',
'local_cert',
$pushCertAndKeyPemFile);
$connectionTimeout = 20;
$connectionType = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT;
$connection = stream_socket_client($apnsServer,
$errorNumber,
$errorString,
$connectionTimeout,
$connectionType,
$stream);
if (!$connection){
echo «Failed to connect to the APNS server. Error no = $errorNumber<br/>»;
exit;
} else {
echo «Successfully connected to the APNS. Processing…</br>»;
}
$messageBody['aps'] = array('alert' => $message,
'sound' => 'default',
'badge' => 2,
);
$payload = json_encode($messageBody);
$notification = chr(0).
pack('n', 32).
pack('H*', $deviceToken).
pack('n', strlen($payload)).
$payload;
$wroteSuccessfully = fwrite($connection, $notification,
strlen($notification));
if (!$wroteSuccessfully){
echo «Could not send the message<br/>»;
}
else {
echo «Successfully sent the message<br/>»;
}
fclose($connection);
Если вы даже не программируете на PHP, внимательно просмотрите этот сценарий и почитайте комментарии к нему. Обязательно замените все значения в этом сценарии теми, что соответствуют вашему приложению. Например, используемый здесь маркер относится к моему устройству. Пользуйтесь маркером своего устройства, который выяснили ранее в этом разделе. У вас будут иные пароли, pem-файлы, скорее всего, будут находиться в других местах. Ради дополнительного упрощения этого раздела я поместил свой PHP-сценарий в тот же каталог, в котором ранее сохранил закрытый ключ и. pem-файл сертификата (PushCertificateAndKey.pem). Поэтому я могу обращаться к. pem-файлу просто по его имени.
Если вы правильно выполнили все шаги и инструкции, описанные в этом разделе, то ваш PHP-сценарий должен открываться в браузере. После этого на устройство начнут поступать уведомления. Сценарий посылает уведомление на APNS-сервер, который уже доставляет это уведомление на устройство. Когда пуш-уведомление попадает на устройство (предполагается, что на устройстве в этот момент отображается экран блокировки), вы увидите на экране примерно такую картинку, какая показана на рис. 15.12.
Рис. 15.12. Пуш-уведомление, отображенное на экране блокировки
См. также
Раздел 15.7.
15.9. Реагирование на пуш-уведомления
Постановка задачи
Проработав раздел 15.8, вы научились доставлять в ваше приложение пуш-уведомления, но не знаете, как реагировать на них в программе.
Решение
Реализуйте метод application: didReceiveRemoteNotification: делегата вашего приложения.
Обсуждение
Метод application: didReceiveRemoteNotification: делегата вашего приложения вызывается всякий раз, когда в систему iOS поступает пуш-уведомление и пользователь реагирует на это уведомление каким-то образом, инициируя открытие приложения. Этот метод срабатывает, когда приложение функционирует в приоритетном или в фоновом режиме, а не завершено. Например, пользователь может проигнорировать поступившее уведомление. Тогда и этот метод вызван не будет. Если пользователь нажимает на экране окно с пуш-уведомлением, в результате чего ваше приложение открывается, то iOS, открыв это приложение, переводит программу в приоритетный режим. После этого в делегате вашего приложения будет вызван вышеупомянутый метод.
Если приложение завершено и не работает даже в фоновом режиме, то iOS инкапсулирует пуш-уведомление, инициирующее запуск приложения, в параметрах запуска. Эти параметры будут переданы методу application: didFinishLaunchingWithOptions: делегата вашего приложения. Чтобы получить объект уведомления, просто запросите параметр didFinishLaunchingWithOptions этого метода (относящийся к типу NSDictionary) и поищите ключ UIApplicationLaunchOptionsRemoteNotificationKey. Значением этого ключа и будет то пуш-уведомление, которое запустило ваше приложение.
Параметр didReceiveRemoteNotification этого свойства несет в себе словарь типа NSDictionary. Этот словарь будет содержать в себе корневой объект под названием aps. Ниже этого объекта располагается словарь со следующими ключами, зависящими от того, как сервер создал пуш-уведомление (сервер может и не прислать все ключи сразу):
• badge — значением этого ключа является номер, который будет задан для ярлыка (значка) вашего приложения;
• alert — это содержащееся в пуш-уведомлении сообщение типа String. Сервер может прислать вам модифицированную версию значения этого ключа. Такое значение само по себе будет словарем, содержащим ключи body и show-view. Если вам будет прислана такая модифицированная версия предупреждения, то ключ body будет содержать именно тот текст, который находится в теле предупреждения. Ключ show-view будет содержать логическое значение, определяющее, должно ли предупреждение отображаться для пользователя. Кнопка Action (Действие) позволяет пользователю нажать на поступившее уведомление в центре уведомлений, чтобы открыть ваше приложение;
• sound — это строка, указывающая имя звукового файла, который должно воспроизводить ваше приложение;
• content-available — значением этого ключа является число. Если здесь стоит число 1, это означает, что на сервере появился новый контент, который приложение может скачать. Сервер может послать этот ключ приложению, затребовав таким образом, чтобы оно забрало с сервера список новых элементов. Ваше приложение может и не выполнять это требование. Это просто протокол, действующий между сервером и клиентом. Если в вашем приложении это целесообразно, следуйте данному протоколу.
См. также
Раздел 15.8.
Глава 16. Фреймворк Core Data
16.0. Введение
Core Data — это мощный фреймворк, входящий в состав iOS SDK. Он позволяет программисту сохранять данные и управлять ими объектно-ориентированным способом. Традиционно программисту приходилось сохранять данные на диске, пользуясь архивационными возможностями Objective-C, либо записывать данные в файлы, а потом управлять ими вручную. С появлением Core Data программист может просто взаимодействовать с его объектно-ориентированным интерфейсом и эффективно управлять своими данными. В этой главе будет рассмотрено, как использовать Core Data для создания модели своего приложения (с применением программной архитектуры «модель — вид — контроллер»).
Фреймворк Core Data обеспечивает низкоуровневые взаимодействия с хранилищем данных устройства, то есть взаимодействия, незаметные для программиста. iOS сама определяет, как будет организовано низкоуровневое управление данными. Для реализации такого взаимодействия программисту достаточно знать, какой высокоуровневый API для этого предназначен. Но при этом важно понимать и структуру фреймворка Core Data, его внутреннее функционирование. Чтобы лучше с этим разобраться, создадим приложение, использующее Core Data.
Теперь у нас есть новый компилятор LLVM, поэтому, чтобы включить фреймворк Core Data в ваш проект, достаточно включить обобщающий заголовок этого фреймворка, вот так:
#import «AppDelegate.h»
#import <CoreData/CoreData.h>
@implementation AppDelegate
<# Остаток вашего кода находится здесь #>
Для работы с Core Data необходимо понимать стек этого фреймворка, который составляют следующие основные элементы:
• постоянное хранилище данных — объект, представляющий находящуюся на диске базу данных. Мы никогда не используем этот объект непосредственно;
координатор постоянного хранилища данных — объект, координирующий считывание информации из постоянного хранилища и запись в него. Координатор — это промежуточное звено между контекстом управляемых объектов и постоянным хранилищем данных;
модель управляемого объекта (MOM) — обычный файл на диске, который будет представлять нашу модель данных. Считайте, что это схема базы данных;
управляемый объект — этот класс представляет сущность, которую мы хотим сохранить в Core Data. В традиционном программировании баз данных такие сущности называются таблицами. Управляемый объект относится к типу NSManagedObject, экземпляры таких объектов помещаются в контекстах управляемых объектов. Они соответствуют схеме, заложенной в модели управляемого объекта, и сохраняются в постоянном хранилище данных с помощью координатора;
контекст управляемых объектов — его можно сравнить с виртуальной приборной панелью. Странно звучит, да? Сейчас все будет понятно. Мы создаем объекты Core Data в памяти, задаем их свойства и манипулируем ими. Все эти операции происходят в контексте управляемого объекта. Контекст отслеживает все операции, совершаемые над управляемыми объектами, и даже позволяет нам отменять такие действия. Представьте другую метафору: ваши управляемые объекты, находящиеся в контексте, — это игрушки, а сам контекст — это стол, на который вы их положили. Игрушки можно передвигать на столе, разбирать, убирать какие-то со стола и класть на их место новые. Итак, стол — это контекст управляемых объектов. Закончив манипуляции с объектами, вы можете сохранить его состояние. Когда мы сохраняем состояние контекста управляемых объектов, информация об операции сохранения передается в постоянное хранилище данных. Это делается посредством координатора, к которому подключен контекст. На основании этой информации координатор хранилища данных будет записывать информацию в постоянное хранилище данных, а затем — на диск.
Чтобы добавить Core Data в свой проект, а затем приступить к использованию всех его классных возможностей, просто создайте проект. Затем, когда система спросит, добавлять ли к нему Core Data, установите соответствующий флажок (рис. 16.1).
Рис. 16.1. Добавление Core Data к новому проекту Xcode
После того как вы создадите проект с Core Data, у делегата вашего приложения появится ряд новых свойств:
NSManagedObjectContext *managedObjectContext;
NSManagedObjectModel *managedObjectModel;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
Вы уже понимаете, что означают эти свойства, — они были описаны ранее в данной главе. Контекст — наш игровой стол, модель — схема базы данных, а координатор — объект, опосредующий сохранение контекста на диске. Все просто. Теперь переходим к изучению основных разделов этой главы.
16.1. Создание модели Core Data с помощью Xcode
Постановка задачи
Требуется визуально спроектировать в Xcode модель данных для вашего приложения iOS.
Решение
Следуя инструкциям из введения к данной главе, создайте проект Core Data. Потом найдите в пакете вашего приложения файл с расширением xcdatamodel и откройте его в визуальном редакторе данных (рис. 16.2).