Este guia descreve as convenções de desenvolvimento da equipe de iOS do The New York Times. Sua contribução é bem-vinda em nossas issues, pull requests e tweets. Além disso, estamos contratando.
Agradecemos a todos os nossos contribuidores.
Seguem alguns dos documentos oficiais da Apple que informam a convenção padrão da linguagem. Caso algo não seja mencionado aqui, as seguintes referências podem ajudar:
- A linguagem de programação Objective-C
- Guia de fundamentos Cocoa
- Convenções de código Cocoa
- Guia de programação de aplicativos iOS
- Quando utilizar ponto
- Espaçamento
- Condicionais
- Tratamento de erros
- Métodos
- Variáveis
- Nomenclaturas
- Comentários
- Init e Dealloc
- Literais
- Funções CGRect
- Constantes
- Tipos enumerados
- Propriedades privadas
- Nomenclatura de imagens
- Booleanos
- Singletons
- Projeto no Xcode
Utilize o ponto sempre quando for acessar e alterar uma propriedade. Em todos os outros casos é recomendada a utilização dos colchetes.
Exemplo correto:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
Inadequado:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
- Idente utilizando 4 espaços, Nunca idente com tabulações. Certifique-se de configurar essa preferência no Xcode.
- Métodos e qualquer bloco de códico que utilize chaves (
if
/else
/switch
/while
etc.) devem sempre ser declarados na mesma linha. Feche o bloco em um nova linha.
Exemplo correto:
if (user.isHappy) {
// verdadeiro
}
else {
// falso
}
- Deve haver exatamente uma linha em branco entre os métodos para auxiliar na organização visual. Espaços em branco dentro dos métodos devem separar funcionalidades, mas sua principal função é separadar métodos.
@synthesize
e@dynamic
devem ser declarados em novas linhas.
Condicionais, como por exemplo if
, devem sempre utilizar chaves (mesmo em casos onde o corpo da condicional necessite apenas de uma linha) evitando assim esse tipo de erro. Esses erros incluem a adição de uma segunda linha e esperam que a mesma faça parte do bloco. Outra falha ainda maior pode ocorrer quando a linha que faz parte da condicional é comentada fazendo com que a próxima linha, involuntariamente, se torne parte da condicional. Além disso, esse estilo é mais consistente com todas as outras condicionais e, portanto, mais facilmente interpretado.
Exemplo correto:
if (!error) {
return success;
}
Inadequado:
if (!error)
return success;
or
if (!error) return success;
O operador ternário, ?
, deve ser utilizado apenas em caso onde sua aplicação facilita a interpretação e clareza visual do código. Ele deve ser aplicado quando uma condição é avaliada. A avaliação de várias condições é mais compreensível com uma instrução if
.
Exemplo correto:
result = a > b ? x : y;
Inadequado:
result = a > b ? x = c > d ? c : d : y;
Quando os métodos retornarem um parâmetro de erro referênciado, deve-se tratar o valor retornado e não a variável de erro.
Exemplo correto:
NSError *error;
if (![self trySomethingWithError:&error]) {
// tratar o erro
}
Inadequado:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// tratar o erro
}
Algumas APIs da Apple armazenam valores ao parâmetro de erro sem existir um erro de fato (um exemplo seria armazenar o valor 'não existem erros'), por isso validar se a váriavel é nula pode ocasionar a falso negativo (e, posteriormente, falhas).
Na assinatura de um método, deve haver um espaço após o escopo (símbolo de -/+). Também deve existir um espaço entre os parâmetros dos métodos.
Examplo correto:
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
As variáveis devem ser nomeadas de forma mais descritiva possível. Nomes de variáveis com uma única letra devem ser evitados, exceto em estruturas de repetição do tipo for()
.
Os asteriscos, que indicam ponteiros, pertencem a variável, por exemplo: NSString *text
, não NSString* text
ou NSString * text
, exceto em caso onde sejam aplicados a constantes.
As definições de propriedade vem ser utilizadas sempre que possível. O acesso direto a variáveis de instância deve ser evitado, com excessão de métodos inicializadores (init
, initWithCoder:
...), métodos dealloc
e métodos acessores (set
e get
). Para mais informações sobre o uso de métodos acessores em métodos inicializadores e dealloc
, consulte esta página.
Exemplo correto:
@interface NYTSection: NSObject
@property (nonatomic) NSString *headline;
@end
Inadequado:
@interface NYTSection : NSObject {
NSString *headline;
}
As convenções de nomenclatura da Apple devem ser respeitadas sempre que possível, especialmente aquelas relacionadas a regras de gerenciamento de memória (NARC).
Nomes longos para variáveis e métodos são uma boa opção.
Exemplo correto:
UIButton *settingsButton;
Inadequado:
UIButton *setBut;
O prefixo de 3 letras (exemplo: NYT
) deve sempre ser usado em nomes de classes e contantes, no entanto, pode ser omitido para nomes de entidades do tipo Core Data. Contantes devem ser camel-case com todas as palavras em a maiúsculo e prefixadas com o nome da classe, facilitando a interpretação do código.
Exemplo correto:
static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;
Inadequado:
static const NSTimeInterval fadetime = 1.7;
Propriedades devem ser nomeadas utilizando o padrão camel-case com a primeira palavra em letras minúsculas. Se o Xcode sintetiza a variável automaticamente, utilize esse recurso. Caso contrário, para manter a consistência, a variável de instância referenciada nessa propriedade deve utilizar o padrão camel-case iniciando com o underscore (_
) e a primeira palavra com letras minúsculas. Este é o formato de síntese padrão do Xcode.
Exemplo correto:
@synthesize descriptiveVariableName = _descriptiveVariableName;
Inadequado:
id varnm;
Quando uma propriedade for utilizada, as variáveis de instância devem ser acessadas ou alteradas com o self.
, isso significa distinguir as propriedades visualmente. Variáveis locais não devem conter underscores.
Quando forem necessários, os comentários devem explicar o porquê um determinado bloco de código faz algo. Qualquer comentário utilizado deve ser mantido atualizado ou excluído.
Blocos de comentários devem ser evitados, assim como o código deve se auto-documentar, ou seja, necessitar de uma quantidade mínima de explicações. Isso não se aplica as informações utilizadas para gerar uma documentação.
Métodos dealloc
devem er colocados no topo das classes de implementação, logo após as declarações de @synthesize
e @dynamic
. O init
deve ser colocado imediatamente abaixo do método dealloc
de qualquer classe.
Métodos init
devem ser estruturados da seguinte forma:
- (instancetype)init {
self = [super init]; // ou chame o inicializar correspondente
if (self) {
// Inicialização customizada
}
return self;
}
NSString
, NSDictionary
, NSArray
e NSNumber
devem ser usados sempre para a criação de instâncias imutáveis desses objetos. Atenção especial a utilização do valor nil
em NSArray
e/ou NSDictionary
, ela pode gerar falha.
Exemplo correto:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
Inadequado:
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];
Ao acessar x
, y
, width
ou height
de um CGRect
, sempre use as funções CGGeometry
ao invés de acessar os membros diretamente. Referência da Apple sobre as funções CGGeometry
:
All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
Exemplo correto:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
Inadequado:
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;
Constantes têm preferência sobre strings literais ou números, uma vez que permitem fácil reprodução de variáveis comumente usadas e podem ser rapidamente alteradas sem a necessidade de localizar e substituir. Constantes devem ser declaradas como static
e não #define
, a menos que sejam utilizadas explicitamente como macros.
Exemplo correto:
static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company";
static const CGFloat NYTImageThumbnailHeight = 50.0;
Inadequado:
#define CompanyName @"The New York Times Company"
#define thumbnailHeight 2
Ao usar enum
s, recomenda-se utilizar a nova espeficicação de tipo fixo subjacente pois este tem forte verificação de código. O SDK inclui agora uma macro que facilita e incentiva o uso de tipos fixos subjacentes - NZ_ENUM()
.
Example:
typedef NS_ENUM(NSInteger, NYTAdRequestState) {
NYTAdRequestStateInactive,
NYTAdRequestStateLoading
};
Propriedades privadas devem ser decladas em extensões da classe (categorias anônimas) no arquivo de implementação. Categorias nomeadas (como NYTPrivate
ou private
) jamais devem ser utilizadas, a menos que sejam extensões de outra classe.
Exemplo correto:
@interface NYTAdvertisement ()
@property (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;
@end
O nome de uma imagem deve ser consistente, preservando a organização e o objetivo ao qual foi criada. Ela deve utilizar o padrão camel-case para descrever sua finalidade, seguido do prefixo da classe ou propriedade que esta sendo personalizada (caso exista), seguido por uma descrição mais detalhada de sua coloração e, finalmente, seu estado (selecionado, por exemplo).
Exemplo correto:
RefreshBarButtonItem
/RefreshBarButtonItem@2x
andRefreshBarButtonItemSelected
/RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite
/ArticleNavigationBarWhite@2x
andArticleNavigationBarBlackSelected
/ArticleNavigationBarBlackSelected@2x
.
Imagens que são utilizadas para um propósito similar devem fazer parte do mesmo grupo, dentro de uma pasta.
nil
é interpretado como NO
portanto não é necessário compara-lo em condições. Nunca compare algo diretamente com YES
porque YES
é definido como 1 e um BOOL
pode ser de até 8 bits.
Isso permite uma maior consistência entre os arquivos e maior clareza visual.
Exemplo correto:
if (!someObject) {
}
Inadequado:
if (someObject == nil) {
}
Para um BOOL
, temos dois exemplos:
if (isAwesome)
if (![someObject boolValue])
Inadequado:
if ([someObject boolValue] == NO)
if (isAwesome == YES) // Never do this.
Se o nome de uma propriedade do tipo BOOL
é expresso como um adjetivo, a propriedade pode omitir o prefixo "is", mas deve continuar especificando o nome convencional para o metodo de acesso get
. Exemplo:
@property (assign, getter=isEditable) BOOL editable;
Textos e exemplos tirados do Cocoa Naming Guidelines.
Objetos Singleton devem utilizar o padrão thread-safe para criação de uma instância compartilhada.
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Isso evita possíveis falhas.
Os arquivos físicos (estrutura de diretórios do Finder), devem ser mantidos em sincronia com os arquivos do projeto no Xcode. Qualquer grupo criado no Xcode deve refletir na geração de uma pasta no sistema de arquivos (Finder). O código não deve ser agrupado apenas pelo tipo, mas também pela sua característica comum.
Quando possível, sempre tratar os avisos de alerta como erros no target Build Settings
e habilitar todos os tipos de alertas adicionais possíveis. Caso seja necessário ignorar uma advertência específica, utilize a pragma do Clang.
Caso não goste de nossa convenção, segue abaixo outros padrões: