Ce guide décrit les conventions de codage des équipes iOS du New York Times. Nous vous remercions pour vos commentaires dans les tickets, pull requests et tweets. Aussi, nous recrutons.
Merci à tous nos contributeurs et contributrices.
Voici quelques-uns des documents d'Apple qui nous ont servi à écrire ce guide. Si quelque chose n'est pas mentionné ici, il est probablement couvert en détail dans :
- Le langage de Programmation Objective-C
- Les Bases Fondamentales de Cocoa
- Conseils Généraux de Codage pour Cocoa
- Guide de Programmation pour App iOS
- Notation pointée
- Espacement
- Conditions
- Gestion des erreurs
- Méthodes
- Variables
- Nommage
- Commentaires
- Init & Dealloc
- Libellés
- Fonctions CGRect
- Constantes
- Types énumérés
- Masques de bits
- Propriétés privées
- Nommage d'image
- Booléens
- Singletons
- Imports
- Projet Xcode
La notation pointée doit toujours être utilisée pour lire ou modifier les propriétés. La notation crochée est préférable dans tous les autres cas.
Par exemple:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
Non pas:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
- L'indentation est de 4 espaces. N'indentez jamais avec des tabulations. Assurez-vous de régler cette préférence dans Xcode.
- L'accolade ouvrante des méthodes et structures de contrôle (
if
/else
/switch
/while
etc.) est toujours sur la même ligne que la déclaration et l'accolade fermante sur sa propre ligne.
Par exemple:
if (utilisateur.estHeureux) {
//Faire quelque chose
}
else {
//Faire quelque chose d'autre
}
- Les méthodes devraient être séparées par une ligne blanche pour améliorer la lisibilité et l'organisation. À l'intérieur des méthodes, des sauts de lignes devraient séparer les sections logiques, mais souvent ces dernières devraient être placées dans de nouvelles méthodes.
@synthesize
et@dynamic
devraient chacun être déclarés sur de nouvelles lignes dans l'implémentation.
Les instructions de condition doivent toujours utiliser des accolades même quand la condition pourrait être écrite sans (par ex. sur une seule ligne) pour éviter des erreurs. Une de ces erreurs serait d'ajouter une deuxième ligne et de penser qu'elle fait partie de la condition. Une autre, plus dangereuse peut arriver quand la ligne « intérieure » de la condition est commentée, et la prochaine ligne devient involontairement une partie de la condition. De plus, ce style est plus cohérent avec d'autres conditions et donc plus facile à détecter.
Par exemple:
if (!error) {
return success;
}
Non pas:
if (!error)
return success;
ou
if (!error) return success;
L'opérateur ternaire, ?
, doit seulement être utilisé s'il rend le code plus lisible ou propre. Il doit seulement évaluer une condition simple. Évaluer plusieurs conditions est généralement plus facile à comprendre avec une condition de type if, ou refactorisé avec des variables nommées.
Par exemple:
result = a > b ? x : y;
Non pas:
result = a > b ? x = c > d ? c : d : y;
Quand une méthode renvoie un paramètre d'erreur par référence, continuez l'exécution du programme sur la valeur returnée, et non sur la variable erreur.
Par exemple:
NSError *error;
if (![self FaireQuelqueChoseAvecErreur:&error]) {
// Gérer l'erreur
}
Non pas:
NSError *error;
[self FaireQuelqueChoseAvecErreur:&error];
if (error) {
// Gérer l'erreur
}
Certaines APIs d'Apple renvoient des valeurs de données poubeille pour un paramètre erreur (si non-NULL), donc continuer l'exécution du programme sur l'erreur peut créer des faux négatifs (et par la suite un plantage).
Pour la signature d'une méthode, il doit y avoir un espace après le scope (symbole -
ou +
). Et il doit y avoir un espace entre les différents segments (paramètres) de la méthode.
Par exemple:
- (void)setTextePourExemple:(NSString *)texte image:(UIImage *)image;
Les variables doivent être nommées de la façon la plus descriptive possible. Une variable d'une seule lettre doit être évitée sauf pour une boucle for
.
Les astérisques qui indiquent le pointeur sont plaçés avant le nom de la variable, par ex., NSString *text
et non NSString* text
ou NSString * text
, sauf dans le cas de constantes (NSString * const NYTConstantString
).
La définition des propriétés doivent être utilisées à la place des variables d'instance quand c'est possible. L'accès direct aux variables d'instance doit être évité sauf pour les méthodes d'initialisation (init
, initWithCoder:
, etc…), la méthode dealloc
et les accesseurs et mutateurs. Pour plus d'information sur l'utilisation de méthodes d'accès, les méthodes d'initialisation et dealloc, consultez cet article.
Par exemple:
@interface NYTSection: NSObject
@property (nonatomic) NSString *headline;
@end
Non pas:
@interface NYTSection : NSObject {
NSString *headline;s
}
En ce qui concerne les qualificateurs de variables introduits avec ARC, le qualificateur (__strong
, __weak
, __unsafe_unretained
, __autoreleasing
) doit être placé entre l'astérisque et le nom de la variable, par ex., NSString * __weak text
.
La convention de nommage Apple devrait être suivie quand possible, surtout en ce qui concerne les règles de management de mémoire (NARC).
Il est mieux d'utiliser des noms descriptifs, et longs si nécessaire, pour les méthodes et variables.
Par exemple:
UIButton *settingsButton;
Non pas
UIButton *setBut;
Un préfixe de trois lettres (par ex. NYT
) doit toujours être utilisé pour le nom des classes et constantes, mais peut être omis pour le nom des entités dans Core Data. Les constantes doivent adopter la convention camelCase avec tous les mots qui commencent avec une lettre capitale, précédées du nom de la classe dans laquelle ils sont déclarés pour la clarté.
Par exemple:
static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;
Non pas:
static const NSTimeInterval fadetime = 1.7;
Les properties et variables locales doivent adopter la convention camelCase avec le premier mot en minuscules.
Les variables d'instance doivent adopter la convention camelCase avec le premier mot en miniscules, précédé par le préfixe « _ ». Ceci est cohérent avec les variables d'instance synthetisées automatiquement par LLVM. Si LLVM peut synthetiser la variable automatiquement, laissez-le faire.
Par exemple:
@synthesize nomDeVariableDescriptif = _nomDeVariableDescriptif;
Non pas:
id nmvar;
Si nécessaire, les commentaires peuvent être utilisés pour expliquer pourquoi un bloc de code fait quelque chose. Les commentaires doivent être à jour ou éliminés.
Les paragraphes de commentaires devraient généralement être évités, le code devrait être suffisament descriptif, avec seulement un besoin intermittent de commentaire et juste quelques lignes d'explications. Ceci ne s'applique pas aux commentaires utilisés pour la documentation.
La méthode dealloc
doit être placée au début de l'implémentation, directement après les expressions @synthesize
et @dynamic
. La méthode init
doit être placée directement après la méthode dealloc
.
La méthode init
doit être structurée comme ceci:
- (instancetype)init {
self = [super init]; // ou appeler l'initialisateur désigné
if (self) {
// Initialisation particulière à cet object
}
return self;
}
NSString
, NSDictionary
, NSArray
, et NSNumber
literals doivent être utilisés quand des instances immutables sont créées pour ces objets. Faites bien attention que la valeur nil
ne soit pas passée aux literals NSArray
et NSDictionary
, parce que ça causerait un plantage.
Par exemple:
NSArray *names = @[@"Brian", @"Craig", @"Véronique"];
NSDictionary *productManagers = @{@"iOS" : @"Andrew", @"Android" : @"Kate"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
Non pas:
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Craig", @"Véronique", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Andrew", @"iOS", @"Kate", @"Android", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];
En accédant à x
, y
, width
, ou height
d'un CGRect
, utilisez toujours les fonctions CGGeometry
au lieu de l'accès direct au membre struct. Extrait de la référence Apple pour CGGeometry
:
Toutes les fonctions décrites dans cette référence qui prendre les structures de data CGRect comme donnée standardise implicitement ces rectangles avant de calculer leurs résultats. Pour cette raison, votre application devrait éviter de lire et écrire directement la donnée sauvegardée dans la structure de données CGRect. À la place, utilisez les fonctions décrites ici pour manipuler les rectangles et pour recupérer leurs caractériques.
Par exemple:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
Non pas:
CGRect frame = self.view.frame;
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
Les constantes sont préférables aux literals in-line ou aux nombres, parce qu'elles peuvent être facilement reproduites de variables utilisés souvent et parce qu'elles peuvent être changées facilement sans avoir besoin de faire une recherche. Constantes devraient être déclarées avec static
et non pas #define
s à moins qu'elle soient utilisées explicitement comme macro.
Par exemple:
static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company";
static const CGFloat NYTImageThumbnailHeight = 50.0;
Non pas:
#define CompanyName @"The New York Times Company"
#define thumbnailHeight 2
Pour l'utilisation d' enum
, il est recommendé de choisir le type fixe spécifié avec un « _ » parce qu'il est de type fort et pour bénéficier de la complétion de code. Le SDK inclus maintenant un macro pour faciliter et encourager l'utilisation de type fixe et souligné — NS_ENUM()
Exemple:
typedef NS_ENUM(NSInteger, NYTAdRequestState) {
NYTAdRequestStateInactive,
NYTAdRequestStateLoading
};
Quand vous travaillez avec des masques de bits, utilisez le macro NS_OPTIONS
.
Exemple:
typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {
NYTAdCategoryAutos = 1 << 0,
NYTAdCategoryJobs = 1 << 1,
NYTAdCategoryRealState = 1 << 2,
NYTAdCategoryTechnology = 1 << 3
};
Les propriétés privées doivent être déclarées dans l'extension de la classe dans le fichier d'implémentation.
Par exemple:
@interface NYTAdvertisement ()
@property (nonatomic) GADBannerView *googleAdView;
@property (nonatomic) ADBannerView *iAdView;
@property (nonatomic) UIWebView *adXWebView;
@end
Les noms des images doit être cohérents pour préserver une bonne organisation. Elles doivent être nommées en utilisant la convention camelCase avec la description de leur utilisation, suivi du suffixe de la classe ou propriété qu'elles customisent (si elle existe), suivie de la description de la couleur et/ou emplacement, et finalement leur état. Par exemple:
RefreshBarButtonItem
/RefreshBarButtonItem@2x
andRefreshBarButtonItemSelected
/RefreshBarButtonItemSelected@2x
ArticleNavigationBarBlanc
/ArticleNavigationBarBlanc@2x
andArticleNavigationBarNoirSelected
/ArticleNavigationBarNoirSelected@2x
.
Les images qui sont utilisées à des fins similaires doivent être regroupées dans leurs groupes respectifs à l'intérieur d'un dossier Images ou d'un « Asset Catalog ».
Puisque nil
est retourné comme NO
il n'est pas nécessaire de le comparer dans une condition. Ne comparez jamais quelque chose avec YES
, parce que YES
est défini comme 1 et un BOOL
peut aller jusqu'à 8 bits.
Ce style permet une plus grande cohérence entre les différents fichiers et une meilleure clarté visuelle.
Par exemple:
if (!unObject) {
}
Non pas:
if (unObject == nil) {
}
Pour un BOOL
, voici deux exemples:
if (estSuper)
if (!unObject.boolValue)
Non pas:
if (estSuper == YES) // Ne faites pas ça
if (unObject.boolValue == NO)
Si le nom d'une propriété BOOL
est exprimée comme un adjectif, la propriété peut omettre le prefixe « is » mais doit specifier un nom conventionel pour l'accesseur get, par exemple:
@property (assign, getter=isEditable) BOOL editable;
Voyez le document et exemple pris de Conseils Généraux de Nommage Cocoa.
Les singletons doivent utiliser un patron thread-safe (état de processus cohérent sans engendrer de problèmes de concurrence) pour créer leur instance unique et partagé.
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});
return sharedInstance;
}
Celui permet d'éviter des plantages possibles, et parfois fréquents.
S'il y a plusieurs directives d'importation, divisez-les en groupes. Commenter chaque groupe est facultatif.
Note : pour les modules utilisez la syntaxe @import.
// Frameworks
@import QuartzCore;
// Models
#import "NYTUser.h"
// Views
#import "NYTButton.h"
#import "NYTUserView.h"
Les fichiers physiques doivent être maintenus en accordance avec le projet Xcode pour éviter d'avoir des fichiers éparpillés. Les groupes crées dans Xcode doivent avoir un dossier équivalent dans le système de fichiers. Le code doit être groupé non seulement par type, mais aussi par caractéristique pour une plus grande clarté.
Si possible, choisissez toujours « Treat Warnings as Errors » dans le Build Settings du target et exposer autant d'avertissements supplémentaires que possible. Si vous avez besoin d'ignorer un avertissement specifique, utiliser Clang's pragma feature.
Si le nôtre n'est pas à votre goût, consultez ces autres guides: