7 способов создания приложений для iPhone на Windows
Какое-то время назад я решил привести пару способов создания приложения для iPhone и iPad, доступных на Windows. Сейчас эта тема всё ещё актуальна. Так что я решил дополнить этот список и включить некоторые другие способы, появившиеся после написания первого сообщения, и те, которые всё ещё находятся в разработке, но уже привлекают, кажутся интересными. Обратите внимание на то, что официальных программ под Windows для разработки приложений на iPhone не существует (и они вряд ли когда-нибудь появятся).
На данной стадии если вы хотите выложить своё приложение в iPhone AppStore от Apple, то вам придётся компилировать его на официальном ПО для iPhone, которое доступно для Mac OS X, использовать Flash’s iPhone Packager, Airplay SDK, либо отправить кому-нибудь свой код для компилирования. Сейчас же, я знаю, есть одно «хакинтошское» решение, но я не буду о нём рассказывать, не только потому, что оно, возможно, нелегальное, но я видел того, кто попробовал так делать. и это превратилось в долгую мороку — на каждое обновление ОС и SDK уходили часы, когда они могли просто купить Mac Mini меньше чем за $700 и не мучиться.
Говорят, что есть способы разработки приложений для iPhone на Windows. Повторюсь, что никакого издания официального SDK для iOS не существует — и я более чем уверен, что не существует никаких официальных способов, несмотря на все маркетинговые речи, которые произносят некоторые продавцы. Если вы увидите, как какой-нибудь продавец утверждает, что их продукт — это официальный SDK для разработки приложений для iPhone на Windows, мой вам совет: держитесь от него подальше.
Я разделяю указанные здесь способы на те, с помощью которых уже были созданы приложения, доступные в продаже, и те, которые, на мой взгляд, особенно заслуживают внимания.
Данные альтернативы для Windows включают игровые движки, html/javascript решения для создания гибридных приложений, а также обычные программы для создания приложений для iPhone/iPad.
Способы создания приложений для iPhone на Windows
- Flash CS5.5 — является очень хорошим решением совместно с Flash iPhone Packager. Упаковщик для iOS позволяет компилировать приложения на платформе Windows. Если вы уже использовали Флэш, то вы являетесь обладателем новейших стандартов Apple. Он хорошо работает, однако не ждите, что игры с высокими техническими требованиями, а также Papervision на нём запустятся. Наличие компьютера с Mac не требуется. Кто-то спрашивал, возможно ли создание iOS флэш-приложений в среде, отличной от официальной среды Adobe. безусловно, вы можете компилировать приложения с помощью компилятора Flex и упаковывать их до тех пор, пока вы следуете рекомендациям ASME.
- Airplay SDK — Airplay SDK позволяет вам создавать игры и подписывать свои приложения на Windows. Отличное решение как для 2D, так и для 3D игр и приложений, благодаря бесплатному предложению от независимых разработчиков.
Оба этих варианта могут компилировать стандартный код iPhone прямо на вашей Windows. Большое количество приложений, доступных в AppStore, было создано с их помощью. - Unity 3D — игровой движок и редактор, использующий Boo и C# и работающий как на Windows, так и на Mac.
- Stonetrip S3D — игровой движок и редактор, использующий LUA и работающий на Windows.
Эти игровые движки позволяют вам создавать приложения на Windows, однако для их тестирования/компилирования вам потребуется Mac. - Appcelerator Titanium — Хорошее решение, имеющее довольно широкую и живую аудиторию. Приложения не требуют высокой производительности, так как они создаются с использованием html/javascript и могут быть скомпилированы как стандартные приложения и отсылаться в AppStore.
- Genuitec MobiOne — это создатель интерфейса, который использует фреймворк PhoneGap для обеспечения стандартной функциональности. Он работает только на Windows и базируется на HTML/Javascript.
Данные способы позволяют создавать приложения с помощью web-технологий (HTML/CSS/Javascript) и в последствии запускать их через Objective-C фреймворк, который отвечает на действия в UIWebView, тем самым предоставляя вам доступ к стандартной функциональности на iPhone. Эти фреймворки бесплатные. Для стандартного тестирования/компиляции потребуется Mac. - Dragonfire SDK — Интересный способ, позволяющий создавать приложения, использующие их собственные SDK на Windows. Далее вы отправляете им свой исходный код, который они компилируют, и вы скачиваете уже готовый файл iOS приложения. Вы загружаете свой код для компилирования на их сервер (при условии, что у вас стоит версия Ultimate). Я его упомянул, так как мне нравится его синтакс, и он довольно популярен, а также является хорошим упаковщиком с удачным симулятором на Windows.
Существует ещё пара других решений, которые либо ещё тестируются, либо являются платными без каких-либо trial-версий (или даже в некоторых случаях без скриншотов), либо просто ещё не вышли. Многие из них, по-видимому, являются упрощёнными версиями Dragonfly SDK, который позволяет разрабатывать iPhone приложения на Windows путём компилирования вашего исходного кода.. Airplay SDK, возможно, на данный момент является наиболее продвинутым «iOS ПО для Windows», доступным бесплатно.
Разработка приложений для iPhone на Windows уже реальна
Если вы действительно решили создавать приложения для iPhone на Windows и даже не хотите покупать Mac Mini, то советую вам использовать html/javascript программы, такие как PhoneGap/Appcelerator либо Airplay SDK, а затем найти кого-нибудь с Mac и попросить их вам помочь. Airplay SDK является единственным бесплатным решением для Windows для создания высококлассных игр на iPhone/iPad. Вы можете использовать Флэш-разработчик, но на данный момент у него имеются ограничения в качестве. Однако скоро выйдет новая версия.
Обновление: к сожалению, Airplay SDK был переименован в Marmalade SDK и больше не доступен бесплатно независимым разработчикам.
Мост Windows для iOS
Используя проект с открытым исходным кодом корпорации Майкрософт, вы можете сохранить приложение для iOS на Objective-C и одновременно использовать множество поддерживаемых платформ iOS с функциями Windows.
Начало работы с мостом
1. Установка Visual Studio
Если у вас еще нет Visual Studio, можно бесплатно скачать Visual Studio Community. Если у вас нет компьютера, опробовать мост можно с помощью доступной для скачивания виртуальной машины для оценки в выбранной вами среде виртуализации.
2. Установка моста Windows для iOS
Установить мост легко. Просто скачайте двоичный файл из репозитория GitHub и извлеките архив winobjc.zip в правильное расположение на компьютере.
3. Разработка приложения
Импортируйте свой проект Xcode в Visual Studio, воспользовавшись инструментом vsimporter в составе пакета SDK, или начните создавать следующее потрясающее приложение на универсальной платформе Windows в Visual Studio с помощью Objective-C. Дополнительные сведения об использовании vsimporter см. на нашей странице в Википедии.
Создание моста на основе исходного кода с целью расширения возможностей сообщества или внесения своего вклада в его развитие.
1. Установка Visual Studio
Если у вас еще нет Visual Studio, можно бесплатно скачать Visual Studio Community. Если у вас нет компьютера, опробовать мост можно с помощью доступной для скачивания виртуальной машины для оценки в выбранной вами среде виртуализации.
2. Клонирование репозитория
Клонируйте наш репозиторий GitHub, создайте собственную ветвь и приступайте к исследованию. Если вы обнаружите неподдерживаемый программный интерфейс, сообщите об этой проблемы, чтобы мы решили ее в приоритетном порядке
3. Создание пакета SDK
Создайте мост, используя включенное решение Visual Studio в каталоге /build. Дополнительные сведения см. на нашей странице в Википедии.
4. Установка моста Windows для iOS
После создания пакета SDK перейдите в папку /build/SDKPackage/Release и извлеките архив winobjc.zip в правильное расположение на компьютере.
5. Разработка приложения
Импортируйте свой проект Xcode в Visual Studio, воспользовавшись инструментом vsimporter в составе пакета SDK, или начните создавать следующее потрясающее приложение на универсальной платформе Windows в Visual Studio с помощью Objective-C. Дополнительные сведения об использовании vsimporter см. на нашей странице в Википедии.
6. Участие
После согласования авторского лицензионного соглашения (CLA) по проекту отправьте запрос на включение, добавив в него свои тесты и изменения в коде SDK, чтобы сделать работу других участников сообщества более удобным.
Преобразование приложения для iOS в приложение для Windows
Узнайте, как автоматически создавать решения Visual Studio на основе ваших проектов Xcode и получить необходимые средства для разработки собственных приложений UWP для Windows с помощью уже знакомого вам языка Objective-C.
*Примеры приложений, выполняемых на устройствах с Windows, приведены исключительно для иллюстрации и являются вымышленными.
*Некоторые сведения относятся к предварительным версиям продуктов, в которые перед коммерческим выпуском могут быть внесены существенные изменения. Майкрософт не дает никаких гарантий, прямых или косвенных, в отношении указанной здесь информации. Мост Windows для iOS не связан с Apple Inc, не спонсируется и не поддерживается компанией.
Исследуем iOS SDK и используем недокументированные API
Из этой главы, да и из всей этой книги понятно, что самые лакомые куски программирования под iOS включены в публичные фреймворки, но не в SDK. Неофициальная политика Apple насчет этого проста: вы можете всё это использовать, но только на свой страх и риск. Ваш код может сломаться при следующем обновлении прошивки. Вам самим придётся искать компромисс между риском и прибылью.
Дисклеймеры
Краткая инструкция по поиску в SDK
$ LookSDKForSymbol.sh light level
U _UIBacklightLevelChangedNotification
Found in ./System/Library/CoreServices/SpringBoard.app/SpringBoard
001b43c4 t -[UIApplication backlightLevel]
001b4360 t -[UIApplication setBacklightLevel:]
0025ce54 t -[UIDevice _backlightLevel]
0025ce40 t -[UIDevice _setBacklightLevel:]
… и ещё несколько десятков символов
Большую часть результатов можно сразу отбросить, например -[UIApplication backlightLevel] возвращает значение подсветки, а не устанавливает его.
Оставшиеся, если их не более нескольких десятков, можно попытаться скормить гуглу. Случается, что кто-то уже занимался исследованием API, связанных с найденными символами, и в этом случае задача, считай, решена. В более сложных случаях приходится заниматься реверс-инжинерингом, то есть выяснять, как работают найденные функции, как использовать найденных оповещения и тому подобное.
Символьные строки, выдаваемые утилитой, делятся на следующие категории:
- Objective-C и С++ функции, классы, структуры и так далее. Всё что относится к Objective-C содержит квадратные скобки([]) либо знаки доллара ($). C++ функции как правило содержатся в каком-нибудь namespace’е, и поэтому в их названии содержиться символ разрешения пространства имён, два двоеточия (::).
- Objective-C блоки. Они имеют следующий общий вид:
Например:
Дальнейшие действия зависят от категории символа.
- Генерируем заголовочный файл для данного фреймворка:
class-dump-z Foundation > $/iOS_private_headers/Foundation.h
В большинстве случаев, сгенерированного заголовчного файла достаточно: в нем должны быть довольно хорошо описаны иерархии наследования классов, структуры, методы и т.д, чтобы потратив немного времени можно разобраться с API и использовать его в своём предложении.
К сожалению, иногда информации содержащейся в заголовочном файле недостаточно, чтобы заставить код работать, и тогда приходится анализировать ассемблерный код, сгенерированный otool.
Hint по дизассемблированию Objective-C кода: почти наверняка вы столкнетесь с вызовами функций типа objc_msgSend (отправка сообщения объекту). В качестве первого параметра всегда идет указатель на объект, а вторым — указатель на селектор (selector), т.е. указатель на строку, являющуюся названием метода (остальные «обычные» аргументы идут третьим, четвертым и т.д. аргументами). Определить, что за сообщение отправляется в данном случае, поможет hexdump .
- Про это можно сразу забыть. Блоки (обычно) локальны, их нельзя вызвать из своего кода.
- Самый сложный вариант. В самых простых случаях можно подобрать сигнатуру для функции, в остальных — только дизассемблирование. Больше об этом можно узнать в разделе «Как узнать сигнатуру неизвестной функции?».
- Начнем с того, что попытаемся отловить оповещения в одном из трёх основных центров оповещений (это Local, Darwin и CoreTelephony). Если оповещения такого типа не приходят, дело может быть в одной из двух вещей:
— Оповещения такого типа приходитя в отдельный, специальный центр оповещений. Cледует поискать следы такого центра оповещений в том же фреймворке, к каторому принадлежит найденное оповещение.
— Доставка оповещений отключена. Попытаться найти механизм включения доставки оповещений такого типа. - В этом случае, скорее всего существует либо функция, которая принимает данную константу в виде параметра, либо словарь, в котором данная константа является ключом. В любом случае, следует искать функцию или метод, название которых начинаются с того же слова (например: константа kLockdownDeviceColorKey -> функция lockdown_copy_value(. ) ;
Как узнать сигнатуру неизвестной функции?
1. Найти в интернете, как это не банально. Мне довольно часто попадались китайский сайты, были корейский и японский сайты с очень полезной информацией. Обычно самого кода уже достаточно, чтобы понять что происходит и как используется данная функция, данный класс и т.д. Спасибо многословности и выразительности Objective-C!
2. Для многих простых функций, можно попытаться угадать сигнатуру. Внимание, это может быть довольно опасно.
Использование некоторые простые функции, таких как GSEventSetBackLightLevel, самоочевидно.
void GSEventSetBackLightLevel(float level);
Для многих других я использовал следующий трюк (на примере функции SBGetRingerSwitchState):
В результате работы этого кода выяснилось, что
1) функция возвращала значение 0x10000003 , не зависящее от реального положения переключателя.
2) Переменная out2 изменила свое значение на self. Возвращаемое значение также не зависит от переключателя.
3) Остальные переменные не изменили свое значение.
Из 1) я сделал вывод функция возвращает значене типа kern_return_t , так как 0x10000003 соответствует системной ошибке MACH_SEND_INVALID_ DEST . По видимому, ошибка указывала на неправильный порт [в данном случае порт — это абстракция ядра mach (mach kernel), характеризующая права и приоритет процесса]. Как правило, если в вызове функции используется номер порта, то он идет первым аргументом. Из 2) следует, что через второй аргумент функция возвращает некое значение по ссылке.
В результате этих нехитрых действий получается следующая сигнатура:
Кстати, если в названии функции присутствует слово get, то согласно naming conventions Objective-C эта функция должна возвращать значение по ссылке. Это также видно из приведенного примера.
3. Дизассемблирование. На примере все той же SBGetRingerSwitchState. Используем otool:
$ otool -p _SBGetRingerSwitchState -tV -arch armv6 SpringBoardServices | less
000038cc b5f0 push
000038ce af03 add r7, sp, #12
000038d0 b092 sub sp, #72
000038d2 aa06 add r2, sp, #24 // значение регистра r2 затирается
000038d4 9205 str r2, [sp, #20]
000038d6 ac08 add r4, sp, #32 // … как и регистра r4
000038d8 ab0f add r3, sp, #60 // … и r3
000038da 9304 str r3, [sp, #16]
000038dc 9103 str r1, [sp, #12] // значение r1 сохраняется в стеке
000038de 4925 ldr r1, [pc, #148] (0x3974)
000038e0 6011 str r1, [r2, #0]
000038e2 6020 str r0, [r4, #0] // значение r0 также сохраняется в стеке
…
Из этого кода, используя даже поверхностные знания arm-ассемблера, можно предположить, что функция принимает два аргумента типа «слово» (word)
Выходит, что у функции два аргумента. Идем дальше, в самый конец.
…
00003964 9e04 ldr r6, [sp, #16]
00003966 6836 ldr r6, [r6, #0]
00003968 9903 ldr r1, [sp, #12]
0000396a 600e str r6, [r1, #0]
// примерно соответствует (в терминах языка си): *r1 = r6; т.е. по адресу, хранящемуся в r1 записывается значение из r6;
// Это значит, что функция возвращает значение по ссылке
0000396c 462e mov r6, r5
0000396e 4630 mov r0, r6
// результат выполнения функции помещается в r0
00003970 b012 add sp, #72
00003972 bdf0 pop
…
В сухом остатке получаем:
Продолжая анализировать этот асcемблерный код, уточняем типы и приходим к окончательному варианту:
Разные прошивки и разные устройства: что может сломаться и как это исправить?
Понятно, что недокументированные API вовсе не обязательно работают на всех устройствах одинаково. По моему опыту, чаще всего ничего не меняется, и API работает одинаково на всех устройствах и всех прошивках. Так, например, все функции расширения UIDevice-IOKitExtensions (кроме определения IMEI) работают одинаково хорошо на всех устройствах и всех прошивках. Какие изменения могут произойти при обновлении iOS?
Вот несколько практических вариантов.
- Может появиться официально документированный программный интерфейс, при этом недокументированный интерфейс, как правило, продолжает работать. Пример:
Для того чтобы избежать проблем совместимости, соблюдайте простые правила: проверяйте наличие функций (например, с помощью -[NSObject respondsToSelector:] ), классов ( NSClassFromString(@»SomeClass») вернет nil в случае отсутствия класса SomeClass ) и т.д., а также заранее подумайте, что должна делать программа в случае, если API отсутствует. При использовании динамической линковки библотек следует также всегда проверять возвращаемые значения dlsym(. ) и dlopen(. ) на равенство NULL.
Примеры
Пример 1:
Определение положения бокового переключателя вибро (a.k.a. Ring/Silent switch, Mute switch)
Одной из задач, которые стояли передо мной, было определение положения бокового переключателя, который в оригинале называется ring/silent switch. Этот переключатель используется для переключения между «тихим» и обычном/«громким» режимами в айфоне и айпаде. Поиск по StackOverflow дал решение:
Которое, впрочем, не работает в iOS 5. Не сработало и использование более нового API (kAudioSessionProperty_AudioRouteDescription) которое дает расширенную информацию об аудиовходах и -выходах. (AUDIOROUTE)
Мои дальнейшие поиски по StackOverflow вывели меня на этот пост. В нем описывается библиотечная функция AudioServicesAddSystemSoundCompletion(), чьё нестандартное поведение рассматривалось разработчиками как баг.
Нестандартное поведение заключается в том, что вызов колбэка MyAudioServicesSystemSoundCompletionProc состоится в конце проигрывания звука в обычном режиме, но сразу после вызова AudioServicesPlaySystemSound в «тихом» режиме. Это создает лазейку для определения текущего состояния переключателя. Если, например, длина аудиофайла что мы проигрываем равна 1 с, то разница во времени вызова MyAudioServicesSystemSoundCompletionProc() в «тихом» и громком режиме составляет 1 c. На этом я построил свое второе, асинхронное решение для определения положения бокового переключателя. Вот оно:
Хотя это новое решение и было рабочим, оно не устраивало меня по нескольким причинам. Во-первых, оно было асинхронным и работало с ощутимой задержкой (около 1/10 секунды). Снижение задержки вело к ложным срабатываниям. Во-вторых, был побочный эффект — сам проигрываемый звук, который звучал достаточно громко чтобы смутить пользователя. Позже я искусственно выкрутил громкость в ноль в аудиоредакторе. В-третьих, это был уже слишком похоже на грязный хак, хотя это, например, не помешало создателям VSSilentSwitch продавать свое решение, по всей видимости основанное на том же эффекте.
Примерно через месяц я вернулся к этой проблеме. Я начал использовать команду nm для поиска символов в объектных файлах, на её основе я написал простейший shell-скрипт, листинг которого можно найти ниже (В разделе «Инструменты»). Скрипт запускается с одним, двумя или тремя параметрами, каждый из которых представляет ключевое слово.
/Documents/LookSDKForSymbol.sh RingerSwitch
# часть результатов опущена
0000d738 S _kGSRingerSwitchCapability
Found in ./System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
000038cc T _SBGetRingerSwitchState
0000370c T _SBGetRingerSwitchState
Found in ./System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices
Функция с названием SBGetRingerSwitchState выглядела многообещающе.
Для получения нужного порта использовалась функция:
из того же фреймворка.
Вот что получилось в итоге:
Пример 2:
IMEI (International Mobile Equipment Identity) — уникальный идентификационный
код, присваиваемый каждому телефону, своего рода MAC-адрес телефона (хотя MAC-адрес у телефона также есть)
Я уже и не помню, как я вышел проект Эрики Садун uidevice-extension, но по мере того, как я с ним разбирался он всё больше казался мне этакой программистской «золотой жилой».
Одна из категорий, UIDeviсe(IOKit_Extensions) содержит функции для определения IMEI. Я протестировал эти функции на iPhone 4 c iOS 5.1 и iPad c iOS 4.3, всё работало и я перешел к другим задачам. Но в ходе бета-тестирования выяснилось, что функция для определения IMEI не работает на новых устройствах: iPad 2, the new iPad и iPhone 4S. Для выяснения причин я отправился на StackOverflow, где мои опасения подтвердились. Поиски привели меня тогда к фреймворку под названием CoreTelephony.
$ nm -g ./CoreTelephony | grep -i imei
U _kCFAbsoluteTimeIntervalSince1970
00053b28 S _kCTMobileEquipmentInfoIMEI
00053ad4 S _kCTPostponementInfoIMEI
00053ac4 S _kCTPostponementStatusErrorDefaultIMEI
$ nm -g ./CoreTelephony | grep MobileEquipment
000260e4 T __CTServerConnectionCopyMobileEquipmentInfo
00053b34 S _kCTMobileEquipmentInfo1xIMSI
00053b20 S _kCTMobileEquipmentInfoCurrentMobileId
00053b24 S _kCTMobileEquipmentInfoCurrentSubscriberId
00053b40 S _kCTMobileEquipmentInfoERIVersion
00053b2c S _kCTMobileEquipmentInfoICCID
00053b28 S _kCTMobileEquipmentInfoIMEI
00053b30 S _kCTMobileEquipmentInfoIMSI
00053b38 S _kCTMobileEquipmentInfoMEID
00053b44 S _kCTMobileEquipmentInfoMIN
00053b3c S _kCTMobileEquipmentInfoPRLVersion
Можно предположить что функция (_CTServerConnectionCopyMobileEquipmentInfo(. )) возвращает словарь(CFDictionaryRef) c ключами вида kCTMobileEquipmentInfo* и соответствующими им значениями. К счастью, на этот раз мне не пришлось восстанавливать сигнатуру. Поиск в гугле по запросу _CTServerConnectionCopyMobileEquipmentInfo привел меня на эту страничку, и вскоре функция для определения IMEI была готова.
Этот метод определения IMEI работает на всех устройствах.
Позже я нашел еще один метод определения IMEI (через lockdownd).
Пример 3:
Использование недокументированных оповещений: нажатия кнопок громкости.
Изначально я наивно полагал, что любая символьная константа, заканчивающаяся на «Notification» является названием системного оповещения и её можно использовать, просто зарегистрировав наблюдателя (observer) с помощью [NSNotificationCenter defaultCenter].
/Documents/LookSDKForSymbol.sh notification$ volume change
001dbe60 S _MPAVControllerVolumeDidChangeNotification
001dbe64 S _MPAVControllerVolumeMutedDidChangeNotification
001dc4f8 S _MPMusicPlayerControllerVolumeDidChangeNotification
001dc314 S _MPVolumeViewRouteButtonChangedNotification
001dc310 S _MPVolumeViewVisibilityChangedNotification
Found in ./System/Library/Frameworks/MediaPlayer.framework/MediaPlayer
000d6d24 D _AVController_EffectiveVolumeDidChangeNotification
000d6d60 D _AVController_VolumeDidChangeNotification
000d6fec D _AVSystemController_CurrentRouteHasVolumeControlDidChangeNotification
000d6ffc D _AVSystemController_EffectiveVolumeDidChangeNotification
000d6fdc D _AVSystemController_SystemVolumeDidChangeNotification
Found in ./System/Library/PrivateFrameworks/Celestial.framework/Celestial
… и еще около десятка из других фреймворков
Написав тестовую программу, я принялся проверять, какие оповещения приходили в ответ на нажатия клавиш громкости.
Из составленного мной довольно большого списка опопвещений приходили только вот эти 2:
Недостаток этих оповещений в том, что
1) Нельзя напрямую определить, какая из двух кнопок была нажата
2) Нельзя отследить, когда нажата и когда отпущена каждая из кнопок
Ищу по другим ключевым словам:
/Documents/LookSDKForSymbol.sh volume button
001b221c t -[UIApplication setWantsVolumeButtonEvents:]
003cce5c t _SBSetWantsVolumeButtonEvents$shim
0054478c S __UIApplicationVolumeDownButtonDownNotification
00544790 S __UIApplicationVolumeDownButtonUpNotification
00544784 S __UIApplicationVolumeUpButtonDownNotification
00544788 S __UIApplicationVolumeUpButtonUpNotification
Found in ./System/Library/Frameworks/UIKit.framework/UIKit
… и еще несколько десятков из разных фреймворкрв
Четыре оповещения из UIKit сработали не сразу: необходимо было подать связанную с ними команду.
После этого стали приходить оповещения о нажатиях соответствующих кнопок.
Побочный эффект: вызов данной функции приводит к тому, что кнопки громкости больше не регулируют громкость, так что по завершении работы с кнопками следует вызвать
Пример 4:
Использование недокументированных оповещений: отслеживание статуса SIM-карты
Работаем по проверенной схеме:
/Documents/LookSDKForSymbol.sh notification$ SIM
…
00052560 S _kCTSIMSupportSIMInsertionNotification
00052564 S _kCTSIMSupportSIMStatusChangeNotification
…
000525bc S _kCTSIMSupportSIMTrayStatusNotification
…
Found in ./System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
…
Found in ./System/Library/PrivateFrameworks/FTServices.framework/FTServices
$
Наиболее подходящими мне показались оповещения под названиями:
1) kCTSIMSupportSIMInsertionNotification
2) kCTSIMSupportSIMStatusChangeNotification
3) kCTSIMSupportSIMTrayStatusNotification
Простейшая тестовая программа показала, что оповещения под названием (1) приходили только в момент вставки сим-карты (я мог бы догадаться и раньше по названию), (2) приходили именно тогда когда мне нужно (при вставке и вынимании), оповещения (3) не приходили вообще. Позже я узнал, что оповещения (3) относятся к специальному центру оповещений под названием CTTelephonyCenter. Об использовании CTTelephonyCenter можно прочитать здесь.