SimSim, откройся

    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 пусть и посредственным, но открытым кодом!
    • +13
    • 5,2k
    • 5
    Поделиться публикацией

    Комментарии 5

      0

      Хм, интересно! Я для таких целей держу Jailbreak. Не так удобно, но вполне устраивает. Ваше решение попробую тоже обязательно!


      А к Angry Birds Вы подключились с помощью DYLD_INSERT_LIBRARIES? =)

        +1
        Постановочный кадр. Ибо с нашими приложениями скриншот выглядел крайне уныло. А с DYLD_INSERT_LIBRARIES лучше всякие isRegistered править :)
          0

          Хм, постановочный — это как делалось? Взяли их иконку просто? Или фотошоп? )

            +1
            Набрали каких-то иконок каких-то приложений, прописали исключительно для скрина в соответствующие пути. Цинично, но выхода не было.
      • НЛО прилетело и опубликовало эту надпись здесь

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое