?

Log in

После установки некоторых Swift-фреймворков обычно хватает проверки следующих моментов:
1. Должна быть прописана строка "use_frameworks!" в pod-файле (без кавычек).
2. Сам фреймворк импортится строчкой: @import FrameworkName
Однако иногда (у меня такое возникло с фреймворком Charts) возникает ошибка такого вида: "Use Legacy Swift Language Version" (SWIFT_VERSION) is required to be configured correctly...

Суть в том, что Xcode не понимает, должен ли данный фреймворк использовать Legacy-версию (то есть последнюю на текущий момент) языка Swift. Чаще всего это означает, что сам фреймворк написан на более низкой версии языка (в моем случае это был фреймворк с версией Swift 2.3). Для исправления проблемы следует перейти в свойства Pods-проекта, найти в нем таргет нужного фреймворка, зайти в его Build Settings, найти строчку "Use Legacy Swift Language Version" и выставить ее в "NO". Так вы явно разрешите таргету использовать устаревшую версию языка, и проект сможет нормально скомпилироваться.
Казалось бы, чего проще, захотел сменить изображение - весь интернет пестрит советами вида:

UIImage *img = [UIImage imageNamed:@"navbarBackground"];
[[UINavigationBar appearance] setBackgroundImage:img
                                   forBarMetrics:UIBarMetricsDefault];

Рекомендованные размеры изображения известны - 375х64 для @2x и 750x128 для @3x-масштаба соответственно. Однако есть нюанс. Вышеприведенный код отлично подходит, если у вас solid-изображение, то есть со сплошным ровным фоном. А вот если изображение неравномерное (к примеру, градиентное), возникает проблема, при которой вы будете получать свое изображение, смещенное на навигационном элементе ровно наполовину по оси абсцисс. То есть, iOS сместит его сама, и вы получите не красивый градиент, а ужасно выглядящий шов ровно посередине панели.

Потратив уйму времени на бесполезные поиски ответа в сети, методом тыка я нашел способ разрешить ситуацию. iOS автоматически растягивает изображение до фактических размеров навигационной панели (хотя наше изображение и так имеет верный размер, но кого это волнует?), и делает это iOS в нашем случае неправильно, разрезая его не в тех местах, где можно. Для решения проблемы смените вышеуказанный код на следующий, и все заработает как часы:

UIImage *img = [[UIImage imageNamed:@"navbarBackground"] 
                stretchableImageWithLeftCapWidth:0
                topCapHeight:0];
[[UINavigationBar appearance] setBackgroundImage:img];

[iOS] Facebook SDK. Error Code 308

Столкнулся с проблемой "Login Error Code 308" при авторизации через FBSDKLoginManager. Условия воспроизведения проблемы:

1. iOS Simulator (iOS 10)
2. Проект на Swift 3.0
3. Версия Xcode = 8.0 GM Seed (8A218a)
4. Версия FB SDK (скачивал с гитхаба facebook-ios-sdk) = 4.15.0

Разрешить ситуацию не помогала ни чистка проекта, ни перезапуск/чистка симулятора, ни дополнительные строки в Info.plist проекта из разных вариантов решений на stackoverflow.com.

Правильным решением оказалось включение опции "Keychain Sharing" в свойствах проекта на вкладке Capabilities.

Tags:

На днях столкнулся с проблемой интеграции SDK от картографического сервиса Here. Скачал SDK, попробовал запустить примеры - из Starter-версии примеры вообще отказались запускаться, бросил это дело и подключил SDK сразу к рабочему проекту. В результате проект отказался компилироваться с сообщением "Undefined symbols for arch x86_64".

Для информации, проблема эта решилась тем, что в настройках проекта в перечне поддерживаемых архитектур (а также в перечне Valid Architectures) нужно было добавить две строки:
i386
x86_64

Если вы столкнетесь с похожими ошибками, попробуйте решить их этим способом. Мне помогло.
В глобальных настройках .gitignore для вашего клиента в случае работы через Xcode оптимально прописать следующее:

# Xcode
#
.DS_Store
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate

После этого можно работать с любым репозиторием, лишние файлы будут игнорироваться.

Tags:

Обфускация кода, как вы наверняка знаете, применяется для усложнения реверсивной инженерии (восстановления алгоритмов работы вашего кода по дизассемблированному объектному коду библиотеки). Для Objective-C существует несколько решений, от бесплатных библиотек (однако, целиком подходящих под мою ситуацию я найти не смог) до платных комплексных решений.

Если же вы хотите просто защитить свой код от вторжения, при этом нет необходимости платить за крупное мощное решение, то можно применить небольшую автоматизацию. Мой вариант довольно трудоемкий, требует много ручной работы, но это все же лучше, чем делать совсем все вручную. Оно подойдет для библиотек с небольшим объемом кода.

Нам понадобятся:
1. Онлайн-решение для записи кода на CoffeeScript.
2. Онлайн-решение для выполнения сгенерированного Javascript-кода.
3. Собственно, сам код на CoffeeScript (можете изменять его по своему желанию):

items = [
    "methodName1"
    "methodName2"
    ]
for item in items
    eng = "abcdefghijklmnopqrstuvwxyz" # prefixed with letter
    min = 0
    max = eng.length - 1
    idx = Math.floor(Math.random() * (max - min + 1)) + min
    letter = eng.charAt idx
    random = do Math.random + 1
    value = random.toString 36 
    value = value.substring 12
    console.log "#define #{item} #{letter}#{value}"

Порядок работы:
1. Открываете нужный вам *.m-файл, код которого вы хотите скрыть. Header-файлы нас не особо интересуют, так как там содержится публичный интерфейс, и его нет смысла скрывать от конечного пользователя.
2. Далее в зависимости от нужной степени защиты находим все приватные методы, далее, если нужно, все приватные свойства, а также все локальные переменные внутри каждой функции. Обращаю ваше внимание, что обфусцировать классы таким образом в public headers библиотеки не получится, так как для этого нужно описать нужный define в *.h-файле, что делает имя общедоступным для третьих лиц. В private headers такой проблемы нет.
3. Вписываем имена всех этих элементов в массив items в коде CoffeeScript.
4. Генерируем на этой основе JS-код (ссылка в шаге 1 выше), исполняем его (ссылка в шаге 2 выше), копируем вывод из консоли (кучку #define-ов) и вставляем его после #import-секции в вашем *.m-файле для public headers и в *.h-файле для private headers. Последняя операция позволит обфусцировать имена классов в недоступных третьим лицам заголовочных файлах.

Что нельзя обфусцировать:
1. Публичные свойства и методы в заголовочных файлах, даже в приватных.
2. Свойства и методы, для которых прописаны кастомные set-методы (например, setValue: для свойства value).
3. Свойства, для которых используется KVO.

В итоге, в результирующем файле вместо имен классов, методов, свойств и переменных (тех, что вы указали в списке) будут бессмысленные наборы символов. Это существенно усложнит работу тем, кто попробует дизассемблировать ваш код. Грубый, но эффективный, пусть и самодельный, способ.
По поводу создания фреймворка на основе static library есть превосходная статья по этой ссылке.

В дополнение к ней расскажу, что делать, если в вашей либе все-таки есть зависимый код (зависимости от других кастомных библиотек), но вы, следуя хорошей практике, не можете включать эти зависимости в итоговый объектный файл, а фреймворк как-то получить надо.

Все, что вам нужно сделать, это создать ваш фреймворк, включив в него необходимые вам библиотеки в виде файлов с кодом (именно такой вариант я пробовал, подключать сторонние фреймворки не приходилось), а затем для таргета вашей static library в свойствах на вкладке Build Phases из секции Compile Sources исключить те *.m-файлы, которые относятся к стороннему коду. Также нужно исключить из таргета библиотеки (не агрегатора, а именно библиотеки) *.h-файлы кода зависимостей, для чего находим эти файлы в проектном дереве и снимаем галку с таргета в свойствах этого файла.

Важно! Обязательно следует зайти в Build Settings для таргета вашего фреймворка (не агрегатора, а именно самой библиотеки) и в Other C Flags прописать ключ
-fembed-bitcode
. В противном случае возможны проблемы с архивацией приложений, использующих ваше SDK, из-за ошибки вида
"... was build without full bitcode"

После этих манипуляций либа будет успешно собираться и компилироваться, однако для запуска приложения с ней конечный разработчик будет должен подключить все нужные для библиотеки зависимости самостоятельно, что вам нужно будет описать в инструкции при дистрибуции вашей библиотеки.

Кроме того, важный момент: собирать (билдить) либу через таргет-агрегатор нужно, обязательно выбрав в целевом устройстве iOS Generic Device (не эмулятор и не свое собственное устройство!). Также перед билдом желательно удалить старые билды, чтобы там не осталось лишних заголовочных файлов (они автоматически не удаляются из таргет-папки, если даже вы удалили их из проекта).

Не один раз мне приходилось сталкиваться с ситуацией, когда вроде бы работающий проект, который уже не раз выгружался в iTunes Connect и выходил в App Store, внезапно перестает работать. Точнее, перестает работать его выгрузка в iTunes Connect.

При этом выдается ошибка вида "An App ID with Identifier <ваш_App_ID> is not available. Please enter a different string.". Плюс вам предлагаются варианты: импортнуть development-профиль (непонятно, правда, зачем), либо посетить Member Center и решить проблему там (видимо, методом шаманства, так как никаких других пояснений вам не дают). Эта проблема возникла с приходом 7-й версии Xcode, впервые ее заметили, насколько я видел в сети, в версии 7.3. Проблема может возникать на совершенно рандомных проектах, даже на тех, которые буквально сутки назад нормально выгружались в iTunes Connect.

Решается проблема двумя способами:

  • Проверьте, что в таргете, который вы архивируете через Project -> Archive, выставлен правильный Provisioning Profile на вкладке Build Settings. Если там выставлено Automatic - установите нужный distribution-профиль вручную.
  • Второй вариант возникновения проблемы - поломка профиля, который был создан средствами Xcode (в Member Center он носит название iOS Team Provision Profile Managed by Xcode). Решением проблемы в этом случае может стать либо его пересоздание (ручное удаление из Xcode и дальнейшее создание его же средствами), либо ручное создание профиля с удалением Managed-профиля.

Tags:

Я не являюсь сторонником использования сторибордов в проектах, так как плюсов от их применения обычно на порядки меньше, чем минусов. Однако зачастую приходится сопровождать старые проекты компаний, в которых уже используются сториборды, и, как правило, безболезненно (во всяком случае, с небольшими трудозатратами) выпилить их попросту невозможно.

В таких ситуациях часто можно столкнуться с крайне низкой производительностью рендера сториборда. Я встречал проблему, когда в одном из мало-мальски крупных проектов сториборд был сделан настолько громоздким, что на MacBook Pro 2013 с процессором Intel Core i5 2.5 GHz на борту этот сториборд загружался около 40 секунд, при этом каждое изменение в нем требовало примерно столько же времени на отвисание Xcode. Исправить эту ситуацию, к сожалению, можно только апгрейдом железа на рабочем компьютере (а еще оптимизацией Xcode, но, объективно, это вряд ли произойдет).

Я описал эту проблему для того, чтобы дать один совет, который может облегчить жизнь вам или тем, кто будет сопровождать проект после вас. Если вам приходится использовать сториборды в проекте, то ни в коем случае не добавляйте туда элементы, у которых включен runtime-рендеринг (то есть где используется IBDesignables). Наличие даже одного такого элемента всего на одном из экранов сториборда резко понизит производительность рендера, а если таких элементов окажется много - можно получить эффект, при котором вы 70% рабочего дня будете просто сражаться с постоянными лагами Xcode, вместо того, чтобы чинить баги и вести разработку. IBDesignables - это удобная и прикольная фишка, но ее производительность оставляет желать лучшего, поэтому лучше никогда рантайм-рендер не применять.

Tags:

Ситуация простая. Есть контроллер в сториборде, нужно отобразить поверх него вью, загруженную из XIB-файла. Внутри этой вью лежит полноразмерный (на весь размер вьюхи) UIScrollView с горизонтальной прокруткой и включенным пейджингом. Эта вьюха при awakeFromNib подгружает три других вьюшки и кладет их внутрь скролла, друг за другом, и настраивает contentSize.

Так вот иногда можно столкнуться с проблемой, что хоть фреймы и рассчитываются корректно (проверяем по отладке), но размещаются такие вьюхи на скролле неправильно - наползают друг на друга (на разных размерах экрана по-разному). Проблема может скрываться в том, что у этих вьюх, которые вы загружаете для псевдо-полноэкранного отображения на контроллере, выставлен размер Freeform в свойствах. Попробуйте поставить Inferred или любой из определенных размеров (4.7 дюйма, например) - это может помочь убрать проблему.

Tags: