resistance is obsolete ™ ;-)
the groupware construction kit

OGo Coding Styleguides

Well, we need to explain our source code style guides so that people can follow ;-)

Well, all this is rather boring and hard to write down. After all it is pretty much obvious how proper sourcecode formatting is to be done ;-) Anyway, we are pretty religious about formatting, so if you plan to provide patches, please follow the (absolutely incomplete) guide or prepare to get reformatted.

General Sourcecode Procedures

a) we do not use CVS commit logs. Instead we use ChangeLog files. Please document every change you do.

b) we increase library and bundle subminor versions on every change. Whether you are "just" changing the makefile or just run indent on the sources - always increase the subminor version prior a commit.
Because you need to document your change because of a), you should always include the new version in the ChangeLog.

Sample ChangeLog

a single change:

2003-11-29 Helge Hess <helge.hess@opengroupware.org>
* WEClientCapabilities.m: added Morgul as a known (WebDAV) user agent
(v4.2.264)

multiple changes:

2003-11-28 Helge Hess <helge.hess@skyrix.com>
* v4.2.263
* WebDAV/SoWebDAVRenderer.m: subminor cleanups
* WebDAV/SoObjectWebDAVDispatcher.m: unescape destination URL pathes
for MOVE/COPY operations (related to bug 456)

Your gain: ChangeLogs are processed periodically by the ChangeBlogger, so its quite easy to track changes: OGo ChangeBLog

Version Files

You should place the version of your library in a separate Version (Make)File and include that file in your project makefile. Check out NGObjWeb for an example of that. Our section looks like:

include ../common.make
include ./Version

The Version file looks like this:

# $Id: Version,v 1.63 2003/12/03 16:48:37 helge Exp $
SUBMINOR_VERSION:=266
# Requirements:
# v4.2.177 => NGExtensions v4.2.33

Note that we also track dependencies in that file (eg starting with libNGObjWeb v4.2.177, NGObjWeb depends on at least NGExtensions v4.2.33).

Objective-C

So many things to write down, so little time, ...

Tabs vs Spaces, Line-Width

We never use physical tabs for obvious reasons ;-) - so set your editor to produce spaces instead of tabs. Tabwidth is 2 chars. Source code is max 80 chars in width, wrapping is forbidden. We have at a single space in most locations, some examples:

@interface MyClass : NSObject < NSCoding >
- (id)blah;

Note that a space is between colon, class and superclass as well as between the brackets and the protocol.

#include vs #import

We always use #include, never #import - except for Foundation (because that is not properly guarded on MacOSX). All headers need to be properly guarded using #ifdef's.

Class Definitions

Place the @interface in a properly guarded headerfile and the @implementation in a .m file. Exception: if the class is completely private (eg usually WOComponent subclasses), you can include at the head of the .m file. But: if you do, please still do include separation of header and implementation.
At the @end of the @implementation we add the classname in a comment.

Header Sample

// $Id$
#ifndef __NGObjWeb_WOResponse_H__
#define __NGObjWeb_WOResponse_H__
#import <Foundation/NSString.h>
#include <NGObjWeb/WOMessage.h>
...
/*
WOResponse
This WOMessage subclass add functionality for HTTP responses, mostly
the HTTP status. WOResponse also provides some methods for zipping itself.
*/
@class NSData;
@class WORequest;
@interface WOResponse : WOMessage < WOActionResults >
{
unsigned int status;
}
/* HTTP */
- (void)setStatus:(unsigned int)_status;
- (unsigned int)status;
@end
#endif /* __NGObjWeb_WOResponse_H__ */

Note the guard. Note that #import is used for Foundation includes and #include for own ones. Note that we do not include the complete Foundation.h header, just the stuff we need - other required classes are just declared using @class.

Implementation Sample

// $Id$
#include <NGObjWeb/WOResponse.h>
...
#include "common.h"
@implementation WOResponse
...
static unsigned int OWMinimumZipSize = 1024;
...
+ (WOResponse *)responseWithRequest:(WORequest *)_request {
...
- (id)init {
...
- (void)dealloc {
...
[your stuff: accessors, actions, operations]
...
/* description */
- (NSString *)description {
...
}
@end /* WOResponse */

We always use a common.h file, so that we can catch differences between Foundations in a central position. We place class "global" statics (usually default configurations) just after the @implementation.
The method sequence is:

  • +initialize / +version
  • Constructors (+ methods)
  • Init Methods (- init*)
  • -dealloc Method
  • accessor methods
  • other stuff
  • action methods
  • description method
  • Types

    Please use proper style for pointers:

    NSString *tmp; // CORRECT
    NSString* wrong; // WRONG
    

    Our approach is obviously correct, since in C the pointer specifier '*' is variable specific ;-)

    Methods

    Various things, braces, variables. For braces we place the opening brace on the same line if the method name fits on a single line, otherwise we place it on the next line.
    For parameters we use the "_var", "var_" and "_var_" idea - for input, output and in/out parameters (so you almost always deal with "_var" parameters.

    - (void)appendString:(NSString *)_str {
    }
    - (void)appendString:(NSString *)_str
    escapeHTML:(BOOL)_doEscape
    {
    }
    

    Note that we do not align wrapped names at the colon, but rather indent the follow-ups by 2 spaces in the next line. We prepend return type definitions with a single space.

    We always declare the return-type:

    - doIt; // WRONG
    - (id)doIt; // RIGHT
    

    Class Versions

    We use class versions to work around the fragile base class issue. Notably in Objective-C we "only" have that for changing ivar layouts. You can use the +initialize method to check whether the superclass is of the proper class:

    + (int)version {
    return [super version] + 0 /* v2 */;
    }
    + (void)initialize {
    NSAssert2([super version] == 2,
    @"invalid superclass (%@) version %i !",
    NSStringFromClass([self superclass]), [super version]);
    }
    

    Note that our class version changes if a superclass of us changes because we add up. We add the current version we think we are at in the comment.

    ivars

    We always refer to ivars using self-> to make it obvious that we are talking about ivars.

    self->name = [_name copy];
    

    This places a copy of the _name parameter into the name instance variable.

    -init Methods

    We always check the return value of the super-init method, to allow the super method to replace or nil the object.

    - (id)init {
    if ((self = [super init])) {
    [self setStatus:200];
    ...
    }
    return self;
    }
    

    RETAIN/RELEASE Macros

    We deprecated the use of RETAIN/RELEASE macros which are plain ugly and are only useful in the Boehm-GC context which isn't used in OGo anyway (mostly because we want to stay portable to Cocoa).
    So while we still use RETAIN/RELEASE macros, we are slowly phasing that out:

    self->person = RETAIN(_person); // WRONG
    self->person = [_person retain]; // correct
    ASSIGN(self->person, _person); // correct
    

    Note that we suggest using ASSIGN and ASSIGNCOPY! Also note that you should always copy basetypes like NSString's, NSNumber's or NSData's to make use of the advantages of the immutable classes.

    Categories

    When writing categories, we use the Classname+Categoryname.m pattern for naming the files. Eg:

    @implementation NSString(HTMLEscaping)
    ...
    @end /* NSString(HTMLEscaping) */
    

    This should end up in a file called NSString+HTMLEscaping.m. Note that the @end is commented because it is an @end of a @implementation section.

    Variable Declarations

    Stuff should be properly aligned:

    int minimumActiveSessionsCount;
    NSString *name;
    NSString *path;
    WORequestHandler *defaultRequestHandler;
    

    If you only have a really short section and one variable has a really long type name, non-aligned stuff is acceptable as well:

    - (void)doThis {
    WORequestHandler *rh;
    int status; }
    

    but this should be limited to really short ones.

    Note that sourcecode should be compatible with gcc 2.95 - so no C++ style inline declarations!

    - (void)doIt {
    int i;
    for (i ...)
    int j; // WRONG: move up to the i
    for (j ...)
    }
    

    Comments

    Hm, where do we use comments? For one we use comments to separate method sections, eg:

    /* localization */
    - (void)setLanguages:(NSArray *)_langs;
    - (NSArray *)languages;
    /* notifications */
    - (void)awake;
    - (void)sleep;
    

    In pratice we almost never need to use comments to describe what a method does - the method name should clearly express that, which is pretty easy using ObjC keyword selectors.
    Anyway, if it is required, we add the comments in the implementation of the method:

    - (void)setLanguages:(NSArray *)_languages {
    /* this method sets the language-array of the receiver */ // useless ...
    }
    

    But we *do* recommend to comment any form of hack in an extensive way. Describe non-obvious sections in detail!, like:

    - (BOOL)allowDeletePropertiesOnNewObjectInContext:(WOContext *)_ctx {
    NSString *ua;
    ua = [[_ctx request] headerForKey:@"user-agent"];
    if ([ua hasPrefix:@"Evolution"]) {
    /* if Evo creates tasks, it tries to delete some props at the same time */
    return YES;
    }
    return NO;
    }
    

    Public "methods" are those declared in the @interface section. If we need forward declarations of private methods, we add an informal protocol in front of the @implementation, like:

    @interface WEClientCapabilities(Privates)
    - (id)initWithRequest:(WORequest *)_request;
    @end
    

    What you want to comment are the so called "designated initializers" (read ObjC documentation on that ;-). We seldom do this, as the DA is usually obvious, but in case:

    - (id)initWithGCCollectSize:(unsigned)_size; /* designated initializer */
    

    Private Methods

    You probably want to have "private" methods. In practice this is impossible in Objective-C since everything can be invoked dynamically. But you can force a compiler warning by not including the method in the @interface section.
    Anyway, if you have a really, really private method, we usually prefix that using a "_", eg:

    - (void)_addHTTPCookie:(id)_cookie to:(NSMutableArray *)_a {
    

    But attention: this *is* dangerous. Remember that the method is not really privately scoped at runtime - so if a super- or subclass overrides the method, you'll might get into trouble. So please name the method as specific as possible - if the name describes exactly what it does, it is unlikely to break ;-)

    BOOL

    Objective-C also introduces a boolean type called 'BOOL' and the associated values 'YES' and 'NO'. We think that BOOL arguments should not be treated as strict booleans but rather in the common C sense that NO is 0 and everything else is true.
    Nevertheless we suggest to follow the rule to be tolerant on input and be strict on output (return values!)
    Some examples:

    if (myBool) {} // correct
    if (myBool == YES) {} // WRONG!!
    if (myBool == NO) // OK
    if (!myBool) // OK
    

    but for output, try to be strict:

    - (BOOL)isEqual:(id)_other {
    return [_other compare:self] == 0 ? YES : NO;
    }
    

    The issue is a bit more controversial on other base types, like integer or pointer values. Some examples what we consider OK:

    if (myInt > 0) {} // correct
    if (myInt) {} // WRONG!
    if (myPtr) {} // OK
    if (!myPtr) {} // so lala, not encouraged
    if (myPtr == NULL) // OK
    

    For most C types we just consider regular C conventions, eg anyone knows what "if (ptr)" does, there is no need for "if (ptr != NULL)".

    C level things

    Some C related constructs.

    if ((a = [self value]))
    return YES;
    

    Note: spaces after if, before and after =. Note that we do not use braces for the return because it is just a single statement.

    if (person == nil) {
    ...
    }
    

    We place the opening brace at the top, then ident with two spaces. Also note that we do an explicit check for == nil instead of using just !person which is valid C, but considered bad style (person is an object, not a boolean).

    Accessors

    Accessor methods are methods which usually retrieve or set an ivar, they are mostly used in conjunction with keyvalue coding. We always write the set-accessor first, followed by the get-accessor. We do not leave an empty separator line between set and get accessor. Finally we prefix the accessor section using an /* accessor */ comment.

    /* accessors */
    - (void)setBlurb:(NSString *)_value {
    ASSIGNCOPY(self->blurb, _value);
    }
    - (NSString *)blurb {
    return self->blurb;
    }
    - (void)setBlah:(NSDictionary *)_value {...
    

    Note that we use "self->" to signal that blurb is an ivar. Further we use ASSIGNCOPY, because _value is a basetype, so that we don't run into mutability issues and gain speed.

    .wod Files

    Write more. but basics is, braces on the top line, indent by two spaces. Spaces not after the element name, but after everything else:

    TableView: SkyTableView {
    list = persons;
    }
    

    If you have a set of really short declarations, you can line them up:

    LoginLabel: WOString { value = labels.login; }
    LogoutLabel: WOString { value = labels.logout; }
    

    But please properly align stuff.

    We welcome your feedback!
    Trademarks.  
    This site is sponsored by
    SKYRIX Software AG
    ZideOne GmbH
    MDlink