Как добавить новый target в iOS-проект (или как сделать lite-версию приложения)
Рано или поздно практически любой уважающий себя iOS-разработчик сталкивается с необходимостью выпуска двух практически идентичных по функционалу приложений, зачастую - обычной и lite-версии своего продукта. Первое, что приходит в голову в таком случае разработчику, не сталкивавшемуся с подобной задачей ранее, - скопировать проект и модифицировать его код. Однако такой подход приведет к необходимости поддерживать уже не один, а два раздельных проекта. Изменения, вносимые в один проект, зачастую необходимо дублировать и в другом проекте. Это неудобно и, более того, опасно: всегда существует вероятность забыть исправить код второго проекта, после изменения первого.
Правильным способом решения этой задачи является добавление дополнительного target в исходный проект. В результате, при сохранение общей кодовой базы исходного проекта разработчик может собирать и старый и новый продукт буквально несколькими кликами. Об этом способе мы и поговорим в данной заметке.
Сперва поговорим о том, что такое этот пресловутый target. Данный объект представляет собой набор инструкций по сборке бинарного файла приложения и включает в себя флаги компиляции, настройки provisioning profile-ов, имя бандла и прочую мета-информацию. Если мы хотим собрать еще одно приложение помимо существующего на основе имеющейся кодовой базы, нам нужно создать новый target.
Как добавить новый target?
Для добавления нового target в проект нужно аккуратно проделать ряд простых действий.
Прежде всего, выберите текущий target проекта (см. рис 1). В нашем примере он имеет имя ContractFinder.
Далее нажмите на текущем (и пока еще единственном) target правой кнопкой мыши и выберите Duplicate (см. рис. 2). Это создаст копию текущего target-а. Копировать target в нашем случае проще всего, поскольку создаваемый таким образом новый target будет иметь найстройки, практически идентичные исходному. При создании target с нуля процесс будет отличаться.
В нашем примере мы создаем новый target для iPhone-приложения, поэтому Xcode спрашивает нас, хотим ли мы продублировать существующий target или же нам надо сделать iPad-версию нашего приложения (см. рис. 3). Выбираем Duplicate Only.
В результате этих действий в проекте появится второй target с именем вида Исходное-имя Copy, а также копия plist-а изначального target’а. Файл plist рекомендую сразу переименовать, чтобы не возникало путаницы при добавлении следующего target в этот же проект. Проверявшаяся мною версия Xcode 4.6.3 не умела отслеживать изменения имени plist-файла для нового target, поэтому если при выборе target вы наблюдаете кнопку Choose Info.plist File… как на рис. 4, смело нажимайте эту кнопку и выбирайте переименованный файл.
Поскольку мы копировали существующий target, все настройки нового target будут идентичны исходному target за исключением параметра Product Name. Этот параметр используется для идентификации приложения в Bundle Identifier (см. рис. 5 - в нашем примере ContractFinder-copy). Я настоятельно рекомендую заменять Product Name на что-нибудь более осмысленное, например на Имя-проекта Lite.
Для смены Product Name, выберите новый target, перейдите на вкладку Build Settings, найдите пункт Product Name и замените его на нужное значение. В нашем примере (см. рис. 6) мы заменили строку ContractFinder-copy
на строку Z-monitor MSK
.
После этой нехитрой операции Bundle Identifier нового target примет корректный вид (см. рис. 7).
Поскольку по-умолчанию в plist-файле target-а подпись под иконкой приложения берется именно из Product Name, а приложения различаются своими Bundle Identifier-ами, мы получаем не одно, а два разных приложения в одном проекте. На устройстве они будут выглядеть как на рис. 8.
Как написать код, специфичный только для одного target?
Для того, чтобы различать в какой именно версии приложения должен работать тот или иной код, принято использовать условные дерективы компилятора. Для этого мы можем создать флаг компиляции (в нашем примере, он называется MSK_VERSION
) в Build Settings нового target’а как в примере на рис. 9. Чтобы его создать найдите параметр Other C Flags и для каждой из схем сборки (в данном примере это Debug, Distribution Ad Hoc и Release) добавьте строку вида -DNAME_OF_YOUR_FLAG
(в нашем примере, повторюсь, это -DMSK_VERSION
). Обратите внимание на то, что в пункте Other C++ Flags добавленный флаг появится самостоятельно, руками его туда вписывать не надо.
В нашем примере у схем Distribution Ad Hoc и Release видна строка -DNS_BLOCK_ASSERTI...
исключительно потому, что она предшествует строке -DMSK_VERSION
; строка -DMSK_VERSION
указана для всех трех схем сборки.
В самом исходном коде для ветвления между собираемыми версиями target-ов при этом необходимо использовать код вида:
Как добавить ресурсы, специфичные только для одного target?
Если в проект необходимо добавить ресурсы только для одного target-а, необходимо при перетаскивании этих ресурсов в экран Project Navigator-а в окрывшемся диалоговом окне добавления файла в проект установить галочку напротив нужного target-а для которого данный ресурс добавляется.
Если из нового target-а необходимо удалить какие-либо ресурсы, которые были нужны для старого target-а, но не нужны для нового, это легко сделать перейдя ко вкладке Build Phases, развернув список Copy Bundle Resources и удалив ненужные для данного target-а ресурсы.
Под ресурсами в данном пункте подразумеваются не только изображения и файлы sqlite, но и, например, xib-файлы.
Как изменить название новой схемы сборки?
При создании нового target в проекте появится дополнительно еще одна схема сборки, повторяющая изначальную схему с добавлением слова copy в конце названия (см. рис. 10. На схему Kal можно не обращать внимания, она возникла из-за специфики нашего проекта).
Для переименования схемы необходимо в меню из рис. 10 выбрать пункт Manage Schemes…. После этого в открывшемся окне (см. рис. 11) необходимо найти имя новой схемы с copy на конце, нажать на нем последовательно два раза (первый раз для выбора строки со схемой, второй - для перехода в режим редактирования имени схемы) и переименовать схему.
Результат переименования схемы в нашем проекте можно увидеть на рис. 12.
Как изменить иконку и загрузочный экран (splash screen) для нового target?
Созданный target использует иконку и загрузочные экраны исходного приложения. Едва ли нас это устроит с учетом того, что создаваемая lite-версия, скорее всего, должна отличаться в оформлении от основной версии.
Для изменения иконок и загрузочных экранов прежде всего необходимо подготовить файлы. Я настоятельно рекомендую назвать иконки и загрузочные экраны единообразно с их аналогами для исходного приложения с добавлением какого-либо префикса. В нашем проекте мы использовали префикс Red.
С изменением иконок все достаточно просто - выбираем новый target, идем на вкладку Summary, находим пункт App Icons и перетаскиваем новые иконки прямо поверх старых. Если имена старых и новых иконок отличаются, все проходит гладко (проверял на Xcode 6.4.3; если у вас возникли проблемы, читайте дальше про способ с загрузочными экранами и сделайте с иконками так же), в проекте сохраняются оба набора иконок, иконки правильно назначаются для каждого target-а.
С загрузочными экранами этот номер не проходит, поскольку Xcode имеет привычку переименовывать изображения с загрузочным экраном в нечто вида Default.png. В этом случае при попытке изменить загрузочные экраны тем же способом, что и иконки, приведет к тому, что в исходном target эти изображения изменятся на новые (что не всегда нужно).
Для корректного задания загрузочных экранов я рекомендую для каждого из target-ов добавить в соответствующий Info.plist файл по ключу UILaunchImageFile
(см. рис. 13 и 14).
В зависимости от имени заданного файла Xcode будет искать файлы для ретина-дисплеев по своим внутренним правилам именования загрузочных экранов. Если задаваемый файл имеет имя вида Default.png, то остальные файлы при именовании в стиле Default@2x.png и Default-568h@2x.png будут успешно обнаружены и использованы (см. рис. 15 и 16)
В результате проделанной процедуры новое приложение будет отличаться от старого и на экране мобильного устройства (см. рис. 17).
Заключение
В данной заметке мы рассмотрели практически со всех сторон процесс добавления нового target-а в iOS-проект. Хочу обратить ваше внимание на то, что мы использовали Xcode 4.6.3 и работали с проектам, поддерживающим iOS 4.3 и выше. В более новых версиях Xcode и iOS могут быть незначительные отличия, которые, тем не менее, не будут сильно отличаться от приведенной процедуры.
При возникновении каких-либо ко мне вопросов/комментариев/пожеланий по рассмотренной теме, прошу присылать их мне на электронную почту max[at]maxmikheev.org.