![image](https://habrastorage.org/files/740/d7d/50a/740d7d50ac39472b8651c9fc3ab4fd1c.jpg)
Многие из вас пишут под iOS. Практически у любого разработчика рано или поздно возникает нужда поковыряться во внутренностях своего приложения на уровне файлов — посмотреть, корректно ли распаковался какой-нибудь бандл, не полетела ли база. Самые настырные пользуются приложением SimPholders.
Мы с коллегами какое-то время эксплуатировали вышеупомянутое творение, а потом утомились и перестали.
Причина была проста — SimPholders Nano (платное приложение из App Store) перестало работать. Связались с разработчиками, они сказали, что вот-вот все починят и вообще они ни в чем не виноваты. Чуть позже они удалили версию Nano из App Store. Написали им снова, как водится — в ответ тишина. Покупать не-AppStore версию после этого совершенно не хотелось.
Легко сказать — перестали. Потребность в отладке двигателя через выхлопную трубу никуда не исчезла. Посидели, погундосили — и написали свою альтернативу, так что — встречайте SimSim!
Умеет открывать приложения, установленные как на симуляторе, так и на устройстве. Полезете в исходники — готовьтесь к худшему. На повестке дня первым пунктом стояло не изящество слога, но максимально быстрое достижение результата. Ну и импортозамещение, опять же.
![Устаревший скриншот](https://habrastorage.org/files/f5d/97c/c5c/f5d97cc5c3e7479bb3f7969026233964.png)
Для просмотра приложений на симуляторе — просто скачайте, распакуйте, запустите, пользуйтесь. На гитхабе есть куцее, но описание.
Процесс получения данных о симуляторах и приложениях не слишком интересен для конечного пользователя — просто парсинг нескольких xml, сопоставление данных. Кому очень интересно — см. исходники.
Для просмотра приложений на устройстве — все работает, но есть нюансы. О них ниже.
Начнем с того, что доступного API для работы с iOS устройствами Apple не предоставляет. Тем паче — для доступа к файловой системе. Есть несколько библиотек, работающих или через private frameworks, или через реализующие протоколы, знания о которых были получены в результате реверс инжиниринга, так что работоспособность их в следующей версии как iOS, так и OS X предсказать затруднительно.
Но ведь у нас задачка несколько проще? Нам не нужен доступ к всей файловой системе устройства — только к нашему приложению. И тут на сцену выходит локальный сервер. Точнее, WebDav поверх GCDWebServer — GCDWebDAVServer. Легко линкуется к проекту, стабильно работает — чего же боле?
Итак, мы запустили проект с прилинкованным GCDWebDAVServer на устройстве, стартовали его, и даже можем подконнектиться к нашему приложению, например, из Finder. Уже хорошо, но хочется большего — автомонтирования из меню SimSim'а.
Вот здесь нас ждал затык. Простого решения монтирования WebDav'а с указанным путем и открытия Finder'а в соответствующем месте программным способом не находилось. Хоть плачь, хоть тресни. Были перепробованы различные варианты как подергивания Finder'а за AppleScript, так и вызова mount'а. Все не то. Вроде что-то работает, но как-то криво и неуютно.
Параллельно пришлось сидеть в дизассемблированном коде Finder'а в попытках подсмотреть — а может быть, можно включить программно показ скрытых файлов без рестарта самого Finder'а? Нудно, грустно, долго изучать листинг дизассемблера на 200Mb. Результат — фиг. Ну и ладно. Apple виновата.
Пока мы делали вид, что возимся с Finder'ом — нас — ура-ура — ткнули носом в вероятное решение, которое при проверке и оказалось тем, что нам было нужно, а именно — NetFSMountURLAsync(). Функция тоже, скажем, не слишком документированная, но альтернативы не было видно.
Кратко: умеет принимать в себя опции монтирования сетевого адреса, логины-пароли, а по окончании монтирования асинхронно подергивать блок кода с передачей в него mount point'ов.
Долго и нудно:
где
Словари openOptions и mountOptions содержат в себе множественные опции, половина из которых закомментирована в NetFS.h, и разбираться в которых досконально не было ни повода, ни времени.
Нам потребовались лишь:
int NetFSMountURLAsync(CFURLRef networkShare, CFURLRef mountPath, CFStringRef userName, CFStringRef password, CFMutableDictionaryRef openOptions, CFMutableDictionaryRef mountOptions, AsyncRequestID* requestID, dispatch_queue_t queue,
^(int status, AsyncRequestID requestID, CFArrayRef mountpoints)
{
// этот блок будет вызван по окончании монтирования
});
где
networkShare - сетевой адрес, который нужно подмонтировать.
mountPath - точка монтирования в файловой системе OS X.
userName, password - описание не требуется.
queue - например, dispatch_get_main_queue().
Словари openOptions и mountOptions содержат в себе множественные опции, половина из которых закомментирована в NetFS.h, и разбираться в которых досконально не было ни повода, ни времени.
Нам потребовались лишь:
openOptions = { kNAUIOptionKey : kNAUIOptionNoUI}, чтобы пользователя никто ни о чем не спрашивал.
mountOptions =
{
kNetFSAllowSubMountsKey : YES, - чтобы монтировался не только верхний уровень шары
kNetFSMountAtMountDirKey: YES - чтобы монтировалось туда, куда мы хотим смонтировать
}
Настало время откуда-то брать адреса монтирования, названия устройств и прочую информацию, дабы воспользоваться ей для отображения в меню SimSim'а. В голову полезли мрачные мысли о диалогах настроек, валидации всего введенного, парсинге того и сего… Неохота. Чем меньше пользовательского интерфейса в подобных утилитах — тем лучше. Результатом размышлений стало соломоново решение — а скинем заботу о корректности данных на пользователей, да и вообще пусть сами прописывают что хотят и куда хотят. Почти куда хотят — в конкретный .plist. (Нам нравятся наши умолчания — см. п. 1.).
Для стойких, дочитавших до сего момента и решившихся использовать SimSim для просмотра приложения на устройстве — пришло время кода.
- Добавьте GCDWebServer и GCDWebDAVServer в проект. Через cocoapods, или как-то еще — в общем, разберитесь.
- Добавьте инициализацию GCDWebDAVServer в ваше приложение — дерните нижеприведенное где-то на старте. (NSApplicationSupportDirectory здесь лишь для примера. Вам решать, в где сервер будет иметь /).
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); NSString *path = [paths lastObject]; GCDWebDAVServer* server = [[GCDWebDAVServer alloc] initWithUploadDirectory:path]; server.allowHiddenItems = YES; [server startWithPort:8082 bonjourName:nil];
- Запишите IP адрес вашего устройства iPad/iPhone.
- Создайте файл com.dsmelov.devices.plist в каталоге ~/Library/Preferences. В нем будет прописано имя устройства и его адрес. Пример:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> <plist version="1.0"> <dict> <key>Devices</key> <array> <dict> <key>name</key> <string>iPad2</string> <key>url</key> <string>http://:@192.168.1.26:8082</string> </dict> </array> </dict> </plist>
- Дело сделано. Возьмите черную метку… Запустите свое приложение на устройстве — вот теперь его можно открыть прямо из SimSim.
В общем, пользуйтесь. А если кому-то что-то не понравится в коде, или, паче чаяния, возникнут свежие идеи — присоединяйтесь к разработке! Самые ленивые могут просто попиарить проект в сети — закидаем SimPholders пусть и посредственным, но открытым кодом!