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:

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

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:

[cc lang=”xml”]


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
[/cc]

___FILEBASENAME___.h:
[cc lang=”objc”]
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//

#import

@interface ___FILEBASENAMEASIDENTIFIER___ : UIViewController

@end
[/cc]

___FILEBASENAME___.m:
[cc lang=”objc”]
//
// ___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
[/cc]

Cela suffit à créer nos fichiers propres et bien formatés, testez, vous verrez!
Fenêtre de création de fichiers

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:

[cc lang=”xml”]



1536
11G63b
2844
1138.51
569.00
com.apple.InterfaceBuilder.IBCocoaTouchPlugin
1930


IBProxyObject
IBUIView


com.apple.InterfaceBuilder.IBCocoaTouchPlugin

PluginDependencyRecalculationVersion


IBFilesOwner
IBCocoaTouchFramework

IBFirstResponder
IBCocoaTouchFramework


274
{320, 548}



3
MQA
2

IBUISimulatedFreeformSizeMetricsSentinel
Freeform

IBCocoaTouchFramework



view


3



0



1


-1


File’s Owner

-2




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





3


___FILEBASENAMEASIDENTIFIER___
UIViewController
IBProjectSource
./Classes/___FILEBASENAMEASIDENTIFIER___.h


0
IBCocoaTouchFramework
YES
3
YES
1930


[/cc]

Voici donc l’arborescence finale de notre template:
Architecture finale du 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!


Next up


Leave a Reply

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