XCode Templates — Part 2

Rakesh Chander
5 min readOct 24, 2020

XCode 11 & above

In this story, we’ll cover up Project Templates. For basics & File Templates please go through last story XCode Templates — Part 1.

Refer Sample Templates in my Git Repo — xcode-templates

While setting up projects, we do most of repeatitive steps like —

  • Project without StoryBoards
  • Localization SetUp
  • Multi Schemes — DEV, UAT, PROD
  • Crashlytics SetUp
  • Git Ignore SetUp
  • CI SetUp
  • CocoaPod SetUp
  • Unit & UI Tests SetUp
  • Theme SetUp
  • Configurations

All this above can be put inside a Project Template and next time when you will create New Project, all will be ready to use :-) after few clicks only.

So, lets start setting Project Template. For structure & basic templateinfo.plist refer last story.

Below are additional main entries in Project TemplateInfo.plist —

  • Nodes
  • Definitions
  • Project
  • Target

Nodes

Set of all files which you want to add into your project can be grouped into folders and placed in .xctemplate folder.

As you see, I’ve put AppDelegate, Configuration, Podfile, GitIgnore File, YML files, CI Helper Scripts & XCSchemes (for different environments like DEV, UAT, PROD etc) in different folders.

Now, to make sure these files are part of your template, you need to add them to Nodes entry in plist. Key Name can be anything unique while Value must be the exact path of that file. Refer below —

In Nodes section we can declare Key-Values which we want to be available in Info.plist. Entries like Custom Fonts etc.

Definitions —

For all entries which has been added into project should be added to target as well. This is the section where we define target mapping. For files which you don’t want to be added to target — don’t specify any Target values inside TargetIndices. By default all files mentioned in Nodes will be added to Target. Refer below —

Project —

Multiple Configurations or Project settings can be defined in this section. Refer below —

We need to add corresponding xcscheme files as well. If you see above, I have declared respective XCSchemes in both Nodes & Definitions section. You can create a new xcscheme file and paste below code into that to use in any template —

<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1110"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D12757D023A389F400623DCB"
BuildableName = "___PROJECTNAME___.app"
BlueprintName = "___PROJECTNAME___"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "DEV"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D37E482E23FF750E00102B51"
BuildableName = "___PROJECTNAME___.app"
BlueprintName = "___PROJECTNAME___"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<TestPlans>
<TestPlanReference
reference = "container:___PROJECTNAME___/TestPlan/LocalizationPlan_Unit.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D38153702404DBC100FA03CF"
BuildableName = "___PROJECTNAME___Tests.xctest"
BlueprintName = "___PROJECTNAME___Tests"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "DEV"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D12757D023A389F400623DCB"
BuildableName = "___PROJECTNAME___.app"
BlueprintName = "___PROJECTNAME___"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "DEV"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D12757D023A389F400623DCB"
BuildableName = "___PROJECTNAME___.app"
BlueprintName = "___PROJECTNAME___"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "DEV">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "DEV"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

In this xcscheme file, you can define any setting like Test Cases are enabled, Code Coverage etc. Refer below section —

<TestAction
buildConfiguration = "DEV"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D37E482E23FF750E00102B51"
BuildableName = "___PROJECTNAME___.app"
BlueprintName = "___PROJECTNAME___"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<TestPlans>
<TestPlanReference
reference = "container:___PROJECTNAME___/TestPlan/LocalizationPlan.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D38153702404DBC100FA03CF"
BuildableName = "___PROJECTNAME___Tests.xctest"
BlueprintName = "___PROJECTNAME___Tests"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D381537B2404DBC200FA03CF"
BuildableName = "___PROJECTNAME___UITests.xctest"
BlueprintName = "___PROJECTNAME___UITests"
ReferencedContainer = "container:___PROJECTNAME___.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>

Target —

This section contains info about Target type of this project & target specific settings like —

  • Scheme Configurations
  • Build Phases

A single project can have multiple targets, so its an array of Target Objects. For our needs we need to declare below items in each Target Object.

Target Identifier

  • App — com.apple.dt.cocoaTouchApplicationTarget
  • Framework — com.apple.dt.iosLibraryOrFrameworkTarget

Shared Settings — Target Device Family & Asset Catalog can be defined here

BuildPhases — For all entries which we make in any project’s Build Phase, we can declare here to be available automatically when project is setup using this template. Most frequently used Build Phases are SwiftLint, Crashlytics etc.

Configurations — For all declared Configurations (UAT, DEV, PROD etc) we can have different bundle ids, app name, testability status etc. All those settings can be defined in this section.

Refer below —

So, Thats all Folks for now!! Hope it’ll be useful for you all to reduce duplication in work and optimising best practices.

--

--

Rakesh Chander

I believe in modular development practices for better reusability, coding practices, robustness & scaling — inline with automation.