Création de templates personnalisés pour Xcode 4

Les actions répétitives font parties des choses les plus agaçantes pour un développeur, et c’est pourquoi nous cherchons toujours à automatiser au maximum ces petites choses que l’on doit systématiquement faire et qui nous usent.

La création de classes et plus particulièrement de classes héritants de UIViewController dans Xcode a toujours été pour moi source de frustration, et ce pour plusieurs raisons:
– je ne supporte pas le constructeur par défaut initWithNibName:bundle: qui demande à tout va le nom de fichier du XIB alors qu’une fois pourrait suffire
– les templates de base n’indentent pas le code à ma façon
– les pragma mark ne sont pas présentent alors qu’elles sont très pratiques
– je dois toujours supprimer les commentaires d’Apple sur ce qui doit être fait ou non dans les méthodes

Voici donc la méthode pour gagner deux minutes à chaque création d’un view controller: les templates personnalisés.

Pour commencer

La meilleure façon de commencer est peu être de comprendre comment sont faits les templates déjà présents dans Xcode. Depuis Xcode 4.3, ils se trouvent sur votre Mac à cet endroit:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates

Vous pouvez les ouvrir, regarder comment ils sont faits, mais je vous déconseille de les modifier pour la simple et bonne raison qu’une erreur dans un de ces templates peut ne plus le faire fonctionner, voir faire crasher Xcode lorsque vous essayerez de les utiliser.

Nous allons plutôt créer nos propres templates dans le répertoire prévu à cet effet, il n’existe d’ailleurs probablement pas et c’est pourquoi nous allons le créer:

~/Library/Developer/Xcode/Templates

Une fois le dossier “Templates” créé, nous allons créer un sous-répertoire “File Templates” qui contiendra lui même un sous-dossier nommé comme bon vous semble pour vous identifier, par exemple “CC” soit:

~/Library/Developer/Xcode/Templates/Project Templates/CC/

Composition d’un template de fichier

Un template est à nouveau un répertoire dont l’extension est .xctemplate. Ce répertoire contient au moins deux fichiers:

– TemplateInfo.plist: il s’agit de la configuration du template

– TemplateIcon.icns: il s’agit de l’icone du template

À ces fichiers s’ajoutent ceux qui seront automatiquement créés lors de l’utilisation de notre template, par exemple:

– ___FILEBASENAME___.h: notre fichier interface

– ___FILEBASENAME___.m: notre fichier d’implémentation

Commençons donc par créer copier le fichier TemplateIcon.icns contenu dans n’importe quel template de base d’Xcode.

Créons ensuite les fichiers suivants:

Architecture du template de base

TemplateInfo.plist:

<!--?xml version="1.0" encoding="UTF-8"?-->

AllowedTypes

public.objective-c-source
public.objective-c-plus-plus-source

DefaultCompletionName
MyClass
Description
A custom UIViewController subclass, with implementation and header files.
Kind
Xcode.IDEKit.TextSubstitutionFileTemplateKind
MainTemplateFile
___FILEBASENAME___.m
Options

Description
The name of the class to create
Identifier
productName
Name
Class
NotPersisted
Required
Type
text

Platforms

com.apple.platform.iphoneos

SortOrder
1

___FILEBASENAME___.h:

//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//

#import

@interface ___FILEBASENAMEASIDENTIFIER___ : UIViewController

@end

___FILEBASENAME___.m:

//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//

#import "___FILEBASENAME___.h"

@interface ___FILEBASENAMEASIDENTIFIER___ ()

@end

@implementation ___FILEBASENAMEASIDENTIFIER___

#pragma mark - Constructor

- (id)init
{
    self = [super initWithNibName:@"___FILEBASENAMEASIDENTIFIER___" bundle:nil];
    if (self)
    {
        // Custom initialization
    }
    return self;
}

#pragma mark - View management

- (void)viewDidLoad
{
    [super viewDidLoad];
}

#pragma mark - Memory

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

@end
Fenêtre de création de fichiers

Cela suffit à créer nos fichiers propres et bien formatés, testez, vous verrez!

Le contenu du fichier TemplateInfo.plist

Ce fichier au format PLIST contient toute la configuration de notre template. Il est composé à partir des clés suivantes:
– AllowedTypes: Un tableau déclarant le ou les types possible
– DefaultCompletionName: Le nom qui sera affiché dans la fenêtre de création d’un nouveau fichier
– Description: La description du template qui sera affichée dans la fenêtre de création d’un nouveau fichier
– Kind: Apparement, on utilise toujours Xcode.IDEKit.TextSubstitutionFileTemplateKind, je n’ai pas plus d’informations là dessus pour le moment
– MainTemplateFile: le fichier principal qui sera créé par le template
– Platforms: la liste des plate-formes autorisées à utilisées le template (pour nous iOS uniquement)
– SortOrder: La position du template dans la fenêtre de création d’un nouveau fichier, utile lorsqu’on a plusieurs templates pour les ordonner
– Options: Contient notamment les informations sur les champs contenu dans la boite de dialogue pour créer un nouveau fichier

Le contenu des fichiers sources

Il existe là quelques macros que l’on peut utiliser afin qu’elles soient automatiquement remplacées à la création d’un nouveau fichier. Dans notre exemple, nous retrouvons les macros suivantes:
– ___FILENAME___: Le nom du fichier avec extension (.h ou .m)
– ___FILEBASENAMEASIDENTIFIER___: Le nom du fichier sans extension
– ___PROJECTNAME___: Le nom du projet
– ___FULLUSERNAME___: Le nom complet de l’utilisateur, dans mon cas “Cyril CHANDELIER”
– ___ORGANIZATIONNAME___: Le nom d’organisation réglé à la création du projet
– ___DATE___: La date du jour
– ___TIME___: L’heure courante
– ___YEAR___: L’année en 4 chiffres

Automatiser l’ajout d’un XIB

En testant tout ça, on se rend compte qu’il manque quelque chose: le XIB. Il serait intéressant d’en avoir un automatiquement créé et configuré. Pour cela, on va en créé un normalement, ajouter le nom de classe du File Owner, supprimer la status bar et mettre le layout en mode “Freeform”.

Maintenant, étudions le en l’ouvrant en mode code puis copions son code.

Au même niveau que les .h et .m, nous allons créer un fichier ___FILEBASENAME___.xib et y coller le code précedemment copié; il ne reste plus qu’à remplacer le nom de classe de test par des macros ___FILEBASENAMEASIDENTIFIER___ pour obtenir quelque chose comme ceci:

<!--?xml version="1.0" encoding="UTF-8"?-->

1536
11G63b
2844
1138.51
569.00
<object class="NSMutableDictionary" width="300" height="150"><string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string><string key="NS.object.0">1930</string></object>

IBProxyObject
IBUIView

com.apple.InterfaceBuilder.IBCocoaTouchPlugin

<object class="NSMutableDictionary" width="300" height="150"><string key="NS.key.0">PluginDependencyRecalculationVersion</string><integer value="1" key="NS.object.0"></integer></object>

<object id="372490531" class="IBProxyObject" width="300" height="150"><string key="IBProxiedObjectIdentifier">IBFilesOwner</string><string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string></object>
<object id="975951072" class="IBProxyObject" width="300" height="150"><string key="IBProxiedObjectIdentifier">IBFirstResponder</string><string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string></object>
<object id="191373211" class="IBUIView" width="300" height="150"><reference key="NSNextResponder"><int key="NSvFlags">274</int><string key="NSFrameSize">{320, 548}</string><reference key="NSSuperview"><reference key="NSWindow"><reference key="NSNextKeyView"><object class="NSColor" key="IBUIBackgroundColor"><int key="NSColorSpace">3</int><bytes key="NSWhite">MQA</bytes><object class="NSColorSpace" key="NSCustomColorSpace"><int key="NSID">2</int></object></object></reference></reference></reference></reference></object>
<object class="IBUISimulatedSizeMetrics" width="300" height="150"><string key="IBUISimulatedSizeMetricsClass">IBUISimulatedFreeformSizeMetricsSentinel</string><string key="IBUIDisplayName">Freeform</string></object>
IBCocoaTouchFramework

<object class="IBObjectContainer" width="300" height="150"><array class="NSMutableArray" key="connectionRecords"><object class="IBConnectionRecord"><object class="IBCocoaTouchOutletConnection" key="connection"><string key="label">view</string><reference key="source" ref="372490531"><reference key="destination" ref="191373211"></reference></reference></object></object></array></object>
3

<object class="IBMutableOrderedSet" width="300" height="150"><array key="orderedObjects"><object class="IBObjectRecord"><int key="objectID">0</int><array key="object" id="0"><reference key="children" ref="1000"><nil key="parent"></nil></reference></array></object></array></object>
<object class="IBObjectRecord" width="300" height="150"><int key="objectID">1</int><reference key="object" ref="191373211"><reference key="parent" ref="0"></reference></reference></object>
<object class="IBObjectRecord" width="300" height="150"><int key="objectID">-1</int><reference key="object" ref="372490531"><reference key="parent" ref="0"><string key="objectName">File's Owner</string></reference></reference></object>
<object class="IBObjectRecord" width="300" height="150"><int key="objectID">-2</int><reference key="object" ref="975951072"><reference key="parent" ref="0"></reference></reference></object>

___FILEBASENAMEASIDENTIFIER___
com.apple.InterfaceBuilder.IBCocoaTouchPlugin
UIResponder
com.apple.InterfaceBuilder.IBCocoaTouchPlugin
com.apple.InterfaceBuilder.IBCocoaTouchPlugin

3
<object class="IBClassDescriber" width="300" height="150"><array class="NSMutableArray" key="referencedPartialClassDescriptions"><object class="IBPartialClassDescription"><string key="className">___FILEBASENAMEASIDENTIFIER___</string><string key="superclassName">UIViewController</string><object class="IBClassDescriptionSource" key="sourceIdentifier"><string key="majorKey">IBProjectSource</string><string key="minorKey">./Classes/___FILEBASENAMEASIDENTIFIER___.h</string></object></object></array></object>

0
IBCocoaTouchFramework
YES
3
YES
1930
Architecture finale du template

Voici donc l’arborescence finale de notre template:

Aller plus loin

Cette technique qui nous permet de gagner deux minutes et un peu de joie de vivre en codant n’est qu’un début. Sur le même principe, il est possible de créer des templates pour automatiser à l’extrème certains processus du code iOS. La création d’un projet complet et configuré est un bon exemple qui peut faire gagner énormément de temps au lancement d’un nouveau projet, l’automatisation de la création de view controllers contenant une table view et faisant l’import par exemple de cellule custom pré-configurées en est un autre. Bref, de nombreuses possibilités s’offrent à vous pour gagner du temps et ainsi confirmer le fait que les développeurs sont des êtres extrêmement feignants!


Leave a Reply

Your email address will not be published. Required fields are marked *