Esta guía de estilo define los líneamientos de desarrollo para los equipos de iOS en The New York Times. Agradecemos sus comentarios en los issues, pull requests y tweets. También, estamos contratando.
Gracias a todos nuestros contribuyentes.
Aquí puedes encontrar algunos de los documentos de Apple sobre las guías de estudio. Si algo no se menciona aquí, probablemente estará cubierto con gran detalle en alguno de éstos:
- El lenguaje de programación Objective -C
- Guía de fundamentos de Cocoa
- líneamientos de desarrollo para Cocoa
- Guía de desarrollo de aplicaciones iOS
- Sintaxis Dot Notation
- Espaciado
- Condicionales
- Manejo de errores
- Métodos
- Variables
- Nombres
- Comentarios
- Init y Dealloc
- Literales
- Funciones CGRect
- Constantes
- Tipos enumerados
- Máscara de bits
- Propiedades privadas
- Nombres de imágenes
- Booleanos
- Singletons
- Imports
- Protocolos
- Proyecto de Xcode
Hay que utilizar punto siempre que se acceda o altere alguna propiedad. En todos los otros casos se recomienda el uso de corchetes.
Por ejemplo:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
Incorrecto:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
- Se tiene que indentar usando 4 espacios, nunca se debe de indentar con tabuladores. Asegúrate de tener activada esta función en Xcode.
- Los métodos o cualquier otro bloque de código que utilice llaves (
if
/else
/switch
/while
etc.) deben de abrirse siempre en la misma línea, pero cerrarse en una línea diferente.
Por ejemplo:
if (user.isHappy) {
// Hacer algo
}
else {
// Hacer otra cosa
}
- Debe de haber exactamente una línea en blanco entre métodos para mejorar la claridad y organización visual.
- Los espacios en blanco dentro de los métodos deben de separar funcionalidad (aunque en algunas ocasiones esto significa que el método puede ser dividido en métodos más pequeños). En métodos con nombres largos que incluyan muchos verbos, una sola línea en blanco puede ser usada para proporcionar una separación visual antes del cuerpo del método.
@synthesize
y@dynamic
deben declararse en nuevas líneas de código.
Los condicionales deben de tener siempre llaves, incluso cuando no sean necesarias se deben de utilizar (por ejemplo, cuando sólo hay una línea de código), esto para prevenir errores. Estos errores pueden ser, agregar una segunda línea de código dentro del if
esperando que sea parte del mismo bloque. Otro error más peligroso
sería comentar la única línea de código del if
, lo que convertiría la siguiente línea parte del primer bloque. Además, este estilo es más consistente con los otros condicionales, por lo tanto es mucho más fácil interpretarlo.
Por ejemplo:
if (!error) {
return success;
}
Incorrecto:
if (!error)
return success;
if (!error) return success;
El operador ternario ?
debe de ser utilizado únicamente cuando ayuda a mejorar la claridad o limpieza del código. Esto es, cuando sólo una condición será evaluada. Evaluar múltiples condiciones es mucho más entendible si se hace con un if
.
Por ejemplo:
result = a > b ? x : y;
Incorrecto:
result = a > b ? x = c > d ? c : d : y;
Cuando los métodos regresen un parámetro error, debe de tratarse el valor regresado y no una variable de error.
Por ejemplo:
NSError *error;
if (![self trySomethingWithError:&error]) {
// Manejar error
}
Incorrecto:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// Manejar error
}
Algunas APIs de Apple guardar valores basura en el parámetro de error aunque no exista el error, por lo que manejar una variable de error puede causar falsos negativos en la aplicación (que podría terminar en un fallo).
En la declaración de métodos, debe de existir un espacio después del símbolo -
o +
. Deben de existir espacios, igualmente, entre los segmentos del método.
Por ejemplo:
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
Las variables deben de ser nombradas de la manera más descriptiva posible, con el nombre comunicando de la manera más explicita que es la variable y la información pertinente para que un programador pueda usarla apropiadamente.
NSString *title
: Es algo razonable asumir que "title" es un string.NSString *titleHTML
: Esto indica que el título puede incluir HTML por lo que necesita que se parsee para ser mostrado. El prefijo "HTML" es necesario para que un programador use la variable correctamente.NSAttributedString *titleAttributedString
: Un titulo que ya tiene formato para ser mostrado._En este caso,AttributedString
ayuda al programador a tomar una buena decisión dependiendo del contexto.NSDate *now
: No se necesita más información en el nombre.NSDate *lastModifiedDate
: Sólo usarlastModified
podría generar ambigüedad; dependiendo del contexto, uno podría asumir distintos tipos con el nombre.NSURL *URL
vs.NSString *URLString
: En el caso en que un mismo valor pueda ser presentado en diferences clases, es recomendado distinguirlos en el nombre para evitar la confusión que se pueda generar.NSString *releaseDateString
: Otro ejemplo de como una variable puede ser representada por otra clase, por lo que el nombre ayuda a evitar ambigüedades.
Variables con una sola letra como nombre se deben de evitar excepto en los contadores de los ciclos.
Los asteriscos que indican apuntadores pertenecen a la variable, por ejemplo, NSString *text
no NSString* text
o NSString * text
, excepto en los casos de las constantes (NSString * const NYTConstantString
).
La definición de propiedades debe de ser usada siempre que sea posible, en lugar de la definición de variables de instancia. Las variables directas de instancia deben de ser evitadas, excepto en los métodos de inicialización (init
, initWithCoder:
, etc…), los métodos dealloc
y los métodos de acceso (set
y get
). Para más información de el uso de métodos de acceso en inicializadores y dealloc
consulte esta página.
Por ejemplo:
@interface NYTSection: NSObject
@property (nonatomic) NSString *headline;
@end
Incorrecto:
@interface NYTSection : NSObject {
NSString *headline;
}
Cuando se trate de la clasificación de variables agregadas con ARC, el clasificador (__strong
, __weak
, __unsafe_unretained
, __autoreleasing
) debe de ser puesto entre el asterisco y el nombre de la variable, por ejemplo: NSString * _weak text
.
Se deben de seguir las convenciones de nombramiento de Apple siempre que sea posible, especialmente las que están relacionadas con las reglas de manejo de memoria (NARC).
Los nombres largos y descriptivos de variables son buenos.
Por ejemplo:
UIButton *settingsButton;
Incorrecto:
UIButton *setBut;
Un prefijo de tres letras (por ejemplo, NYT
) siempre se debe de usar para nombres de clases y constantes, pero debe de ser omitido en los nombres de las entidades de Core Data. Las constantes deben de ser 'camel-case' con todas las palabras en mayúsculas y un prefijo relacionado con el nombre de la clase. Usar un prefijo de sólo dos letras (por ejemplo, NS
) está reservado para el uso de Apple.
Por ejemplo:
static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;
Incorrecto:
static const NSTimeInterval fadetime = 1.7;
Las propiedades y variables locales deben de ser 'camel-case' con la primera palabra en minúsculas.
Las variables de instancia deben de ser 'camel-case' con la primera palabra en minúsculas y debe de tener el prefijo con un guión bajo. Esto es consistente con la sintetización automática de variables de instancia del LLVM. Si el LLVM puede sintetizar la palabra automáticamente, hay que dejar que lo haga.
Por ejemplo:
@synthesize descriptiveVariableName = _descriptiveVariableName;
Incorrecto:
id varnm;
Las categorías deben de ser usadas para segmentar funcionalidad de manera concisa y deben de ser nombradas para describir esa funcionalidad.
Por ejemplo:
@interface UIViewController (NYTMediaPlaying)
@interface NSString (NSStringEncodingDetection)
Incorrecto:
@interface NYTAdvertisement (private)
@interface NSString (NYTAdditions)
Los métodos y las propiedades agregadas a una categoría deben ser nombrados con el prefijo de la aplicación u organización. Esto evita que por error se genere un override de algún método existente y además reduce la posibilidad de que dos categorías de diferentes librerías tengan un método con el mismo nombre (en este caso, no se sabe cual de los dos métodos será llamado por lo que puede prestarse a efectos no deseados).
Por ejemplo:
@interface NSArray (NYTAccessors)
- (id)nyt_objectOrNilAtIndex:(NSUInteger)index;
@end
Incorrecto:
@interface NSArray (NYTAccessors)
- (id)objectOrNilAtIndex:(NSUInteger)index;
@end
Cuando sean necesarios, los comentarios deben de ser usados para explicar el porque de una sección del código hace algo. Los comentarios deben de ser actualizados o eliminados.
Los bloques de comentarios deben de ser evitados, el código debe de ser lo más entendible posible, con la necesidad de pequeñas explicaciones intermitentes en el mismo. Esto no aplica con los comentarios usados para generar documentación.
Los métodos dealloc
deben de ser incluidos al principio de la implementación, justo después de las líneas @synthesize
y @dynamic
. El init
debe de ser puesto debajo de los métodos dealloc
de cualquier clase.
Los métodos de init
deben de ser estructurados de la siguiente manera:
- (instancetype)init {
self = [super init]; // o llamar el inicializador designado
if (self) {
// Inicialización propia
}
return self;
}
Las literales de NSString
, NSArray
y NSNumber
deben de ser usadas cuando se creen instancias inmutables de esos objetos. Hay que prestar mayor atención a que los valores nil
no sean pasados a las literales NSArray
y NSDictionary
, porque esto ocasionará que la aplicación deje de funcionar.
Por ejemplo:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
Incorrecto:
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];
Cuando se accede a las propiedades x
, y
, width
o height
de un CGRect
, siempre se debe de utilizar las funciones de CGGeometry
functions en lugar de accederlas directamente. De la referencia de Apple sobre CGGeometry
:
Todas las funciones descritas en esta referencia que toman las estructuras de datos de CGRect como entradas implícitamente estandarizan los rectángulos antes de calcular los resultados. Por esta razón, sus aplicaciones deben de evitar leer y escribir directamente la información guardada en la estructura de datos de CGRect. En lugar de eso, se deben de utilizar las funciones descritas aquí para manipular rectángulos y obtener sus características.
Por ejemplo:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
Incorrecto:
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;
Las constantes tienen preferencia sobre literales o números directo en la línea de código, porque facilitan la reproducción de variables usadas en la aplicación y pueden ser cambiadas fácilmente sin la necesidad de buscar y reemplazar. Las constantes deben de ser declaradas como static
y no #define
excepto cuando se esté usando explícitamente como macro.
Por ejemplo:
static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company";
static const CGFloat NYTImageThumbnailHeight = 50.0;
Incorrecto:
#define CompanyName @"The New York Times Company"
#define thumbnailHeight 2
Cuando se estén utilizando enum
se recomienda utilizar la nueva especificación de tipo fijo subyacente, ya que tiene una forma más fuerte de verificación de código. El SDK incluye una macro que facilita el uso de tipos fijos subyacentes: NS_ENUM()
.
Ejemplo:
typedef NS_ENUM(NSInteger, NYTAdRequestState) {
NYTAdRequestStateInactive,
NYTAdRequestStateLoading
};
Cuando se trabaje con máscara de bits, se debe de usar el macro NS_OPTIONS
.
Ejemplo:
typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {
NYTAdCategoryAutos = 1 << 0,
NYTAdCategoryJobs = 1 << 1,
NYTAdCategoryRealState = 1 << 2,
NYTAdCategoryTechnology = 1 << 3
};
Las propiedades privadas deben de ser declaradas en las extensiones de las clases (categorías anónimas) en el archivo de implementación de una clase.
Ejemplo:
@interface NYTAdvertisement ()
@property (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;
@end
Los nombres de imágenes deben de preservar la organización de manera consistente y ayudar al desarrollador. Los nombres deben de utilizar 'camel-case' y describir de manera explicita su proposito, seguido por el nombre de la clase o propiedad a la que serán asignados sin prefijo (en caso de que exista una), seguido de una descripción de color y/o colocación, y por último el estado al que corresponde.
Ejemplo:
RefreshBarButtonItem
/RefreshBarButtonItem@2x
andRefreshBarButtonItemSelected
/RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite
/ArticleNavigationBarWhite@2x
andArticleNavigationBarBlackSelected
/ArticleNavigationBarBlackSelected@2x
.
Las imágenes que se utilicen para propositos similares deben de estar agrupadas respectivamente en la carpeta de Imágenes o el catálogo de 'Assets'.
Nunca se debe de comparar algo directamente con YES
, porque YES
está definido como un 1
, y un BOOL
en Objective-C es de tipo CHAR
de 8 bits (por lo que el valor de 1111110
regresaría un NO
si es comparado con YES
).
Para el apuntador de un objeto:
if (!someObject) {
}
if (someObject == nil) {
}
Para un valor BOOL
:
if (isAwesome)
if (!someNumber.boolValue)
if (someNumber.boolValue == NO)
Incorrecto:
if (isAwesome == YES) // Nunca hacer esto.
Si el nombre de una propiedad BOOL
está expresado como un adjetivo la propiedad puede omitir el prefijo is
pero debe de especificar el nombre convencional para el acceso (‘getter’).
Por ejemplo:
@property (assign, getter=isEditable) BOOL editable;
Texto y ejemplo obtenido de Cocoa Naming Guidelines.
Los objetos Singletons deben de usar un patrón 'thread-safe' para la creación de una instancia compartida.
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});
return sharedInstance;
}
Esto ayudará a prevenir que la aplicación deje de funcionar de manera ocasional o frecuente.
Si hay más de una declaración de 'import', se deben de agrupar las declaraciones juntas. Comentar cada grupo es opcional.
Nota: Para los modulos usar la sintaxis @import.
Por ejemplo:
// Frameworks
@import QuartzCore;
// Modelos
#import "NYTUser.h"
// Vistas
#import "NYTButton.h"
#import "NYTUserView.h"
En un protocolo de delegado o 'data source', el primer parámetro de cada método debe de ser el objeto que envía el mensaje.
Esto ayuda a evitar ambigüedades en los casos en que un objeto es delegado de múltiples objetos, además ayuda a clarificar la lectura de la clase que implementa los métodos del delegado.
Por ejemplo:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
Incorrecto:
- (void)didSelectTableRowAtIndexPath:(NSIndexPath *)indexPath;
Los archivos físicos deben de asemejar al proyecto de Xcode para evitar el desorden de los archivos. Cualquier grupo que se haga en Xcode debe de ser reflejado en las carpetas del proyecto. El código debe de ser agrupado no sólo por su tipo, si no además por su funcionalidad para mayor claridad.
Cuando sea posible, siempre hay que activar el "Treat Warnings as Errors" (tratar alertas como errores) en los 'Build Settings' del objetivo y activar la mayor cantidad de alertas adicionales como sea posible. Si es necesario ignorar ciertas alertas, se puede utilizar la funcionalidad Clang’s pragma.
Si nuestra guía de estilo no es lo que estás buscando, puedes encontrar otras guías de estilo aquí: