Как создать разные версии приложений с разными скинами/брендингом?

Я создаю универсальное приложение, которое будет иметь разные сборки для разных клиентов. Приложение на 99,5% идентично для каждого клиента, различие заключается в том, что каждый из них заклеймен на клиенте своими конкретными изображениями, текстом и значком приложения и т.д.

Очевидно, это можно сделать с помощью флагов, таких как:

#if defined (CUSTOMER_A)
    NSString* text = @"Text for customer A";
    UIImage *image = [UIImage imageNamed:@"customerAImage"];
#elseif defined (CUSTOMER_B)
    NSString* text = @"Text for customer B";
    UIImage *image = [UIImage imageNamed:@"customerBImage"];

Но, очевидно, я бы хотел этого избежать и просто:

   NSString* text = @"Text";
   UIImage *image = [UIImage imageNamed:@"image"];

(Текст будет локализуемым, поэтому в финальной версии он будет использовать NSLocalizedString).

Мне было интересно, может ли возможный подход разместить проект в рабочее пространство вместе с рядом статических библиотек, каждый из которых содержит конкретный текст и изображения для каждого клиента, а затем использует разные схемы для создания разных сборок. Таким образом, схема A создала бы цель, построенную с основным проектом и статической библиотекой A. Например:

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

  • Как можно получить изображение в статической библиотеке из кода основного проекта? Должен ли быть создан пакет для доступа к содержимому библиотеки, как это делается?

  • Можно ли изменить значки рабочего стола и рынка приложений в зависимости от того, какая схема используется?

  • Можно ли указать другой набор сертификатов распространения и т.д. для каждой схемы?

  • Правда ли, что статические библиотеки не могут содержать локализованные варианты?

Это для iOS, поэтому невозможно использовать фреймворк для этого.

Спасибо за любую обратную связь.

(P.S. система сборки будет автоматизирована с использованием Jenkins).

Ответ 1

Вам просто нужно создать несколько целей в своем проекте и папки ресурсов для каждой цели (клиент/бренд). Вот как это сделать:

  • Создание двух новых папок ресурсов а. Resources_Customer1 б. Resource_Customer2
  • Скопируйте соответствующие ресурсы в соответствующие папки
  • Выбрать проект → Цели
  • Дублировать объект
  • Настройте имя ресурса в [Копировать ресурсы сборки], чтобы он указывал на подходящую клиент/бренд
  • Проверить и проверить соответствующие целевые трассы на основе выбранного целевого объекта.

Надеюсь, это поможет!

Ответ 2

Сколько вариантов вы планируете поддерживать? Наличие цели/проекта/etc становится громоздким, если у вас больше, чем несколько. Это примерно то, что мы делаем:

  • Jenkins контролирует исходный контроль и строит любые новые коммиты, которые он видит.
  • Дженкинс архивирует вывод .app вместе с коллекцией "скинов"
  • Домашний веб-сайт подключается к API Jenkins и перечисляет все перестановки (ветвь, скин).
  • "Пользователь" выбирает (ветвь, скин), а домашний сайт вызывает script, который "применяет" скин к пакету .app, подписывает его и упаковывает его в .ipa, который затем устанавливается к пользовательскому устройству.

Процесс приложения "skin" в основном состоит из предоставления списка "скиновых" каталогов, которые являются файлами, которые нужно скопировать в пакет приложений. Любые дубликаты файлов будут перезаписаны. База приложений Info.plist объединяется с любыми изменениями, указанными в каталоге скинов.

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

Ответ 3

Мы применяем процесс post-build, частично отчасти ответивший на @SedateAlien, примерно 3 года назад у моего нынешнего работодателя, и я должен сказать, что это определенно способ пойти, если вы имеете дело не только с несколькими бренды.

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

Здесь мы рассмотрели подход высокого уровня:

  • Создайте и создайте проект приложения с единственной целью, которая будет действовать как ваш прототип, т.е. только один бренд или "некнижированный" вариант. Вы будете выполнять большую часть своей разработки против этой цели, поэтому убедитесь, что она имеет достаточные базовые ресурсы для полноценного использования.
  • Создайте репозиторий "ресурсы бренда" где-нибудь, чтобы хранить различные активы бренда. Мы используем файл "brand.config" в каждом каталоге брендов, который содержит различную информацию о бренде, такую ​​как имя приложения, идентификатор пакета и т.д., А также различные известные файлы ресурсов, которые будут заменены на пакет.
  • Настройте сервер сборки (мы используем Jenkins) или оболочку script для выполнения процесса брендинга на основе встроенного прототипа .app. Процесс бренда работает следующим образом:

  • Скопируйте весь прототип пакета .app в новый каталог и удалите любые подписи кода.

  • Используйте Plistbuddy, чтобы изменить панель связки на основе параметров в файле brand.config.
  • Обменивать известные ресурсы (изображения и т.д.) в комплекте с ресурсами из каталога ресурсов бренда.
  • Кодифицируйте исполняемый файл приложения и свяжите его и запишите в .ipa.

Самая сложная часть, без сомнения, будет координировать. Возможно, вам понадобится использовать файл прав, а также убедитесь, что сертификаты правильно идентифицированы и установлены на вашей машине сборки.

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

Я также должен упомянуть, что у нас никогда не было проблем с представлениями в App Store относительно этого процесса, и мы представляем множество приложений (100+ для каждого обновления).

Ответ 4

Я сделал то же самое для компании, чтобы поделиться с вами тем, как я это сделал. Я был с вами, пока вы не сказали Дженкинсу, поскольку я использовал Xcode, но, возможно, вы можете заставить его работать.

По существу вы создаете один проект библиотеки, который включен в ваши проекты приложений "skin" . Шкуры включают любое изображение, специфичное для приложения, plist и, возможно, даже несколько методов. Вы создаете файл .h, который используется инфраструктурой с одним классом, который vends объекты, или функции, или глобальные переменные - вы решаете. Заголовок для этого файла включен в библиотеку, чтобы библиотека могла создавать.

Например, предположим, что у него есть "extern UIImage * mainBackgroundImage;", и вы ссылаетесь на это в библиотеке (т.е. вы извлекаете его для отображения).

У приложения "skin" , конечно, есть файл .m(или .c), чтобы разрешить все публичные элементы, которые вы обещали в вашем файле .h.

В Xcode вы создадите проект библиотеки. Вы бы включили это в каждый проект "skin" (имея в виду, что вам нужно получить зависимость от библиотеки на панели "Фаза сборки" и убедиться, что библиотека включена в фазу "Ссылка". Если вы используете категории в своей библиотеке, вам нужно для принудительной загрузки библиотеки путем добавления специальной линии для привязки флагов в каждом целевом проекте.

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

Ответ 5

Стоит отметить, что любой, кто сталкивается с этой дилеммой, что введение каталогов активов в iOS7 делает эту проблему очень простой в XCode. У меня есть проекты с несколькими целями и каталоги активов, которые позволяют легко обрабатывать ресурсы, настраивать параметры сборки и проблемы с подделкой кода в одном месте. Кроме того, когда Apple вводит новый форм-фактор (например, iPhone 6+), легко увидеть, какие каталоги активов не обновлены (например, отсутствует экран запуска iPhone 6+). Очень гибкий. Скажем, у вас есть 12 целей, требующих скининга, 7 школ, 5 рабочих мест. Создайте новый каталог активов под названием SchoolMedia, а другой - WorkMedia. В каждый каталог добавьте набор изображений, который называется main_symbol. В XIB используйте main_symbol. Сделайте SchoolMedia членом школьных целей и WorkMedia членом целевых заданий. Правое main_symbol будет отображаться в зависимости от цели. Перестановки бесконечны, и визуальный интерфейс XCode позволяет легко понять, что происходит (даже если это было немного неуправляемо в прошлом)