Este guia de estilo define as convenções de código da equipa de Aplicativos Móveis na Infoglobo. O seu feedback é bem-vindo.
Aqui estão alguns documentos da Apple que contribuíram para a formação do guia de estilo. Se algo não é mencionado aqui, provavelmente é abrangido em detalhe num dos seguintes:
- The Objective-C Programming Language
- Cocoa Fundamentals Guide
- Coding Guidelines for Cocoa
- iOS App Programming Guide
- CocoaDevCentral: Cocoa Style for Objective-C: Part I
- CocoaDevCentral: Cocoa Style for Objective-C: Part II
- Encoding
- Língua
- Sintaxe de Dot-Notation
- Espaçamento
- Condicionais
- Golden Path
- Processamento de Erros
- Métodos
- Variáveis
- Nomenclatura
- Comentários
- Init & Dealloc
- Literais
- Funções CGRect
- Constantes
- Tipos Enumerados
- Propriedades Privadas
- Nomenclatura de Imagens
- Boleanos
- Singletons
- Projecto Xcode
- Escuteiro
Todos os ficheiros de código fonte devem estar codificados em UTF-8.
O código fonte deve ser escrito em inglês americano. Isso inclui nomes de classes, métodos, variáveis, propriedades e comentários.
O único lugar onde é aceitável ter texto em português é em strings, e mesmo assim devem estar localizadas com NSLocalizedString
ou NSLocalizedStringFromTable
.
Dot-notation deverá ser sempre utilizado para aceder a, e alterar propriedades. Notação de chaveta é preferida em todos os outros casos.
Por exemplo:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
Não:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
- Indentar com 4 espaços. Nunca com tabs. Certifique-se de que o Xcode está devidamente configurado.
- Chavetas de método, assim como outras chavetas (
if
/else
/switch
/while
etc.) abrem sempre na mesma linha do statement mas fecham numa linha nova.
Exemplo:
if (user.isHappy) {
// Fazer alguma coisa
} else {
// Fazer outra coisa
}
- Deverá existir exactamente uma linha em branco entre métodos para ajudar na claridade visual e organização. Espaço em branco dentro de métodos deverá separar funcionalidade, mas frequentemente isso é um indicador de que o código deverá residir noutro método.
@synthesize
e@dynamic
devem ser declarados em linhas novas na implementation.- Utilizar
#pragma mark
s para demarcar partes funcionais do mesmo ficheiro de código fonte. Preceder demarcações com um#pragma mark #
para gerar uma linha.
Exemplo:
#pragma #
#pragma mark - Datasource methods
Condicionais deverão utilizar sempre chavetas, mesmo quando podem ser escritos sem chavetas (e.g., apenas uma linha), para prevenir erros. Esses erros incluem adicionar uma segunda linha com a expectativa de que fará parte do if
. Outro erro, ainda mais perigoso pode acontecer quando a linha "dentro" do if
é comentada, e a linha seguinte passa a fazer parte do if
. Adicionalmente, este estilo é mais consistente e mais fácil de ler rapidamente.
Exemplo:
if (!error) {
return success;
}
Não:
if (!error)
return success;
ou
if (!error) return success;
O operador ternário, ? :, deverá apenas ser utilizado quando aumenta a claridade ou a limpeza de código. Uma condição, normalmente, é tudo o que deve ser calculado. Calcular mais do que uma condição é normalmente mais fácil de ler quando escrito sob a forma de um if
, ou refactorado para variáveis de instância.
Exemplo:
result = a > b ? x : y;
Não:
result = a > b ? x = c > d ? c : d : y;
Ao escrever código condiconal, a margem esquerda do código deve ser o "golden path", ou caminho "feliz".
Não:
- (void)someMethod {
if ([someOther boolValue]) {
// Fazer algo importante
}
}
Isso é difícil de ler. No lugar, escreva código que falhe cedo e falhe frequentemente:
Exemplo:
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
// Fazer algo importante
}
Um método não deverá ser dividido a meio com uma condição.
Não:
- (void)someMethod {
if ([someOther boolValue]) {
// Fazer algo importante
} else {
// Fazer outra coisa importante
}
}
Isso é difícil de ler de deverá ser reescrito como:
Exemplo:
- (void)someMethod {
if ([someOther boolValue]) {
// Fazer algo importante
return;
}
// Fazer outra coisa importante
}
Eis uma situação em que uma bisecção é aceitável:
Exemplo:
- (void)someMethod {
if ([someOther boolValue]) {
// Fazer algo importante
} else {
// Fazer outra coisa importante
}
// Fazer isto de qualquer forma
}
O objectivo é fazer com que o código na margem esquerda seja o código "esperado", e que o código indentado seja a excepção. Consistência nesta área é importante para a leitura do código.
Quando um método retorna um parâmetro de erro por referência, faça switch
no valor retornado, não na variável de erro.
Exemplo:
NSError *error;
if (![self trySomethingWithError:&error]) {
// Lidar com erro
}
Não:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// Lidar com erro
}
Alguns dos API's da Apple escrevem lixo no parâmetro de erro (caso não seja nulo) em casos de sucesso, por isso utilizar um switch
no erro pode causar falsos negativos (e subsequentes crashes).
Nas assinaturas de método, deverá existir um espaço depois do escopo (símbolo -/+). Deverá existir um espaço entre os segmentos do método.
Exemplo::
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
As variáveis deverão ser nomeadas da forma mais descritiva possível. Variáveis de uma só letra deverão ser evitadas, excepto em ciclos for()
.
Asteriscos que indicam pointers pertencem à variável, e.g., NSString *text
não NSString* text
ou NSString * text
, excepto no caso de constantes.
Definições de propriedades deverão ser utilizadas em vez de variáveis de instância sempre que possível. Acesso directo a variáveis de instância deverá ser evitado, excepto em métodos de inicialização (init
, initWithCoder:
, etc…), métodos dealloc
e em setters e getters escritos à mão. Para mais informações sobre métodos de acesso em inicializadores e dealloc, veja aqui.
Exemplo:
@interface IGBSection: NSObject
@property (nonatomic) NSString *headline;
@end
Não:
@interface IGBSection : NSObject {
NSString *headline;
}
As convenções de nomenclatura da Apple devem ser seguidas sempre que possível, especialmente as relacionadas com regras de gestão de memória (NARC).
Nomes longos e descritivos são bons.
Exemplo:
UIButton *settingsButton;
Não:
UIButton *setBut;
Um prefixo de três letras (e.g. IGB
) deverá ser sempre utilizado para nomes de classes e constantes, no entanto poderão ser omitidos no caso de nomes de entidade no Core Data. Constantes devem ser em camel-case com todas as palavras com inicial maiúscula, e precedidas pela classe relacionada para efeitos de clareza.
Exemplo:
static const NSTimeInterval IGBArticleViewControllerNavigationFadeAnimationDuration = 0.3;
Não:
static const NSTimeInterval fadetime = 1.7;
Propriedades deverão ser escritas em camel-case com a palavra inicial em minúsculas. Se o Xcode puder sintetizar automaticamente a variável, então deixe-o. Caso contrário, de forma a ser consistente, as variáveis de instância associadas deverão ser escritas em camel-case com a palavra inicial em minúsculas, e um underscore de prefixo. Este é o mesmo formato utilizado pela síntese automática do Xcode.
Exemplo:
@synthesize descriptiveVariableName = _descriptiveVariableName;
Não:
id varnm;
Quando utilizar propriedades, variáveis de instância deverão sempre ser acedidas e alteradas utilizando self.
. Isso quer dizer que todas as propriedades deverão ser visualmente distinctas, dado que todas serão precedidas de self.
. Variáveis locais não deverão conter underscores.
Quando necessários, comentários deverão ser utilizados para explicar por que um bocado de código faz o que faz. Os comentários deverão ser mantidos actualizados ou eliminados.
Comentários de bloco deverão ser evitados, dado que o código deve-se auto-documentar o mais possível, com apenas uma necessidade irregular de explicações de poucas linhas. Isto não se aplica a comentários utilizados para gerar documentação.
Métodos dealloc
deverão ser colocados imediatamente a seguir aos inicializaores, que por sua vez seguem os @synthesize
e @dynamic
.
Métodos init
deverão ser estruturados da seguinte forma:
- (instancetype)init {
self = [super init]; // Ou chamar inicializador designado
if (!self) {
return self;
}
// Inicialização customizada
return self;
}
Literais NSString
, NSDictionary
, NSArray
, e NSNumber
deverão ser utilizados aquando da criação de instâncias imutáveis dessas classes. Preste atenção para que valores nil
não sejam passados para literais NSArray
e NSDictionary
, já que isso causa um crash.
Exemplo:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
Não:
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 aceder a x
, y
, width
, ou height
de um CGRect
, utilize sempre as funções de CGGeometry
no lugar de acesso directo aos membros da struct.
Exemplo:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
Não:
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;
Prefira constantes em vez de literais in-line, ou números mágicos, dado que isso permite reproduzir variáveis utilizadas frequentemente, e podem ser alteradas sem find e replace. As constantes deverão ser declaradas como static
e não como #define
s, excepto no caso em que realmente são parte de uma macro.
Exemplo:
static NSString * const IGBAboutViewControllerCompanyName = @"Infoglobo";
static const CGFloat IGBImageThumbnailHeight = 50.0;
Não:
#define CompanyName @"Infoglobo"
#define thumbnailHeight 2
Ao utilizar enum
s, é recomendado que utilize a nova especificação com tipo subjacente porque permite verificação de tipo e o Xcode consegue completar código automaticamente. O SDK inclui uma macro que facilita e encoraja a utlização de tipos subjacentes — NS_ENUM()
Exemplo:
typedef NS_ENUM(NSInteger, IGBAdRequestState) {
IGBAdRequestStateInactive,
IGBAdRequestStateLoading
};
Propriedades privadas deverão ser declaradas em extensões de classe (categorias anónimas) no ficheiro de implementação da classe. Categorias com nome (como IGBPrivate
ou private
) nunca deverão ser utilizadas, a menos que estejam realmente a estender outra classe.
Exemplo:
@interface IGBAdvertisement ()
@property (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;
@end
Nomes de imagens deverão ser consistentes para preservar organização e a sanidade dos desenvolvedores. Deverão ser nomeadas com camel-case do seu propósito, seguido da classe sem prefixo, ou propriedade que estão customizando, seguido de uma descrição de côr ou colocação, e finalmente o seu estado.
Exemplo:
RefreshBarButtonItem
/RefreshBarButtonItem@2x
andRefreshBarButtonItemSelected
/RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite
/ArticleNavigationBarWhite@2x
andArticleNavigationBarBlackSelected
/ArticleNavigationBarBlackSelected@2x
.
Imagens que são utilizadas para um propósito semelhante deverão ser agrupadas em assets de Xcode.
Considerando que nil
é equivalente a NO
, não é necessário compará-los numa condição. Nunca compare nada a YES
, porque YES
está definido como 1 e um BOOL
pode ter até 8 bits.
Isto permite mais consistência e mais claridade visual.
Exemplo:
if (!someObject) {
}
Não:
if (someObject == nil) {
}
Para BOOL
, aqui estão dois exemplos:
if (isAwesome) {
}
if (![someObject boolValue]) {
}
Não:
if ([someObject boolValue] == NO) {
}
if (isAwesome == YES) { // Nunca fazer isto.
}
Se o nome de uma propriedade BOOL
é exprimida como adjectivo, a propriedade pode omitir o prefixo “is”, mas especifica o prefixo convencional para o getter. Por exemplo:
@property (assign, getter=isEditable) BOOL editable;
Objectos singleton deverão utilizar um padrão thread-safe para criar a instância partilhada.
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Isso prevenirá crashes possíveis.
Os ficheiros físicos deversão ser mantidos em sincronia com o projecto Xcode, para evitar a desorganização. Grupos de Xcode deverão estar associados a directórios no sistema de ficheiros. Código deverá ser agrupado não só por tipo, como por funcionalidade.
Sempre que possível, active o "Treat Warnings as Errors" nos settings de build, e active o máximo possível de avisos opcionais. Se precisar de ignorar um aviso específico, utilize um pragma de Clang.
Sempre que estiver a fazer alterações no código, deixe-o mais limpo do que o encontrou. Se encontrar código que viole este guia, corrija-o. Se o código é antigo, actualize-o.