SimSim, откройся

в 13:51, , рубрики: ios simulator, objective-c, open source, webdav, ни строчки на swift, разработка под iOS, Разработка под OS X

imageПривет!

Многие из вас пишут под iOS. Практически у любого разработчика рано или поздно возникает нужда поковыряться во внутренностях своего приложения на уровне файлов — посмотреть, корректно ли распаковался какой-нибудь бандл, не полетела ли база. Самые настырные пользуются приложением SimPholders.

Мы с коллегами какое-то время эксплуатировали вышеупомянутое творение, а потом утомились и перестали.

Причина была проста — SimPholders Nano (платное приложение из App Store) перестало работать. Связались с разработчиками, они сказали, что вот-вот все починят и вообще они ни в чем не виноваты. Чуть позже они удалили версию Nano из App Store. Написали им снова, как водится — в ответ тишина. Покупать не-AppStore версию после этого совершенно не хотелось.

Легко сказать — перестали. Потребность в отладке двигателя через выхлопную трубу никуда не исчезла. Посидели, погундосили — и написали свою альтернативу, так что — встречайте SimSim!

Умеет открывать приложения, установленные как на симуляторе, так и на устройстве. Полезете в исходники — готовьтесь к худшему. На повестке дня первым пунктом стояло не изящество слога, но максимально быстрое достижение результата. Ну и импортозамещение, опять же.

Устаревший скриншот

Для просмотра приложений на симуляторе — просто скачайте, распакуйте, запустите, пользуйтесь. На гитхабе есть куцее, но описание.

Процесс получения данных о симуляторах и приложениях не слишком интересен для конечного пользователя — просто парсинг нескольких 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'ов.

Долго и нудно:

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 пусть и посредственным, но открытым кодом!

Автор: dns78

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js