
Exploring our iOS toolbox: sharing our codebase with the world

20. Sep 2021
iOSEvery time I set up a new project, it took a full working day to get it ready for development. Until I discovered templates. Xcode templates have been around since the days of Xcode 4, but I only recently learned about them and they have completely changed the way I look at project creation. Try them with me and new projects will suddenly be that much more consistent and maintainable.
In the previous article about Xcode projet templates, I walked you through running this tool step by step and pointed out its benefits. This time I will introduce you to the additional capabilities of Xcode project templates, where when generating the project we will have a podfile ready with a pre-filled configuration. What that means in practice is that, after we generate new project, we enter the project via the console, we call
pod install
and the project will have pods configured. This will significantly reduce the configuration of the project and the style of the podfiles will be uniform when it comes to more complex podfiles.
The first thing to do is to create a new template, similar to last time, in the project templates folder with the .xctemplate extension
Here we will first create a Podfile file and insert the pods we want to import into each new project.
platform :ios, '___VARIABLE_VERSION___'
target '___PACKAGENAME___' do
use_frameworks!
pod 'SwiftGen'
pod 'SwiftLint'
end
Note the variables used __VARIABLE_VERSION__ which sets the minimum project version below and __PACKAGENAME__ which sets the project name. When creating a project, __VARIABLE_VERSION__ will be the input parameter and __PACKAGENAME__ will be taken from the project name.
Then it is necessary to configure the TemplateInfo.plist file which represents the configuration of the template
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kind</key> <!--States the template type-->
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<!--States if the template is a standalone template or a part
of template used in a diffrent template. In this case it will
be hidden from the template wizzard-->
<key>Concrete</key>
<true/>
<key>Identifier</key> <!--Template ID in the "template space"-->
<string>com.goodrequest.CocoaPods</string>
<key>Definitions</key> <!--Template variables-->
<dict>
<key>../Podfile</key>
<dict>
<key>Path</key>
<string>Podfile</string>
<key>Group</key>
<string>Resources</string>
</dict>
</dict>
<key>Options</key> <!--"USER INPUT" options-->
<array>
<dict>
<key>Identifier</key> <!--Option ID-->
<string>cocoapods</string>
<key>Name</key> <!--Field title-->
<string>CocoaPods</string>
<key>Description</key> <!--Field tooltip-->
<string>Integrate CocoaPods template</string>
<key>SortOrder</key> <!--Sorting priority-->
<integer>250</integer>
<key>Type</key> <!--Field type-->
<string>checkbox</string>
<key>Default</key> <!--Default value-->
<string>false</string>
<key>Units</key> <!--Field outcome-->
<dict>
<key>true</key> <!--True branch outcome-->
<array>
<dict>
<key>Nodes</key> <!--Files to include if True-->
<array>
<string>../Podfile</string>
</array>
</dict>
</array>
</dict>
</dict>
<dict>
<key>Identifier</key> <!--Option ID-->
<string>VERSION</string>
<key>Required</key> <!--Required to fill in the field-->
<true/>
<key>Name</key> <!--Field title-->
<string>iOS Min. Version</string>
<key>Description</key> <!--Field tooltip-->
<string>Minimum supported iOS version</string>
<key>Type</key> <!--Field type-->
<string>text</string>
<key>Default</key> <!--Default value-->
<string>13.0</string>
<key>NotPersisted</key> <!--Remember last filled in choice-->
<true/>
</dict>
</array>
</dict>
</plist>
Here it is essential to note the identifier variable
However, we now want to insert this template into our project template from the previous instructions.
The result will be as follows.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kind</key>
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<key>Identifier</key>
<string>com.apple.dt.unit.singleViewApplication</string>
<key>Ancestors</key>
<array>
<string>com.goodrequest.cocoaTouchApplicationBase</string>
<string>com.goodrequest.CocoaPods</string> <!--ID of the Pod template-->
</array>
<key>Concrete</key>
<true/>
<key>Definitions</key>
<dict>
<key>Application/AppDelegate.swift</key>
<dict>
<key>Path</key>
<string>Application/AppDelegate.swift</string>
<key>Group</key>
<array>
<string>Application</string>
</array>
</dict>
<key>Extensions/Foundation/String+Extensions.swift</key>
<dict>
<key>Path</key>
<string>Extensions/Foundation/String+Extensions.swift</string>
<key>Group</key>
<array>
<string>Extensions</string>
<string>Foundation</string>
</array>
</dict>
<key>Extensions/UIKit/UIView+Extensions.swift</key>
<dict>
<key>Path</key>
<string>Extensions/UIKit/UIView+Extensions.swift</string>
<key>Group</key>
<array>
<string>Extensions</string>
<string>UIKit</string>
</array>
</dict>
<key>Helpers/TypeAliases.swift</key>
<dict>
<key>Path</key>
<string>Helpers/TypeAliases.swift</string>
<key>Group</key>
<string>Helpers</string>
</dict>
<key>Models/Error/AppError.swift</key>
<dict>
<key>Path</key>
<string>Models/Error/AppError.swift</string>
<key>Group</key>
<array>
<string>Models</string>
<string>Error</string>
</array>
</dict>
<key>Models/Response/SampleResponse.swift</key>
<dict>
<key>Path</key>
<string>Models/Response/SampleResponse.swift</string>
<key>Group</key>
<array>
<string>Models</string>
<string>Response</string>
</array>
</dict>
<key>Models/Request/SampleRequest.swift</key>
<dict>
<key>Path</key>
<string>Models/Request/SampleRequest.swift</string>
<key>Group</key>
<array>
<string>Models</string>
<string>Request</string>
</array>
</dict>
<key>Managers/Dependency/DependencyContainer.swift</key>
<dict>
<key>Path</key>
<string>Managers/Dependency/DependencyContainer.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Dependency</string>
</array>
</dict>
<key>Managers/Request/RequestManager.swift</key>
<dict>
<key>Path</key>
<string>Managers/Request/RequestManager.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Request</string>
</array>
</dict>
<key>Managers/Request/RequestManagerType.swift</key>
<dict>
<key>Path</key>
<string>Managers/Request/RequestManagerType.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Request</string>
</array>
</dict>
<key>Managers/Request/Endpoint.swift</key>
<dict>
<key>Path</key>
<string>Managers/Request/Endpoint.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Request</string>
</array>
</dict>
<key>Coordinators/Coordinator.swift</key>
<dict>
<key>Path</key>
<string>Coordinators/Coordinator.swift</string>
<key>Group</key>
<string>Coordinators</string>
</dict>
<key>Coordinators/AppCoordinator.swift</key>
<dict>
<key>Path</key>
<string>Coordinators/AppCoordinator.swift</string>
<key>Group</key>
<string>Coordinators</string>
</dict>
<key>Screens/SampleController/SampleViewController.swift</key>
<dict>
<key>Path</key>
<string>Screens/SampleController/SampleViewController.swift</string>
<key>Group</key>
<array>
<string>Screens</string>
<string>SampleController</string>
</array>
</dict>
<key>Screens/BaseViewController.swift</key>
<dict>
<key>Path</key>
<string>Screens/BaseViewController.swift</string>
<key>Group</key>
<array>
<string>Screens</string>
</array>
</dict>
<key>Views/SampleView/SampleView.swift</key>
<dict>
<key>Path</key>
<string>Views/SampleView/SampleView.swift</string>
<key>Group</key>
<array>
<string>Views</string>
<string>SampleView</string>
</array>
</dict>
<key>Resources/Assets.xcassets</key>
<dict>
<key>Path</key>
<string>Resources/Assets.xcassets</string>
<key>Group</key>
<array>
<string>Resources</string>
</array>
</dict>
<key>Resources/Colors.xcassets</key>
<dict>
<key>Path</key>
<string>Resources/Colors.xcassets</string>
<key>Group</key>
<array>
<string>Resources</string>
</array>
</dict>
<key>Resources/SwiftGen/swiftgen.yml</key>
<dict>
<key>Path</key>
<string>Resources/SwiftGen/swiftgen.yml</string>
<key>Group</key>
<array>
<string>Resources</string>
<string>SwiftGen</string>
</array>
</dict>
</dict>
<key>Nodes</key>
<array>
<string>Application/AppDelegate.swift</string>
<string>Extensions/UIKit/UIView+Extensions.swift</string>
<string>Extensions/Foundation/String+Extensions.swift</string>
<string>Helpers/TypeAliases.swift</string>
<string>Models/Error/AppError.swift</string>
<string>Models/Response/SampleResponse.swift</string>
<string>Models/Request/SampleRequest.swift</string>
<string>Managers/Dependency/DependencyContainer.swift</string>
<string>Managers/Request/RequestManager.swift</string>
<string>Managers/Request/RequestManagerType.swift</string>
<string>Managers/Request/Endpoint.swift</string>
<string>Coordinators/Coordinator.swift</string>
<string>Coordinators/AppCoordinator.swift</string>
<string>Screens/SampleController/SampleViewController.swift</string>
<string>Screens/BaseViewController.swift</string>
<string>Views/SampleView/SampleView.swift</string>
<string>Resources/SwiftGen/swiftgen.yml</string>
<string>Resources/Colors.xcassets</string>
<string>Resources/Assets.xcassets</string>
<string>Info.plist:UIMainStoryboardFile</string>
<string>Info.plist:UIApplicationSceneManifest:UISceneStoryboardFile</string>
</array>
</dict>
</plist>
The only way these files differ is the ancestors key where we add the Identifier from the template we just created.
<key>Ancestors</key>
<array>
<string>com.goodrequest.cocoaTouchApplicationBase</string>
<string>com.goodrequest.CocoaPods</string> <!--ID of the Pod template-->
</array>
The only way these files differ is the ancestors key where we add the Identifier from the template we just created.