alex_lip
alex_lip14 января 2018 г. 14:15

Разработка на Qt под iOS

Преамбула

Самая демократическая страна в мире создала самую тоталитарную операционную систему. Поэтому если вы хотите тестировать ваше ПО на IPad или IPhone вам нужно обязательно зарегистрироваться на сайте https://developer.apple.com/ в качестве разработчика. На первом этапе денег платить не обязательно. Добрый дядя Джобс даст вам сертификат, на использование для тестирования вашего же устройства, сроком на 7(семь) дней. По окончании действия сертификата ваше ПО не будет запускаться и его надо будет снова закачать с компа. Если вы захотите установить ваше ПО на устройство другого человека – его apple ID нужно будет точно так же прописать в Xcode как и ваш.


Проблематика

Для изучения Qt, я для тестовой задачи решил разработать мобильное рабочее место для документооборота. Поскольку документы могут быть в виде word , pdf , jpg и т.д. Мне естественным образом нужно после скачивания оных с сервера чем-то их открывать. В Qt есть замечательная конструкция

QDesktopServices :: openUrl ( QUrl :: fromLocalFile ( QStandardPaths :: writableLocation ( QStandardPaths :: GenericDataLocation ) + "/temp_esd/file." + file_ext ));
// File _ ext я вытаскиваю в соответствии с MIME типом
if ( head == "Content-Type" ) {
    if ( content_from_url == "image/jpeg" )
        file_ext = "jpg";
    else if ( content_from_url == "application/pdf" )
        file_ext = "pdf";
    else if ( content_from_url == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" )
        file_ext = "docx";
    else if ( content_from_url == "application/msword" )
        file_ext = "doc";
    else if ( content_from_url == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" )
        file_ext = "xlsx";
    else if ( content_from_url == "application/vnd.ms-excel" )
        file_ext = "xls";
    else if ( content_from_url == "application/vnd.oasis.opendocument.text" )
        file_ext = "odt";
    else if ( content_from_url == "application/vnd.oasis.opendocument.spreadsheet" )
        file_ext = "ods";
    else if ( content_from_url == "application/octet-stream" )
        file_ext = "7z";
    else if ( content_from_url == "video/x-msvideo" )
        file_ext = "avi";
    else if ( content_from_url == "image/bmp" )
        file_ext = "bmp";
    else if ( content_from_url == "application/cdr" )
        file_ext = "cdr";
    else if ( content_from_url == "image/gif" )
        file_ext = "gif";
    else if ( content_from_url == "video/quicktime" )
        file_ext = "mov";
    else if ( content_from_url == "audio/mpeg" )
        file_ext = "mp3";
    else if ( content_from_url == "image/png" )
        file_ext = "png";
    else if ( content_from_url == "application/vnd.ms-powerpoint" )
        file_ext = "ppt";
    else if ( content_from_url == "application/vnd.openxmlformats-officedocument.presentationml.presentation" )
        file_ext = "pptx";
    else if ( content_from_url == "application/x-rar-compressed" )
        file_ext = "rar" ;
    else if ( content_from_url == "application/rtf" )
        file_ext = "rtf";
    else if ( content_from_url == "image/tiff" )
        file_ext = "tif";
    else if ( content_from_url == "text/plain" )
        file_ext = "txt" ;
    else if ( content_from_url == "audio/x-ms-wma" )
        file_ext = "wma";
    else if ( content_from_url == "application/zip" )
        file_ext = "zip";
}

Но в IOS – в соответствии с недавно принятыми стандартами безопасности запрещено открытие локального URL в одном приложении при помощи запуска другого. Поэтому под IOS использование данной конструкции вызывало ошибку

this plugin does not support

Сначала я пошел в сторону использования Info.plist – тем более, что под IOS он все равно нужен. Прописал в секции

<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>ms-word</string> 
        <string>ms-powerpoint</string> 
        <string>ms-excel</string>
    </array>

Но это работает, только если файлы открываются НЕ локально – а по интернет ссылке. После дальнейшего изучения проблемы я наткнулся на статью https://code.tutsplus.com/tutorials/ios-sdk-previewing-and-opening-documents--mobile-15130 в которой описано использование UIDocumentInteractionController для открытия файлов в «песочнице» ios приложения. Также огромное спасибо Ekkehard Gentz за совершенно замечательный блог, в котором детально расписан пример использование вышеописанной функции в Qt.

Решение проблемы

И так имеем MAC MINI для компиляции исходников и IPAD для тестирования. На MAC MINI устанавливаем Qt. Во время установки вам предложат скачать XCODE – соглашаемся. Все замечательно устанавливается. Далее в Xcode – Preferences – Accounts добавляем свой Apple аккаунт и запрашиваем сертификат (через Manage Certificates ). Естественно предварительно надо зарегистрироваться на сайте Apple в качестве разработчика. Далее следует подключить ваш IPAD и в разделе  Настройки – Developer – UI AUTOMATION – включить Enable UI Automation .  Таким образом, мы перевели IPAD в режим разрешенной отладки.

В Xcode – Window – Devices and Simulators вы можете проверить ваше подключенное устройство. Конечно, в процессе подключения вас пару раз спросят о доверии компа устройству и устройства компу – говорите ДА.

В QT в файле проекта добавляем следующую секцию

ios {
OBJECTIVE_SOURCES += ios/iosshareutils.mm \
ios/docviewcontroller.mm

QMAKE_INFO_PLIST = ios/Info.plist

LIBS += - framework UIKit
}

В этой секции мы указываем расположение Info.plist – это некий текстовый конфигурационный файл, который можно править вручную.

OBJECTIVE_SOURCES – это наши файлы на OBJECTIVE- С

Директива LIBS – возможно уже устарела. Но я ее убирать не стал.

В самих исходниках очень удобно использовать директиву

#if defined( Q_OS_IOS )

QString temp_url= QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/temp_esd/file." +file_ext;
qDebug() << "path_file " << temp_url;
IosShareUtils ios;
ios.viewFile(temp_url, "View File" , "application/pdf" , 21 );

#else
QDesktopServices :: openUrl ( QUrl :: fromLocalFile ( QStandardPaths :: writableLocation ( QStandardPaths :: GenericDataLocation ) + "/temp_esd/file." + file_ext ));
#endif

Исходные тексты вы можете посмотреть в блоге https://blog.qt.io/blog/2017/12/01/sharing-files-android-ios-qt-app/

В общем, после того, как готовы исходные тексты, приступаем к деплою. Обратите внимание, чтобы в Qt Creator при выпуске на Qt 5.10 for iOS справа внизу от иконки телефона с яблоком горела зеленая точка. Наведите на нее мышкой и увидите информацию об устройстве и работает ли это устройство в режиме разработчика. После этого компиляем исходники и пытаемся загрузить оные на телефон. С первого раза не получается. Идем в папку Release и открываем Xcode проект нашего приложения. Наверху, там где имя вашей схемы, выбираем активную схему на то устройство, которое присоединено к компьютеру. Затем нажимаем значок “ Build and then run the current scheme ”. Но это еще не все. На вашем устройстве появится значок вашего приложения, но оно не запустится. Или запустится, но быстро закроется. Открываем Ipad – идем в настройки – основные – управление устройством – ПО разработчика – выбираем свой apple id – далее говорим, что мы доверяем этому разработчику. После всех этих операций можно спокойно деплоить приложение непосредственно из Qt Creator .

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

Небольшое добавление о компиляции проекта под старые версии iOS (8-9)

Я откомпилял свой проект на Qt  5.10 под iOS 10 и архитектуру arm64 (с помощью Xcode9) - все отлично. Но когда я попробовал отдеплоить тот же проект под старый iPad с iOS 9 и архитектурой armv7 у меня возникло масса проблем, которые я так и не смог решить. Я пробовал настраивать архитектуру в Xcode - получал ошибки типа   - ignoring file LibQt5Core_debu.a missing required architecture armv7 in file LibQt5Core_debu.a,   пробовал директивы

 QMAKE_IOS_DEVICE_ARCHS = armv7
 CONFIG += armv7
 QMAKE_IOS_DEPLOYMENT_TARGET = 9.0

Откатывался на Xcode 7 - все безрезультатно. И наконец после нескольких дней безуспешных попыток вот что я вычитал

https://bugreports.qt.io/browse/QTBUG-65544

Description Any app that compiled with Qt 5.10 needs at least iOS 10 and does not deploy to older devices (iPad 1, iPhone 5, etc.)

Even uploaded to AppStore that apps doesn't install to devices with iOS < 10.

There are too many devices with iOS 9 to drop support of it.

Same apps compiled with Qt 5.9.3 works fine on all devices.

Eskil Abrahamsen Blomfeldt added a comment - 03 Jan '18 10:43 AM Qt 5.9 is a long-term supported release and continues to support iOS 9 if your app requires support for older devices.

For Qt 5.10, the minimum target version is iOS 10, as you say. You can see the full list of supported platforms here: http://doc.qt.io/qt-5/supported-platforms.html

According to numbers from Apple, 92% of active devices are now on iOS 10 or iOS 11.

Таким образом если вы хотите поддерживать старые версии iOS - используйте Qt 5.9.3 - все компиляется нормально сразу под нужную архитектуру.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

BlinCT
  • 14 января 2018 г. 14:28

Вот честно, на сколько же муторно под огрызок что то делать. Куча проблем)
А вод линь или под Андроид все просто и тривиально))

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
e
  • ehot
  • 31 марта 2024 г. 21:29

C++ - Тест 003. Условия и циклы

  • Результат:78баллов,
  • Очки рейтинга2
B

C++ - Тест 002. Константы

  • Результат:16баллов,
  • Очки рейтинга-10
B

C++ - Тест 001. Первая программа и типы данных

  • Результат:46баллов,
  • Очки рейтинга-6
Последние комментарии
k
kmssr9 февраля 2024 г. 1:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 8:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 17:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 15:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 декабря 2023 г. 4:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
a
a_vlasov14 апреля 2024 г. 13:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 апреля 2024 г. 9:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex4 апреля 2024 г. 11:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
P
Pisych27 февраля 2023 г. 11:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 18:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Следите за нами в социальных сетях