Best strategy for automating multiple builds from a single white-label xcode project? - xcode

I'm researching the best approach to automating our build process. I've got my own ideas (through experience on a previous non-iOS project) but need good arguments for and against various possibilities.
Objective: A single xcode project with a single target (think white-label) needs to be built in 1..N different flavours (concrete brandings) with minimum user interaction and minimum technical knowledge. For AdHoc and/or AppStore.
Essentially, that will mean specifying per build; a folder containing Icons + Splashscreen, a bundle containing brand specific resources and (presumably?) the Info.plist, specifying appname, bundle-id, etc.
Issues that need to be respected or clarified;
Manual build of a single brand via Idiot-Proof GUI (choose a git
branch/tag, specify a certain brand, configure the app e.g.
IAP-enabled, server-domainname, etc - will be written to the
info.plist)
In previous manual tests, setting the executable name in
the plist didn't work? Sorry, have forgotten the exact problem..
perhaps was only an Xcode Debug buildconfig problem, not relevant to
a distribution build?
Code-Signing?!? Can the profile be specified
on-the-fly? Some brands need to be built with the customer's own
profile.
My personal feeling: Hudson or CruiseControl + Xcode plugin.
There seems to be plenty of documentation around for an Xcode solution and I've seen this in action on a Flex project I worked on, with almost exactly the same white-label/branding requirements. Of course that was using Ant script though and there was NO behavioral config to respect. That's my only uncertainty here... I suspect it would have to be hardcoded somewhere, but that's not the answer that's going to please some people. There is a wish to be able to specify the various app-config settings (server url, is function Foo supported, is the view X displayed, etc, etc) via a GUI form, when building manually. I'm not sure how easy it would be to shoehorn that into a typical Hudson or CC config?
And hence one suggestion that has been made is to write an OSX app for building our clients. The theory being, nice clean non-tech UI for entering all the necessary meta data & app setting and a big shiny green button labelled "Build". But personally I'm skeptical that this approach is any more flexible or easier to implement than a classic CI solution.
So the question is basically, what's preferable; a classic server based, version control integrated, CI approach or a custom OSX utility?
Whichever we go for it'll almost certainly be a requirement to get it up and running in 2 or 3 days (definately less than one week).

IMHO you can resolve all issues using different targets of XCode.
Every target will share the code but it could:
be signing with diferent profiles
use diferent plist: this implies having different names..
use diferent brand images. You only have to name the image with the same name and select the correct target in file inspector.
Build with one click in XCode.
I hope this helps

An extremely later reply, but the approach I would take would be to create the white label IPA, and then create a script to:
1. Unzip it (change the .ipa file extension to .zip).
2. Change assets.
Update the info.plist (using Plistbuddy command)
Zip it again.
Resign the code.
See this script as a starting point: https://gist.github.com/catmac/1682965

Very late answer. But I would go with different .xcconfig files and multiple schemes. The scheme names could be a combination of target/brand.

Related

tfs2013 share project across many projects

I have a few (3) core projects I want to share across many solutions (12+).
So, say I have 12 websites and they use some shared back end core code (in this case I'm not talking about shared js, css or views - I'm talking about business objects, entity stuff, etc.).
I need to be able to identify which site has which version of the shared code in dev, test, prod, etc. so a developer can get the website code and get the right version of the shared code to develop or patch the website.
And then the MS build server needs to know which version of the shared code to get for the deployment.
To solve this, I'm seeing people branch that core code - which seems absurd to do 12+ times. (I do expect to branch the core code sometimes for things like hot fixes and long running projects.)
I'm also seeing people copy DLLs of the core code and check those in.
I would think I would list the dependencies for my solutions based on TFS label names somewhere so developers can easily get the apps running with the right code and given a tfs label the build server can get the code for the website and the proper version of the core code. I'm using TFS & VS 2013 at the moment too, so there's that.
So, is there a way to do this that's straightforward, supportable/scale-able and intuitive? Thanks - Peter
Labels in TFS is very limited. For example once the label created you couldn't change and update it. If one of your core projects updated, did you need to create a new label for it. If you did and use the new label for one of your solution. However you found there are some bugs in this update, you need a newer update of your core project to fix the bug. Then a newer label created, you need to manually maintain the dependencies which seems not to be an easy job.
Moreover how to list the dependencies for your solutions based on TFS label names? TFS don't have this built-in option, seems the only way is store it in a txt or someother files and check in the source control. Every time the developer open a website application need to check it first and get label from server to their workspace and work on it.
Usually the purpose of sharing code between projects is reducing maintenance. There’s two main code sharing paths: source and binary. The difference between them you could take a look at this blog: Code Sharing in Team Foundation Server
Sharing code between products is a primary cause of quality erosion and elevated bug counts. I would recommend you to build separately and sharing binary output through NuGet which use preferable.
Also take a look below similar questions:
Sharing code between solutions in TFS
TFS 2010 Branch Across Team Projects - Best Practices

Define schemes in an xcode phonegap project from terminal

I'm writing a script to archive the iOS portion of a phonegap project. The script wipes the directory that the project is in and then repopulates it using the latest code from source control. I then run$ phonegap local build ios in order to build the project. However in order to archive the project I need its schemes to be defined. I have tried building the project from the command line but I get the message ** BUILD FAILED **. As of right now I have the code open the xcode project (the only way that I've found to get the schemes defined) and then sleep for 30 second while I wait for xcode to work its magic. My question is how can I either simulate opening xcode or otherwise define the scheme from the command line.
Thanks in advance for any help.
This is a completely fair question given that Xcode schemes are somewhat less than thoroughly documented and schemes have this feeling of being somewhat magical until you see how they hook into the build process as a whole.
Based on the workarounds you are seeking, it sounds as though you need to promote a scheme to being "Shared" so that automated tools (or other developers) do not have to first open your project and wait for Xcode to auto-generate the default scheme. This is an entirely normal 'ask' from developers trying to make their Xcode projects work with Continuous Integration systems or with other command line tools acting on an Xcode 4 or Xcode 5 project. The great news is that there are Xcode-native ways to configure your project without having to resort to messy or error prone workarounds.
TL;DR Version:
The default Xcode behavior for schemes is to treat them as a developer-specific setting and not share with other developers or tools. We need to promote your project's scheme to being 'Shared' and commit those changes to your version control system:
Start with a clean checkout of your project.
Navigate Xcode's Menus: Product > Scheme > Manage Schemes... menu option
Uncheck 'Autocreate Schemes' in the upper left corner of the scheme sheet,
Check the 'Shared' checkbox next to the scheme that should be made available to all developer users and build systems.
Finally commit all project changes back to your version control system.
This will make a single Scheme shared across all developer using this project, regardless of OS X username and make it such that unattended builds via xcodebuild or the build tool of choice will have a scheme to work with.
...And now, on to the the longer answer for the curious
First a bit of background before we dive into your direct questions:
Target: The app, static library, bundle, or more generally the 'product' constructed from the source code, assets, plists, build settings, and other files contained within the project. This 'product' is generated when a build operation is invoked either via Xcode's "Run" button or via the command line tool xcodebuild
Build Configuration: A named set of build settings that can be identified by a human-readable label. By default, all Xcode projects start with a "Debug" configuration that generates build targets with the greatest amount of transparency aiding developers in debugging their applications and a "Release" configuration that strips the resulting build of this diagnostic information and optimizes the build to reduce its size. Some developers elect to create additional configurations based on their team's needs: "Ad-Hoc" might be created so that the Signing Identity and Provisioning Profile settings can be changed for code signing the app for installation via an Ad-Hoc provisioning profile. "AppStore" or "Distribution" are other common custom Build Configurations one might see in other projects.
Action: A set of related activities supporting different phases involved in the development, diagnosis, and testing of a product. As of the time of writing there are six actions: "Build", "Run", "Test", "Profile", "Analyze", and "Archive". As a developer the two you will most frequently use are "Build" and "Run".
Build Scheme: An Xcode 4 invention for managing project build target dependencies, build parallelization options, for a specified Build Target. Each Scheme allows a developer to select exactly one Build Configuration (ex. "Debug" or "Release") for each Action ("Build", "Run", etc.) of a project's lifecycle as well as define other behaviors or options associated with that specific Action. For example, the "Profile" action in a scheme allows the developer to select which diagnostic instrument will be loaded by default when Profiling code in Instruments.app.
With these definitions in mind, lets get back to your questions:
How can I either simulate opening xcode or otherwise define the scheme from the command line?
Very simply: You don't need to do either, there is an Xcode-native mechanism for making schemes available and we just need to do some minor scheme reconfiguration to get you up and running then commit those changes to version control (I'm going to refer to this as 'SCM' for the rest of this answer).
The behavior you are facing is Xcode's default project behavior when it comes to persisting project settings. By default, many things are considered developer-specific settings and reside in a set of files mapping to the specific username of the account that opened the Xcode project itself (more on this in a moment). The policy governing these settings could be distilled down to the rule that Xcode settings were considered 'developer private until explicitly promoted to shared'. Although this was present in versions of Xcode prior to Xcode 4, it wasn't until the introduction of Schemes as the primary vehicle for invoking builds that this approach caused development teams and their Continuous Integration systems problems.
Schemes came along and consolidated a great number of settings screens from early versions of Xcode into a single editor window where a developer could take a look at the highest-level settings for each of the different Action phases of the app:
When running the "Build" action, one could define which targets need to get constructed, or if Xcode should try and identify build dependencies on its own.
For a "Run" action, select which Build Configuration should be used as well as which Debugger to use.
For a "Test" action, select which Build Configuration should be used as well as which Test Classes and Test Data Bundles should be used to test application behavior.
...etc...There are lots of other high-level settings but I'm going to leave exploring them as an exercise for the reader...or an opportunity to ask another SO question!
In each case, these settings cause something of a cascade effect -- Selecting a "Debug" configuration keeps as much diagnostic data in the app as possible to aid developers in tracing the source of problems, this in turn would invoke the "Debug" specific Build Settings as configured in the Build Target itself that may also run "Debug" specific scripts or enable "Debug" specific settings.
Naturally, these selections needed to live somewhere so that they could be persisted between Development sessions or on the rare occasion that Xcode decides to crash. The behavior of "Developer private until promoted" reigned supreme and these Scheme settings were persisted in the "xcuserdata" folder within the .xcodeproj file itself -- This still holds true for those projects that reside as a part of an .xcworkspace.
You can see this for yourself in your own project. First, ensure you are working with a clean version of your code, then open the Xcode project or workspace to ensure that your personal version of the default scheme is available when we walk through your project file:
Switch from Xcode to Finder, then navigate to your project's checkout directory.
Right-click on the .xcodeproj file for your project and select 'Show Package Contents'. If you use a workspace, still select the .xcodeproj that contains your project files, and not the .xcworkspace itself
Navigate into "xcuserdata".
Depending on the number of developers that have been involved with this project or the number of different machines with different usernames that have committed against this project, it is distinctly possible to have more than one .xcuserdatad folder.
Select the folder that matches your OS X username. For me, my OS X username is 'bmusial' so I would select the 'bmusial.xcuserdatad' folder.
Navigate into 'xcschemes' folder.
Observe that you have two files: "[TARGET NAME].xcscheme" and "xcschemenamagement.plist" that contains information about the order of schemes and if schemes should be auto-generated or not.
Ah ha! Schemes are treated as developer-private data and are auto-generated on the first launch of the project!
This realization starts to get at the core of what we need to do -- migrate this scheme out of the developer-specific xcuserdata folder into something shared among all developers, disable auto-scheme-generation to prevent others from falling into the trap in the future, and commit those changes back to your SCM. Switch back to Xcode, let's reconfigure a few things:
Navigate Xcode's Menus: Product > Scheme > Manage Schemes... menu option
Uncheck 'Autocreate Schemes' in the upper left corner of the scheme sheet,
Check the 'Shared' checkbox next to the scheme that should be made available to all developer users and build systems.
Switch back to your Finder window and go up a two levels to get back to the contents of the .xcodeproj folder (the one that contains a 'xcuserdata' folder). Notice that you now have a 'xcshareddata' folder. This folder contains a 'xcschemes' folder that contains the scheme we just shared and the .xcscheme in our own xcuserdata folder is now gone. We have just promoted your private Scheme as a shared, public scheme that will be available to all developers and tools, even those that have never launched the Xcode project directly.
Commit all of the changes we've made (there will be some new folders and files!) back to your SCM so that everyone receives the same configuration changes when the next time they update their source code!
The next time you run phonegap it will reset your checkout as your indicated but because you have a scheme committed it will have build actions it can work with.
Give this a shot and let us know how things go and if you run into any followup questions or problems along the way.
You may also find the ruby gem xcodeproj useful. It can create schemes without having to open xcode.
You can read more about it here.
For phonegap/cordova, save the share_schemes.rb script in a scripts directory in the cordova project.
#!/usr/bin/env ruby
# share_schemes.rb
require 'xcodeproj'
xcproj = Xcodeproj::Project.open("platforms/ios/MyProject.xcodeproj")
xcproj.recreate_user_schemes
xcproj.save
Then add a hook to run it in your config.xml.
<platform name="ios">
<hook type="after_platform_add" src="scripts/share_schemes.rb" />
</platform>
Now you don't have to open xcode to make changes, or check in any changes in your platforms folder. Every time you add the ios platform, your scheme will be created by this script.

Xcode 5 and shared Schemes

I'm having an annoying issue with Xcode 5 and the shared schemes.
In my team (we're just two, but hey! it's still a team) we're sharing some Schemes to run the app with different configurations (Debug, QA, Release, ...)
Well now Xcode 5 seems to change the BlueprintIdentifier setting of each scheme based, from what I can understand, on the machine of the developer. And of course the change get's noticed bit git and we either have to checkout them, or commit. Both solution don't make sense...
Any idea on how to fix it? I did a google search but found nothing for Xcode 5...
I don't want to give up on those shared configurations! A solution I can came up with to keep having the shared Schemes tracked is unshared them, copy them in a dedicated test folder, then using a strategy like the one used for the Pods to copy those schemes locally, where Xcode can mess up with them without us noticing. Something like a schemes script that reads a Schemefile file which lists the schemes to copy from the Schemes/ folder, and a Schemefile.lock that is updated every time there's some change and that is read by the Xcode build process in order to make sure everything is up-to-date.
This seems a lot of work for such a tiny thing, so before jumping head down into it I would like to know if anyone has some better solution to propose.
Cheers :)
First, I'm assuming you're using CocoaPods because you mentioned it in your question.
I've had this issue for a while and just recently discovered that some of my shared schemes are not getting their BlueprintIdentifier changed with the others. Upon investigation, I noticed that the untouched schemes had a Pods target under builds that was missing–as in it was listed in red like this: Pods (missing). My theory is that pod install and various Xcode events (i.e. crashing, launching, etc) were causing the scheme to change its BlueprintIdentifiers because they had a reference to a Pod target in the scheme build settings.
I removed the Pods reference in all of my schemes and have not been able to reproduce the BlueprintIdentifierchange since. My target that depends on Pods has libPods.a specified under Link Binary With Libraries in Build Phases and set to required, so it still gets compiled before the main target.
Note that switching between branches with this change and without this change might still produce modifications to the scheme files.
#Wes's answer didn't specifically fix this issue for me, but it did lead me to the right place. The solution for me was to turn "Find Implicit Dependencies" from "off" (unchecked) to "on" (checked) from the Edit Scheme screen for the Scheme that keeps changing.

xcodebuild says does not contain scheme

I have a curios issue.
I have a project that I've worked on and always built from the XCode IDE, and it worked fine. Now I'm setting up Bamboo to build the project and as such am building it from the command line.
The issue is, if I check my code out of GIT and then use xcodebuild to build it it says that the scheme cannot be found, but if I open the project, it builds and if I then try to build it again from the command line with the same command, it works.
What magic is XCode doing when I open the project or am I doing something dumb, maybe excluding a file in my .gitignore that I shouldn't?
You are definitely on the right track with respect to the .xcscheme file -- I had this problem appear while setting up my own projects!
For posterity, or at least anyone getting here from a search, here are two versions of things -- the "I'm busy, so just the facts please" version and a more involved discussion and rationale. Both of these versions assume you are trying to build from a Workspace file; if you aren't then my apologies as this mostly applicable to workspace-based projects.
Condensed 'Fix-it' Version
The root cause is that the default behavior of Schemes is to keep schemes 'private' until they are specifically marked as shared. In the case of a command-line initiated build, the Xcode UI never runs and the xcoderun tool doesn't have its own cache of Schemes to work with. The goal is to generate, share, and commit the scheme you want Bamboo to run:
On a clean working copy of the code, open your Project's workspace.
Choose Scheme > Manage Schemes... from the Product Menu.
The list of Schemes defined for the project appears.
Locate the Scheme Bamboo is trying to run
Ensure the 'Shared' box is checked for that scheme and that the 'Container' setting is set to the Workspace and not the project file itself.
Click 'OK' to dismiss the Manage Schemes sheet.
A new .xcscheme file has been created in your project at WorkspaceName.xcworkspace/xcshareddata/xcschemes.
Commit this file to your repository and run a Bamboo build.
Deeper Discussion and Rationale
Xcode 4 introduced Workspaces and Schemes as a way to help try and tame some of the chaos that is inherent to dealing with the mechanics of wiring related Xcode projects, build targets, and build configurations together. The workspace itself has its own set of configuration data that describes each of the smaller 'boxes' of data it contains and acts as a skeleton for attaching .xcodeproj files and a set of shared configuration data that gets mirrored to each developer machine or CI system. This is both the power and pitfall of Workspaces -- there are 1) lots of ways in which one can get things configured 100% correctly, but put into the wrong container or 2) put into the correct container, but configured improperly thus rendering data inaccessible by other parts of the system!
The default behavior of Xcode 4 schemes is to automatically generate new schemes as projects are added to the Workspace file. Those of you that have added several .xcodeproj files may have noticed that your scheme list quickly becomes unruly especially as project files are added, then removed, and then readded to the same workspace. All schemes, autogenerated or manually created, default to being 'private' schemes visible only to the current user even when .xcuserdata files are committed with the project's data and configuration. This is the root cause of that cryptic build error Bamboo reports from xcodebuild -- Because Bamboo operates the build through the command line and not the Xcode UI, it doesn't have an opportunity for Schemes to get automatically generated and relies only on those that are defined in the workspace itself. Assuming you've configured Bamboo to build from a workspace using a command like this:
xcodebuild -workspace MyWorkspace.xcworkspace -scheme MyApplication -configuration Debug
xcodebuild goes looking for file <'scheme' Parameter Value>.xcscheme existing at <'workspace' Parameter Value>/xcshareddata/xcschemes.
Obviously there are bunches of ways in which one could configure both Bamboo and a workspace, so keep in mind that your unique configuration may not map 100% to what is presented here. The key takeaways:
Certain automated tasks the Xcode UI magically takes care of are not available via the Xcodebuild CLI.
You can attach scheme and build configuration data to many places in the 'container hierarchy' -- Make sure your data winds up in the right container (Workspace, Project, and/or Build Target)
Consider where in the container hierarchy the xcodebuild tool may be looking for configuration data; a great indicator of where it will start looking is based on the use of '-workspace' or '-project' arguments.
The 'Shared' box is already checked...now what?
I encountered this same issue on my own Bamboo instance; it turned out that the scheme that was committed in my repository was outdated and the latest version of the command line tools wasn't handling it gracefully. Since this existed previously, I took a look through the settings to make sure there wasn't anything glaringly custom about the scheme, deleted and recreated the scheme ensuring that I marked it as 'Shared', and recommitting the new .xcscheme file to the repository.
If everything looks good and rebuilding it doesn't solve the issue, double check that container setting -- it is really easy to get that scheme attached to the wrong container in the hierarchy!
Debug the issue like this:
xcodebuild -list
or if you are using a workspace (e.g. with pods)
xcodebuild -workspace MyProject.xcworkspace -list
If you scheme is not listed fix like so:
Most of the answers would suggest you to make your scheme shared using Xcode, then commit changes to repo. That works, of course, but only if you have access to source code and have rights to commit changes, and couple of other assumptions.
But there's a number of "what ifs" to consider
What if you just can't modify the Xcode project for some reason?
What if you create a new scheme automatically on CI server? This actually happens quite often. If you use test automation framework, like Calabash, you'll normally end up duplicating an existing target, which automatically duplicates a scheme as well, and the new scheme is not shared, even if the original scheme was.
Ruby & xcodeproj gem
I would recommend using xcodeproj Ruby gem.
This is a really cool open source tool that can help you to automate tons of Xcode-related tasks.
Btw, this is the gem used by CocoaPods to mess around with your Xcode projects and workspaces.
So install it
sudo gem install xcodeproj
Then write a simple Ruby script to re-share all the schemes, the gem has recreate_user_schemes method for that purpose
#!/usr/bin/env ruby
require 'xcodeproj'
xcproj = Xcodeproj::Project.open("MyProject.xcodeproj")
xcproj.recreate_user_schemes
xcproj.save
It doesn't just copy scheme files form user's folder to xcshareddata/xcschemes, it also creates those files first by parsing the pbxproj file.
Ok I know its 2 minutes later but I found another stack overflow that says the scheme has to be set to shared... Where does Xcode 4 store Scheme Data?
One common reason for the scheme to be missing is forgetting to push the commits to the origin. If you get a missing scheme message, you should first verify the scheme is shared, then verify you have committed the changes AND pushed them to the origin server.
I had this error while implementing CI.The Question above is identical to my problems except I am using Gitlab's own CI tool.You can check if there is any such file in Bamboo.
I solved it by making some changes to gitlab-ci.yml file.
After you hav made your scheme availabe by sharing. In Xcode Go to Products>Scheme>Manage Scheme and check share to share.
Changes
Set absolute path everywhere.
eg.xcodebuild clean archive -archivePath /path/to/your/project/build/testDemo -scheme testDemo | xcpretty
here you need to change /path/to/your/project/ with your path and testDemo with your project name.
I faced this issue and even if some of the answers here actually provide the solution, I didn't find it very clear. So I will just add one more. In a nutshell how to share a schema from excode.
Navigate to Product > Scheme > Manage Schemes
You will then be shown a list of schemes, with each denoted as being shared or not. Just check the ones that you want to share (it may be different ones for dev and prod builds)
Images taken from this article https://developer.nevercode.io/docs/sharing-ios-project-schemes
Got the same problem but during building with xcode as subproject of main one. Built subproject in xcode standalone - after that this error disappeared.
I want to add solution for my case related to this thread. This one is for you who clone existing project, with all the schemes you need are already being shared:
, with fastlane lanes correctly display all your lanes including all your schemes:
, but fastlane gym only show main schemes (not dev and test schemes):
The solution is to uncheck the shared option for schemes that not listed by fastlane gym and then check it again. It will generates .xcscheme for the schemes:
Now, if you check with fastlane gym, all the schemes will be listed:
Then you should commit those .xcshemes file to the repository, so other developer who clone the project will get the files.
For anyone with Xcode 11.4 trying to find "Shared" button on scheme, it's now moved into the individual scheme.
Select the scheme you want
Press "Edit"
Check the "Shared" box

Will PackageMaker work for me?

I have an application to be distributed.
Pretty much everything is self-contained within one bundle so I could just put the bundle in a DMG for the first version-- however this won't work for upgrades, so I figured I'd best work out the process ahead of time.
I've been looking at PackageMaker and it seems straightforward enough. However I have a couple of very specific requirements which may cause problems, and it wasn't apparent from reading the PM docs as to whether this is even possible:
First of all, I want the installer to automatically choose between installing two different versions of the app, depending upon whether a previous version of the application has previously been installed.
Secondly, I don't want to just overwrite the previous application's bundle, I just want the installer to overwrite files within it and add new ones -- in other words, I want certain files within the application bundle to remain from the previous version.
Is this something I could achieve with PackageMaker, or should I look for a different approach?
You can do a lot with an installer package, for example run a shell script which in turn allows you to do everything.
I get the feeling that you're doing it not the Mac way. It's uncommon to store data in the application bundle, normally such stuff goes to ~/Library/Application Support/. I presume that those two different applications are quite similar, why not just set a preference?
Personally, I feel that today a .zip is the best way to distribute your apps. It gets automatically decompressed and shows up with your application icon in the downloads stack. (Only if you get it from the internet, of course.)
For updating look at the sparkle framework. The forst install is just copy the application bundle from the internet and te user then just asks to do update and does not have to leave your application to do anything

Resources