В данной статье описаны некоторое типичные проблемы, возникающие при сборке/запуске на устройстве/выкладке в App Store проекта XCode для iOS, сгенерированного через Unity3D, а также их причины и решения. По личному опыту данные проблемы возникают довольно часто и не только у меня. В большей части статья может помочь тем, кто не очень знаком с нативной разработкой на iOS.

На момент написания статьи использовались Unity3d версии 5.1.2f и XCode 7.0.1. При использовании Unity3D версий 5.2 и выше, проблемы, перечисленные далее в статье также будут повторяться. Некоторые из проблем связаны с нововведениями в iOS 9.0. Внимание под катом много графики.

Bitcode


Технология Apple, введенная с релизом iOS 9. Является составной частью технологии App Thinning, используется для разбиения пакета приложения на несколько пакетов в зависимости от архитектуры устройства. Для того, чтобы в приложении можно было использовать Bitcode, необходимо не только, чтобы само приложение было написано с поддержкой Bitcode, но и все сторонние библиотеки должны быть собраны с использованием Bicode.

В случае, если одна из библиотек не поддерживает Bitcode, XCode выдаст ошибку на этапе линковки, и выглядеть она будет примерно так:



В данном случае, библиотека, с которой возникла проблема — GameCenter.

Исправить ошибку можно двумя путями:
  • Разобраться в чем дело. Возможно библиотека устарела и необходимо ее обновить. После обновления пересобрать проект XCode из Unity.
  • Выключить Bitcode в настройках приложения

С первым вариантом все предельно ясно. Второй путь на данный момент легче. Для выключения необходимо найти в настройках проекта необходимую опцию и выставить ее значение в NO.



iPad multitasking


Эта технология используется для возможности разделения экрана iPad на 2 части, в каждой из которых работает отдельное приложение.

При разработке игр данная технология не имеет смысла, так как в процессе игры необходимо задействовать весь экран устройства для максимального погружения пользователя. В этом вопросе Apple солидарна с нами:
Consider opting out only if your app falls into one of these narrow categories:
  • Camera-centric apps, where using the entire screen for preview and capturing a moment quickly is your primary feature
  • Full-device apps, such as games that use iPad sensors as part of their core gameplay

При тестировании приложения и сборке на устройство проблем с multitasking не возникает, ошибки не появляются. Однако при отправке пакета в App Store могут начаться проблемы.



Ошибки ITMS-90475 и ITMS-90474 возникают в самый последний момент отправки приложения в стор, когда вы уже думаете, что все хорошо. Избавиться от них лучше заранее, потому что сборка и отправка пакета может занимать очень продолжительное время (на практике доходило до 20 минут).

Решается проблема довольно просто, опять же, через настройки проекта:



Нужно выставить галочку «Requires full screen».

Ошибка при использовании категорий в Objective-C


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

При генерации проекта сам Unity не использует категории. Ошибки, связанные с категориями могут появляться только в случае использования нативных iOS плагинов в проекте. Это происходит из-за динамической природы Objective-C. Сама ошибка закрадывается на этапе линковки приложения. На этапе компиляции, если в файле исходного кода содержатся вызовы функций или методов другого класса, в объектный файл за место вызова класса записывается undefined symbol. Далее на линковщик при сборке исполняемого файла вставит за место undefined symbol объектный файл с необходимым методом. Если происходит сборка с использованием статических библиотек (или iOS фреймворков) линковщик пытается уменьшить размер исполняемого файла, не включая всю библиотеку целиком, а выбирая необходимые части.

В силу динамической природы Objective-C, symbol'ы определяются только для классов, и не определяются для методов. По этой причине если у класса, который был вставлен линковщиком за место undefined symbol не будет нужного метода, то в runtime произойдет exception типа unrecognized selector to instance.

Выглядеть это будет примерно так:



В примере из скриншота, у класса NSSting была попытка вызвать метод MD5, который не был найден, потому что был реализован в категории из сторонней библиотеки.

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

Более подробно проблема расписана здесь.

Нужно добавить дополнительный флаг линковщику и пересобрать проект. Есть 2 флага, которые могут помочь в этой ситуации: -all-load и -ObjC. Стоит попробовать сначала -all-load, и, если не поможет -ObjC (на практике был случай, когда остальные части нативного проекта переставали собираться при использовании -ObjC).

Добавляются флаги в настройках, во вкладку Build Settings XCode проекта:



Ошибка при отправке запроса на какой-либо URL


Ситуация выглядит примерно так: в Unity Editor или на Android запросы уходят без проблем, ответ приходит корректный, все хорошо. Даже все хорошо на девайсе с iOS < 9.0. На iOS 9.0 ответ не приходит или ошибка соединения. При этом необязательно будет exception, если в проекте предусмотрен случай, когда ответ от сервера не пришел или пришел с ошибкой. В этом случае проблема исходит от App Transport Security.

У проблемы есть 2 решения:
  • В документации перечислены требования, которым должны соответствовать ваши сервера, для того, чтобы запросы проходили без проблем. + Apple настаивает на использовании https
  • Добавить URL в список исключений приложения

Исключения проекта задаются в файле .plist с настройками XCode проекта. Нужно знать, каким требованиям не соответствует домен, чтобы правильно составить исключение, используя значения, указанный в документации. Можно редактировать .plist файл как исходный код, можно добавить необходимые поля, используя редактор XCode, кому как удобно.

Пример исключения в виде исходного кода (кусок кода добавляется в уже существующий файл Info.plist):

	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSExceptionDomains</key>
		<dict>
			<key>your_domain.com</key>
			<dict>
				<key>NSIncludesSubdomains</key>
				<true/>
				<key>NSExceptionRequiresForwardSecrecy</key>
				<false/>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
			</dict>
		</dict>
	</dict>

После данных манипуляций запрос проходит без проблем, а ранние версии iOS игнорируют NSAppTransportSecurity ключи в .plist проекта.

Missing arm64 architecture


Ошибка, известная как itms-90533. Появляется при попытке отправить в стор пакет, который не содержит архитектуру arm64.



Убедитесь, что при сборке проекта в Unity в разделе Project Settings -> Player для iOS в качестве Scripting Backend использовался IL2CPP(вторая опция mono2x не умеет arm64).



Если использовался mono2x, Xcode проект нужно перегенировать.

Проблема с Trampolines


В runtime приложения происходит ошибка, приложение крэшится и в логе появляется нечто похожее на «Ran out of trampolines of type 0/1/2». Эта ошибка также специфична для iOS. Trampolines — вещь, которая используется внутри Mono. Узнать о них больше можно здесь и здесь.

Если коротко, то trampolines — небольшие куски ассемблерного кода, написанные для выполнения разных задач в mono runtime. Они генерируются в runtime с использованием нативных macro, используемых JIT компилятором. Можно рассматривать trampolines как инструмент для вызова JIT-кода из runtime и обратно. Например JIT-кодом можно считать generic методы использование интерфейсов в C# для Unity.

Проблемы начинаются с того, что iOS не поддерживает JIT, только AOT. Это возможно по ряду причин: iOS не разрешает использование страниц памяти, которые одновременно writable и executable, Apple может запрещать JIT-компиляцию, как следствие запрета исполнения приложениями кода, который не присутствовал в билде на момент его отправки на ревью в стор.

Всего в mono 3 вида trampolines, предположительные роли которых таковы:

  • type 0 — generic methods
  • type 1 — recursive generics
  • type 2 — interfaces

Для iOS trampolines задаются заранее, и если ваш проект будет пытаться использовать их больше, чем создано на момент запуска, приложение упадет с ошибкой ran out of trampolines. По умолчанию в приложении создается 1024 trampoline типа 0, 1024 типа 1 и 128 типа 2.

Для решения данной ошибки нужно просто увеличить число trampolines, нужного для вашего проекта типа, на большее. Число trampolines, необходимое в проекте, можно выяснить только экспирементально постепенно увеличивая это значение. Делается это с использованием с помощью AOT Compilation Options в Unity (Project Settings -> Player).

Увеличить число trampolines можно с использованием следующих параметров:

  • тип 0 — ntrampolines=xxxx
  • тип 1 — nrgctx-trampolines=xxxx
  • тип 2 — nimt-trampolines=xxxx



Стоит обратить внимание на то, что в проекте может не хватать trampoline нескольких типов. В этом случае ключи необходимо указывать через ";" без пробелов, иначе ключи будут игнорироваться.

Комментарии (3)


  1. dimamatik
    20.10.2015 19:25

    Очень интересно. Можно ли использовать статью\ссылки на нее в своей статье по автоматизации билда под iOS?


    1. vmchar
      20.10.2015 23:09

      Конечно, используйте. По поводу автоматизации у меня тоже есть пару набросков. Если есть еще какие-то вопросы, обращайтесь


      1. dimamatik
        21.10.2015 00:02

        Спасибо