xcode 如何使用不同的皮肤/品牌构建不同版本的应用程序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11750124/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to build different versions of apps with different skins/branding?
提问by Gruntcakes
I am creating a generic app which will have different builds for different customers. The app is 99.5% identical for each customer, the difference being each is branded with the customer's own particular images and text and app icon etc.
我正在创建一个通用应用程序,它将为不同的客户提供不同的版本。每个客户的应用程序 99.5% 相同,不同之处在于每个客户都使用客户自己的特定图像和文本以及应用程序图标等进行品牌化。
Obviously this could be done using flags such as:
显然,这可以使用以下标志来完成:
#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"];
But obviously I'd like to avoid this and just have:
但显然我想避免这种情况,只需要:
NSString* text = @"Text";
UIImage *image = [UIImage imageNamed:@"image"];
(The text would be localizable, so it would be using NSLocalizedString in the final version).
(文本将是可本地化的,因此在最终版本中将使用 NSLocalizedString)。
I was wondering if a possible approach would be to place the project into a workspace along with a number of static libraries, each of which contains the specific text and images for each customer and then use different schemes to create different builds. So Scheme A would create a target built with the main project and static library A for example.
我想知道是否有一种可能的方法是将项目与一些静态库一起放入工作区,每个静态库都包含每个客户的特定文本和图像,然后使用不同的方案来创建不同的构建。例如,方案 A 将创建一个使用主项目和静态库 A 构建的目标。
I started with a small proof of concept but before going too far with it I'd first like to check this is a feasible and reasonable approach, or if there's a better alternative. If it is feasible then a few questions come to mind:
我从一个小的概念证明开始,但在走得太远之前,我首先想检查这是一种可行且合理的方法,或者是否有更好的替代方法。如果可行,那么会想到几个问题:
how can an image in a static library be accessed from the code in the main project? Does a bundle have to be created to access the contents of the library, how is this done?
is it possible to change the application desktop and marketplace icons depending upon which scheme is used?
is it possible to specify a different set of distribution certificates etc. to be used per scheme?
is it true that static libraries cannot contain localized variants?
如何从主项目中的代码访问静态库中的图像?是否必须创建包才能访问库的内容,这是如何完成的?
是否可以根据使用的方案更改应用程序桌面和市场图标?
是否可以为每个方案指定一组不同的分发证书等?
静态库不能包含本地化变体是真的吗?
This is for iOS so its not possible to use a framework for this.
这是针对 iOS 的,因此不可能为此使用框架。
Thanks for any feedback.
感谢您的任何反馈。
(P.S. the build system will be automated using Jenkins).
(PS 构建系统将使用 Jenkins 自动化)。
采纳答案by Native_Mobile_Arch_Dev
You just need to create multiple targets in your project and have resource folders for each target (customer/brand). Here's how to accomplish this:
您只需要在项目中创建多个目标,并为每个目标(客户/品牌)设置资源文件夹。以下是实现此目的的方法:
- Create two new Resource Folders a. Resources_Customer1 b. Resource_Customer2
- Copy the appropriate resources into the respective folders
- Select Project -> Targets
- Duplicate the Target
- Customize the Resources Name in [Copy Build Resources] so that it points to the appropriate
customer/brand - Test and Verify the appropriate Target runs based upon the Target selected.
- 创建两个新的资源文件夹 Resources_Customer1 b. 资源_客户 2
- 将相应的资源复制到各自的文件夹中
- 选择项目 -> 目标
- 复制目标
- 在【复制构建资源】中自定义资源名称,使其指向合适的
客户/品牌 - 根据所选目标测试并验证适当的目标运行。
Hope this helps!
希望这可以帮助!
回答by Sedate Alien
How many variants do you plan on supporting? Having a target/project/etc becomes unwieldy if you have more than a handful. This is roughly what we do:
您计划支持多少种变体?如果您有多个目标/项目/等,那么拥有一个目标/项目/等就会变得笨拙。我们的做法大致是这样的:
- Jenkins monitors the source control and builds any new commits it sees.
- Jenkins archives the output .app along with a collection of "skins"
- A home-brewed website hooks into the Jenkins API and lists all the permutations of (branch, skin).
- The "user" selects a (branch, skin) and the home-brewed site calls a script that "applies" the skin to the .app bundle, signs it and packages it into an .ipa that is then installed to the user's device.
- Jenkins 监控源代码控制并构建它看到的任何新提交。
- Jenkins 将输出 .app 与“皮肤”集合一起存档
- 一个自制的网站连接到 Jenkins API 并列出(分支、皮肤)的所有排列。
- “用户”选择一个(分支,皮肤),自制站点调用一个脚本,该脚本将皮肤“应用”到 .app 包中,对其进行签名并将其打包到 .ipa 中,然后将其安装到用户的设备上。
The skin "application" process basically consists of providing a list of "skin" directories, which are files to be copied into the application bundle. Any duplicate files are overwritten by the skin. The application's base Info.plist
is merged with any changes specified in the skin directory.
皮肤“应用程序”过程基本上包括提供“皮肤”目录列表,这些目录是要复制到应用程序包中的文件。任何重复的文件都会被皮肤覆盖。应用程序的基础Info.plist
与皮肤目录中指定的任何更改合并。
Essentially, we try to separate out the skinning as much as possible from the code itself. We have found that any solution involving Xcode itself involves much manual self-removal of hair from developers' scalps.
本质上,我们尽量将皮肤与代码本身分开。我们发现任何涉及 Xcode 本身的解决方案都涉及从开发人员的头皮上手动自行去除头发。
回答by devios1
We have been applying a post-build process inspired in part by @SedateAlien's answer for about 3 years now at my current employer and I have to say it is definitely the way to go if you are dealing with more than just a few brands.
大约 3 年来,我们一直在应用一个后期构建过程,部分灵感来自@SedateAlien 的回答,现在我现在的雇主已经在我现在的雇主那里应用了大约 3 年,我不得不说,如果您要处理的不仅仅是几个品牌,这绝对是一种可行的方法。
It's not exactly a walk in the park, but once you get everything finally working properly it will save you tons of time, as well as keeping your project clean and simple.
这并不完全是在公园里散步,但是一旦您使一切最终正常运行,它将为您节省大量时间,并保持您的项目简洁明了。
Here's the high-level approach we took:
这是我们采用的高级方法:
- Design and build your app project with a single target that will act as your prototype, i.e. just a single brand or "unskinned" variant. You will perform most of your development against this target so make sure it has enough base resources to be fully usable.
- Set up a "brand resources" repository somewhere to hold the different brand assets. We use a "brand.config" file in each brand's directory that contains various brand-specific info like app name, bundle id, etc, as well as the various well-known resource files that will be swapped into the bundle.
Set up a build server (we use Jenkins) or a shell script to perform the branding process based on a built prototype .app. The brand process works like so:
Copy the entire prototype .app bundle to a new directory and remove any code signatures.
- Use Plistbuddy to modify the bundle's plist based on the params in brand.config.
- Swap-out the well-known resources (images, etc.) in the bundle with the resources from the brand's resource directory.
- Codesign the app executable and bundle and zip up into an .ipa.
- 设计和构建您的应用程序项目,将作为您的原型的单一目标,即只有一个品牌或“无皮肤”变体。您将针对此目标执行大部分开发,因此请确保它具有足够的基础资源以完全可用。
- 在某处设置一个“品牌资源”存储库来保存不同的品牌资产。我们在每个品牌的目录中使用一个“brand.config”文件,其中包含各种品牌特定的信息,如应用程序名称、捆绑包 ID 等,以及将交换到捆绑包中的各种知名资源文件。
设置构建服务器(我们使用 Jenkins)或 shell 脚本来执行基于构建原型 .app 的品牌化过程。品牌流程如下:
将整个原型 .app 包复制到新目录并删除所有代码签名。
- 使用 Plistbuddy 根据brand.config 中的参数修改bundle 的plist。
- 用品牌资源目录中的资源交换捆绑包中的知名资源(图像等)。
- 对应用程序可执行文件进行编码,并将其打包并压缩为 .ipa。
The trickiest part will no doubt be the codesigning. You will probably need to use an entitlements file and will also have to make sure the certificates are properly identified and installed on your build machine.
最棘手的部分无疑是代码设计。您可能需要使用权利文件,并且还必须确保证书被正确识别并安装在您的构建机器上。
If you run into issues, don't give up. It ispossible I promise! I will try to help with any specific issues people have in the comments, but understand it's been a while since I actively developed this so I won't be able to be too detailed.
如果您遇到问题,请不要放弃。这是可能的我的承诺!我会尽量帮助解决人们在评论中遇到的任何具体问题,但我知道我已经有一段时间没有积极开发这个了,所以我不能说得太详细了。
I should also mention we've never had any issues with App Store submissions concerning this process, and we submit a lotof apps (100+ for each update).
我还应该提到,我们在 App Store 提交方面从未遇到过与此过程相关的任何问题,而且我们提交了大量应用程序(每次更新 100 多个)。
回答by arcady bob
Worth noting for anyone who stumbles across this dilemma that the introduction of asset catalogs in iOS7 make this problem very easy to solve in XCode. I have projects with multiple targets and asset catalogs makes it easy to handle resources, custom build settings and code signing issues all in one place. Not only that, when Apple introduces a new form factor (eg. iPhone 6+) it is easy to see which of asset catalogs are not up to date (eg. missing the iPhone 6+ launch screen). Very flexible too. Let's say you have 12 targets needing skinning, 7 are schools, 5 are workplaces. Create a new asset catalog called SchoolMedia
and another called WorkMedia
. Into each catalog add an image set called main_symbol
. In your XIB, use main_symbol
. Make SchoolMedia
a member of the school targets and WorkMedia
a member of the work targets. The right main_symbol
will be displayed depending on the target. The permutations are endless and XCode's visual interface makes it easy to understand what is going on (even if it was a bit unmanageable in the past)
值得一提的是,任何遇到过这种困境的人,iOS7 中资产目录的引入使得这个问题在 XCode 中很容易解决。我有多个目标和资产目录的项目,可以轻松地在一个地方处理资源、自定义构建设置和代码签名问题。不仅如此,当 Apple 推出新的外形规格(例如 iPhone 6+)时,很容易看出哪些资产目录不是最新的(例如,缺少 iPhone 6+ 的启动屏幕)。也非常灵活。假设您有 12 个需要剥皮的目标,其中 7 个是学校,5 个是工作场所。创建一个名为 的新资产目录SchoolMedia
,另一个名为WorkMedia
。在每个目录中添加一个名为main_symbol
. 在您的 XIB 中,使用main_symbol
. 使SchoolMedia
学校目标的成员,WorkMedia
工作目标的一员。main_symbol
将根据目标显示右侧。排列是无止境的,XCode 的可视化界面使人们很容易理解正在发生的事情(即使过去有点难以管理)
回答by David H
I did exactly this same thing for a company so I can share with you how I did it. I was with you until you said Jenkins, since I used Xcode, but maybe you can make it work.
我为一家公司做了完全相同的事情,所以我可以与你分享我是如何做到的。我一直和你在一起,直到你说 Jenkins,因为我使用了 Xcode,但也许你可以让它工作。
Essentially you create one library project that is included in your "skin" application projects. The skins include any app-specific image, the plist, and possibly even a few methods. You create a .h file that is used by the framework with one class that vends objects, or functions, or globals - you decide. The header for this file is included in the library to allow the library to build.
本质上,您创建了一个包含在“皮肤”应用程序项目中的库项目。皮肤包括任何特定于应用程序的图像、plist,甚至可能还有一些方法。您创建一个由框架使用的 .h 文件,其中包含一个提供对象、函数或全局变量的类 - 您决定。此文件的标头包含在库中以允许构建库。
For instance, suppose it has "extern UIImage *mainBackgroundImage;", and you reference that in the library (ie you retrieve it for display).
例如,假设它有“extern UIImage *mainBackgroundImage;”,并且您在库中引用它(即您检索它以进行显示)。
The "skin" application of course has the .m (or .c) file to resolve all the public items you promised in your .h file.
“皮肤”应用程序当然有 .m(或 .c)文件来解决您在 .h 文件中承诺的所有公共项目。
In Xcode, you would create a library project. You would include that in every "skin" project (keeping in mind you need to get a dependency on the library in the Build Phase pane, and make sure the library is included in the Link phase. If you use categories in your library you need to force_load the library by adding a special line to Link Flags in each target's project.
在 Xcode 中,您将创建一个库项目。您可以将其包含在每个“皮肤”项目中(请记住,您需要在 Build Phase 窗格中获得对库的依赖,并确保该库包含在 Link 阶段中。如果您在库中使用类别,则需要通过向每个目标项目中的链接标志添加特殊行来强制加载库。
You can in a half hour build a little test harness with two projects to convince yourself that this works just fine.
您可以在半小时内用两个项目构建一个小测试工具,让自己相信这很好用。