エラーはNSErrorを使いますが、何らかの形でカスタマイズしておくと扱いが楽になります。自分はNSErrorのカテゴリメソッドを追加しておくのがスマートかと思います。

例えばアプリケーションのクラスプレフィックスがPSならば、こんな感じです。

NSError+PS.h

// 独自のエラードメイン(他と重複しないようにする。アプリケーションのBundle Identifierと合わせると良いかも)
#define PSErrorDomain @"net.stack3.PSError"

// エラーコードの定義
typedef enum {
    PSErrorCodeNone = 0,
    PSErrorCodeInternalError,
    PSErrorCodeParseResponseError,
    PSErrorCodeResponseStatusError
} PSErrorCode;

@interface NSError (PS)
  // エラーコードとローカライズされたエラー説明文を受け取る
  + (NSError*) errorWithPSErrorCode:(PSErrorCode)code localizedDescription:(NSString*)localizedDescription;
  // エラーコードのみ受け取る。この場合はコードに応じた説明文が使われる
  + (NSError*) errorWithPSErrorCode:(PSErrorCode)code;
@end

NSError+PS.m

#import "NSError+PS.h"

// コードに対応した説明文
static NSString* _PS_CODE2DESCRIPTION[] = {
    @"",
    @"PSError.InternalError",
    @"PSError.ParseResponseError",
    @"PSError.ResponseStatusError"
};
// 説明文の数を得る(最近こういう事しなくなったな・・・)
static int _PS_NUM_CODE = sizeof(_PS_CODE2DESCRIPTION[0])/sizeof(_PS_CODE2DESCRIPTION);

@implementation NSError(PS)

+ (NSError*)errorWithPSErrorCode:(PSErrorCode)code localizedDescription:(NSString*)localizedDescription {
    // userInfoのキーNSLocalizedDescriptionKeyに割り当てた文字列はNSErro#localizedDescriptionで参照される
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:localizedDescription forKey:NSLocalizedDescriptionKey];
    
    NSError *error = [[self class] errorWithDomain:PSErrorDomain code:code userInfo:userInfo];
    return error;
}

+ (NSError*)errorWithPSErrorCode:(PSErrorCode)code {
    assert(code < _PS_NUM_CODE);
    NSString *description = _PS_CODE2DESCRIPTION[code];
    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:description forKey:NSLocalizedDescriptionKey];
    
    NSError *error = [[self class] errorWithDomain:PSErrorDomain code:code userInfo:userInfo];
    return error;
}

delegateなどでNSErrorを受け取った時、たいていの場合はlocalizedDescriptionを画面に表示することになります。分岐が必要なときはNSError#domainとcodeを見て分岐するのが良いでしょう。