Added a base class for all FMDatabase errors. #110

Closed
wants to merge 1 commit into
from

Conversation

Projects
None yet
5 participants
@dodikk
Contributor

dodikk commented Jan 10, 2013

Added an NSError child as a base class for all FMDatabase errors (both existing and upcoming ones). It should be easier for your users to handle them.

Fixed error domain for SQL validation function.

@ccgus

This comment has been minimized.

Show comment
Hide comment
@ccgus

ccgus Jan 14, 2013

Owner

Not sure why this is needed?

Owner

ccgus commented Jan 14, 2013

Not sure why this is needed?

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Feb 21, 2014

Contributor

I use error polymorphism in my applications to avoid long if statements (by implementing UI related categories within the client code).

P.S. Sorry for long delay in response and missing your reply among other notifications.

Contributor

dodikk commented Feb 21, 2014

I use error polymorphism in my applications to avoid long if statements (by implementing UI related categories within the client code).

P.S. Sorry for long delay in response and missing your reply among other notifications.

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Dec 29, 2014

Contributor

@dodikk Your subclass is not adding a single method to NSError and even if it were it would be a better approach to add them in a category to NSError. Fixing the error domain is a good idea though.

Contributor

ChristianKienle commented Dec 29, 2014

@dodikk Your subclass is not adding a single method to NSError and even if it were it would be a better approach to add them in a category to NSError. Fixing the error domain is a good idea though.

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

@dodikk Your subclass is not adding a single method to NSError

@ChristianKienle , neither the exception subclasses do in the languages like Java and C#. I think, having a hierarchy of errors is a good practice since you can use some help from the compiler to handle the error.

Contributor

dodikk commented Dec 29, 2014

@dodikk Your subclass is not adding a single method to NSError

@ChristianKienle , neither the exception subclasses do in the languages like Java and C#. I think, having a hierarchy of errors is a good practice since you can use some help from the compiler to handle the error.

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

Here is how I use the error hierarchies :

  1. An application defines a protocol to build user friendly messages
@protocol SDErrorMessageBuilder <NSObject>

@required
-(NSString*)messageForLogin;
-(NSString*)messageForUserAction;
-(NSString*)messageForForceRefresh;

@end
  1. A category has been added to the error classes to conform the protocol from [1]
@interface NSError (DashboardErrors)<SDErrorMessageBuilder>
@end
  1. Errors from FMDatabase can now be handled separately
#import "FMError+DashboardErrors.h"

typedef std::map<NSInteger, __strong NSString*> SqlErrorsMap;

@implementation FMError (DashboardErrors)

-(SqlErrorsMap&)errorsMap
{
    static dispatch_once_t onceToken;
    static SqlErrorsMap result_;

    dispatch_once( &onceToken,
    ^{
        result_[ SQLITE_ERROR       ] = @"SQLITE_ERROR";
        result_[ SQLITE_INTERNAL    ] = @"SQLITE_INTERNAL";
        result_[ SQLITE_PERM        ] = @"SQLITE_PERM";
        result_[ SQLITE_ABORT       ] = @"SQLITE_ABORT";
        result_[ SQLITE_BUSY        ] = @"SQLITE_BUSY";
        result_[ SQLITE_LOCKED      ] = @"SQLITE_LOCKED";
        result_[ SQLITE_NOMEM       ] = @"SQLITE_NOMEM";
        result_[ SQLITE_READONLY    ] = @"SQLITE_READONLY";
        result_[ SQLITE_INTERRUPT   ] = @"SQLITE_INTERRUPT";
        result_[ SQLITE_IOERR       ] = @"SQLITE_IOERR";
        result_[ SQLITE_CORRUPT     ] = @"SQLITE_CORRUPT";
        result_[ SQLITE_NOTFOUND    ] = @"SQLITE_NOTFOUND";
        result_[ SQLITE_FULL        ] = @"SQLITE_FULL";
        result_[ SQLITE_CANTOPEN    ] = @"SQLITE_CANTOPEN";
        result_[ SQLITE_PROTOCOL    ] = @"SQLITE_PROTOCOL";
        result_[ SQLITE_EMPTY       ] = @"SQLITE_EMPTY";
        result_[ SQLITE_SCHEMA      ] = @"SQLITE_SCHEMA";
        result_[ SQLITE_TOOBIG      ] = @"SQLITE_TOOBIG";
        result_[ SQLITE_CONSTRAINT  ] = @"SQLITE_CONSTRAINT";
        result_[ SQLITE_MISMATCH    ] = @"SQLITE_MISMATCH";
        result_[ SQLITE_MISUSE      ] = @"SQLITE_MISUSE";
        result_[ SQLITE_NOLFS       ] = @"SQLITE_NOLFS";
        result_[ SQLITE_AUTH        ] = @"SQLITE_AUTH";
        result_[ SQLITE_FORMAT      ] = @"SQLITE_FORMAT";
        result_[ SQLITE_RANGE       ] = @"SQLITE_RANGE";
        result_[ SQLITE_NOTADB      ] = @"SQLITE_NOTADB";
        result_[ SQLITE_ROW         ] = @"SQLITE_ROW";
        result_[ SQLITE_DONE        ] = @"SQLITE_DONE";

    });

    return result_;
}

-(NSString*)messageForLogin
{
    return  [ self genericMessage ];
}

-(NSString*)messageForUserAction
{
    return  [ self genericMessage ];
}   

-(NSString*)messageForForceRefresh
{
    return  [ self genericMessage ];
}

-(NSString*)genericMessage
{
    NSInteger sqliteCode_ = [ self code ];
    NSString* result_ = [ self errorsMap ][ sqliteCode_ ];
    if ( nil == result_ )
    {
        result_ = @"FMDATABASE_UNKNOWN_ERROR";
    }

    return NSLocalizedString( result_, nil);
}

@end

Here is an example of CSV related errors handling (also derived from the NSError directly)

#import "CsvImportError+DashboardErrors.h"

@implementation CsvImportError (DashboardErrors)

-(NSString*)messageForLogin
{
    NSAssert( NO, @"Csv parser must not be launched during login" );
    return nil;
}

-(NSString*)messageForUserAction
{
    return NSLocalizedString( @"CSV_ERROR", nil );
}

-(NSString*)messageForForceRefresh
{
    return  [ self messageForUserAction ];
}

@end
Contributor

dodikk commented Dec 29, 2014

Here is how I use the error hierarchies :

  1. An application defines a protocol to build user friendly messages
@protocol SDErrorMessageBuilder <NSObject>

@required
-(NSString*)messageForLogin;
-(NSString*)messageForUserAction;
-(NSString*)messageForForceRefresh;

@end
  1. A category has been added to the error classes to conform the protocol from [1]
@interface NSError (DashboardErrors)<SDErrorMessageBuilder>
@end
  1. Errors from FMDatabase can now be handled separately
#import "FMError+DashboardErrors.h"

typedef std::map<NSInteger, __strong NSString*> SqlErrorsMap;

@implementation FMError (DashboardErrors)

-(SqlErrorsMap&)errorsMap
{
    static dispatch_once_t onceToken;
    static SqlErrorsMap result_;

    dispatch_once( &onceToken,
    ^{
        result_[ SQLITE_ERROR       ] = @"SQLITE_ERROR";
        result_[ SQLITE_INTERNAL    ] = @"SQLITE_INTERNAL";
        result_[ SQLITE_PERM        ] = @"SQLITE_PERM";
        result_[ SQLITE_ABORT       ] = @"SQLITE_ABORT";
        result_[ SQLITE_BUSY        ] = @"SQLITE_BUSY";
        result_[ SQLITE_LOCKED      ] = @"SQLITE_LOCKED";
        result_[ SQLITE_NOMEM       ] = @"SQLITE_NOMEM";
        result_[ SQLITE_READONLY    ] = @"SQLITE_READONLY";
        result_[ SQLITE_INTERRUPT   ] = @"SQLITE_INTERRUPT";
        result_[ SQLITE_IOERR       ] = @"SQLITE_IOERR";
        result_[ SQLITE_CORRUPT     ] = @"SQLITE_CORRUPT";
        result_[ SQLITE_NOTFOUND    ] = @"SQLITE_NOTFOUND";
        result_[ SQLITE_FULL        ] = @"SQLITE_FULL";
        result_[ SQLITE_CANTOPEN    ] = @"SQLITE_CANTOPEN";
        result_[ SQLITE_PROTOCOL    ] = @"SQLITE_PROTOCOL";
        result_[ SQLITE_EMPTY       ] = @"SQLITE_EMPTY";
        result_[ SQLITE_SCHEMA      ] = @"SQLITE_SCHEMA";
        result_[ SQLITE_TOOBIG      ] = @"SQLITE_TOOBIG";
        result_[ SQLITE_CONSTRAINT  ] = @"SQLITE_CONSTRAINT";
        result_[ SQLITE_MISMATCH    ] = @"SQLITE_MISMATCH";
        result_[ SQLITE_MISUSE      ] = @"SQLITE_MISUSE";
        result_[ SQLITE_NOLFS       ] = @"SQLITE_NOLFS";
        result_[ SQLITE_AUTH        ] = @"SQLITE_AUTH";
        result_[ SQLITE_FORMAT      ] = @"SQLITE_FORMAT";
        result_[ SQLITE_RANGE       ] = @"SQLITE_RANGE";
        result_[ SQLITE_NOTADB      ] = @"SQLITE_NOTADB";
        result_[ SQLITE_ROW         ] = @"SQLITE_ROW";
        result_[ SQLITE_DONE        ] = @"SQLITE_DONE";

    });

    return result_;
}

-(NSString*)messageForLogin
{
    return  [ self genericMessage ];
}

-(NSString*)messageForUserAction
{
    return  [ self genericMessage ];
}   

-(NSString*)messageForForceRefresh
{
    return  [ self genericMessage ];
}

-(NSString*)genericMessage
{
    NSInteger sqliteCode_ = [ self code ];
    NSString* result_ = [ self errorsMap ][ sqliteCode_ ];
    if ( nil == result_ )
    {
        result_ = @"FMDATABASE_UNKNOWN_ERROR";
    }

    return NSLocalizedString( result_, nil);
}

@end

Here is an example of CSV related errors handling (also derived from the NSError directly)

#import "CsvImportError+DashboardErrors.h"

@implementation CsvImportError (DashboardErrors)

-(NSString*)messageForLogin
{
    NSAssert( NO, @"Csv parser must not be launched during login" );
    return nil;
}

-(NSString*)messageForUserAction
{
    return NSLocalizedString( @"CSV_ERROR", nil );
}

-(NSString*)messageForForceRefresh
{
    return  [ self messageForUserAction ];
}

@end
@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

In case all errors have the same NSError type, there will be huge switch..case statements. All the error handling code will be in the same god class.

Contributor

dodikk commented Dec 29, 2014

In case all errors have the same NSError type, there will be huge switch..case statements. All the error handling code will be in the same god class.

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Dec 29, 2014

Contributor

@ChristianKienle , neither the exception subclasses do in the languages like Java and C#. I think, having a hierarchy of errors is a good practice since you can use some help from the compiler to handle the error.

Objective-C is not Java nor C#. If you think having a hierarchy of errors in the form of a class hierarchy then you are more or less the only person who thinks that way.

In case all errors have the same NSError type, there will be huge switch..case statements. All the error handling code will be in the same god class.

No. In Objective-C usually convention much more common than in other languages. You could simply say that if FMDB returns an error it will always be an FMDB related error. Meaning the domain will always be the one from FMDB.

The examples you gave could all be done without subclassing NSError.

Even if one would merge your pull request I would like to mention that the adoption of your custom error class is incomplete. For example:

- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error

The type of the error out parameter should be FMError or am I wrong?

That being said, you are using std::map instead of NSDictionary so you introduce a dependency to C++ just so that you are able to do that. You are obviously not comfortable with Objective-C and maybe this is where your misconceptions are coming from.

Contributor

ChristianKienle commented Dec 29, 2014

@ChristianKienle , neither the exception subclasses do in the languages like Java and C#. I think, having a hierarchy of errors is a good practice since you can use some help from the compiler to handle the error.

Objective-C is not Java nor C#. If you think having a hierarchy of errors in the form of a class hierarchy then you are more or less the only person who thinks that way.

In case all errors have the same NSError type, there will be huge switch..case statements. All the error handling code will be in the same god class.

No. In Objective-C usually convention much more common than in other languages. You could simply say that if FMDB returns an error it will always be an FMDB related error. Meaning the domain will always be the one from FMDB.

The examples you gave could all be done without subclassing NSError.

Even if one would merge your pull request I would like to mention that the adoption of your custom error class is incomplete. For example:

- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error

The type of the error out parameter should be FMError or am I wrong?

That being said, you are using std::map instead of NSDictionary so you introduce a dependency to C++ just so that you are able to do that. You are obviously not comfortable with Objective-C and maybe this is where your misconceptions are coming from.

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

Even if one would merge your pull request I would like to mention that the adoption of your custom error class is incomplete. For example:

Well, I might have missed a few usages. Or those were introduced after my patch

Contributor

dodikk commented Dec 29, 2014

Even if one would merge your pull request I would like to mention that the adoption of your custom error class is incomplete. For example:

Well, I might have missed a few usages. Or those were introduced after my patch

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

you are using std::map instead of NSDictionary so you introduce a dependency to C++ just so that you are able to do that

  1. That code won't go to the Fmdb
  2. I really hate the boxing thing that is done by nsdictionary. By boxing I mean converting the enum entry (plain old couple of bytes) to nsnumber (an object on the heap)
  3. I do know about the nsobject related optimizations. And still I don't find boxing being "ok"

You are obviously not comfortable with Objective-C and maybe this is where your misconceptions are coming from.

Please have some respect. Being not agree with my point of view does not give you a right to call it a "misconception", does it?

Contributor

dodikk commented Dec 29, 2014

you are using std::map instead of NSDictionary so you introduce a dependency to C++ just so that you are able to do that

  1. That code won't go to the Fmdb
  2. I really hate the boxing thing that is done by nsdictionary. By boxing I mean converting the enum entry (plain old couple of bytes) to nsnumber (an object on the heap)
  3. I do know about the nsobject related optimizations. And still I don't find boxing being "ok"

You are obviously not comfortable with Objective-C and maybe this is where your misconceptions are coming from.

Please have some respect. Being not agree with my point of view does not give you a right to call it a "misconception", does it?

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Dec 29, 2014

Contributor

That code won't go to the Fmdb

I know. I would not use that in my own code though.

I really hate the boxing thing that is done by nsdictionary. By boxing I mean converting the enum entry (plain old couple of bytes) to nsnumber (an object on the heap)

Is this the same hate that you have agains non-typed NSError instances? :)

I do know about the nsobject related optimizations. And still I don't find boxing being "ok"

Creating this error map is only done once (dispatch_once) and most of the stuff are constant strings which are not even created at runtime anymore. Querying a NSDictionary is O(1) (constant time) so it is very fast.

Contributor

ChristianKienle commented Dec 29, 2014

That code won't go to the Fmdb

I know. I would not use that in my own code though.

I really hate the boxing thing that is done by nsdictionary. By boxing I mean converting the enum entry (plain old couple of bytes) to nsnumber (an object on the heap)

Is this the same hate that you have agains non-typed NSError instances? :)

I do know about the nsobject related optimizations. And still I don't find boxing being "ok"

Creating this error map is only done once (dispatch_once) and most of the stuff are constant strings which are not even created at runtime anymore. Querying a NSDictionary is O(1) (constant time) so it is very fast.

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Dec 29, 2014

Contributor

Please have some respect. Being not agree with my point of view does not give you a right to call it a "misconception", does it?

I had no intention to be disrespectful. If I disagree I simply think that this implies that you have, in my mind, a misconception. And from your point of view I have a misconception as well haven't I? :)

Contributor

ChristianKienle commented Dec 29, 2014

Please have some respect. Being not agree with my point of view does not give you a right to call it a "misconception", does it?

I had no intention to be disrespectful. If I disagree I simply think that this implies that you have, in my mind, a misconception. And from your point of view I have a misconception as well haven't I? :)

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

Okay. Suppose, we launch a composite operation that may somehow fetch the data (either from the network or from the local storage on the device).

[ myApiFacade invokeOperation: op
               withCompletion: ^void(MyModelEntity* result, NSError* error) 
{
   if (null == result)
   {
       [ self.errorMessageProvider handleError: error ];
   }

   // process successful response
} ];

So, we can get an error from three different sources :

  • networking (tcp/http)
  • deserialization (xml/json/csv/... parsing)
  • local storage (fmdb/CoreData/...)

If we apply your approach where each operation returns an NSError, then error handling code will look like this :

-(void)handleError:(NSError*)error
{
     NSString* domain = error.domain;
     NSString* message = null;

     if ( [domain isEqualToString: MY_NETWORK_LIBRARY_DOMAIN ] )
     {
            switch (error.code)
             {
                    case 404:
                    {
                         message = "Not Found";
                         break;
                    }
                    case 500:
                    {
                         message = "Server Error";
                         break;
                    }
                    default:
                    {
                         message = "Unexpected Network Error";
                         break;
                    } 
             }
     }
     else if ( [domain isEqualToString: FMDB_ERROR_DOMAIN] )
     {
           switch (error.code)
           {
 ......
           }
     }

     // show alert with "message" variable
}

P.S. okay, a few dictionaries can make the code a bit nicer. However, we might need different messages for login and data fetching actions and end up with actually writing some error handling logic.


With my approach the code will look like this

-(void)handleError:(NSError*)error
{
    // polymorphism will do the job
    NSString* message = [ error messageForDataFetchingOperation ]; 

    // show alert with "message" variable
}
Contributor

dodikk commented Dec 29, 2014

Okay. Suppose, we launch a composite operation that may somehow fetch the data (either from the network or from the local storage on the device).

[ myApiFacade invokeOperation: op
               withCompletion: ^void(MyModelEntity* result, NSError* error) 
{
   if (null == result)
   {
       [ self.errorMessageProvider handleError: error ];
   }

   // process successful response
} ];

So, we can get an error from three different sources :

  • networking (tcp/http)
  • deserialization (xml/json/csv/... parsing)
  • local storage (fmdb/CoreData/...)

If we apply your approach where each operation returns an NSError, then error handling code will look like this :

-(void)handleError:(NSError*)error
{
     NSString* domain = error.domain;
     NSString* message = null;

     if ( [domain isEqualToString: MY_NETWORK_LIBRARY_DOMAIN ] )
     {
            switch (error.code)
             {
                    case 404:
                    {
                         message = "Not Found";
                         break;
                    }
                    case 500:
                    {
                         message = "Server Error";
                         break;
                    }
                    default:
                    {
                         message = "Unexpected Network Error";
                         break;
                    } 
             }
     }
     else if ( [domain isEqualToString: FMDB_ERROR_DOMAIN] )
     {
           switch (error.code)
           {
 ......
           }
     }

     // show alert with "message" variable
}

P.S. okay, a few dictionaries can make the code a bit nicer. However, we might need different messages for login and data fetching actions and end up with actually writing some error handling logic.


With my approach the code will look like this

-(void)handleError:(NSError*)error
{
    // polymorphism will do the job
    NSString* message = [ error messageForDataFetchingOperation ]; 

    // show alert with "message" variable
}
@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

If you think having a hierarchy of errors in the form of a class hierarchy then you are more or less the only person who thinks that way.

https://github.com/dropbox/Lists-ios/blob/master/Dropbox.framework/Headers/DBError.h#L75
Actually, looks like I'm not the only one.

Contributor

dodikk commented Dec 29, 2014

If you think having a hierarchy of errors in the form of a class hierarchy then you are more or less the only person who thinks that way.

https://github.com/dropbox/Lists-ios/blob/master/Dropbox.framework/Headers/DBError.h#L75
Actually, looks like I'm not the only one.

@taoeffect

This comment has been minimized.

Show comment
Hide comment
@taoeffect

taoeffect Dec 29, 2014

This is what I do personally:

 #define $error(CODE, MSG, ...) [NSError errorWithDomain:$app.localizedName code:CODE userInfo:$d(NSLocalizedDescriptionKey, $sprintf(MSG, ## __VA_ARGS__))]

Use like so:

*err = $error(errTECustomErr, NSLocalizedString(@"blah blah blah",@""));

Then show the alert with [err localizedDescription]. Don't see a need for an if block or for subclasses...

This is what I do personally:

 #define $error(CODE, MSG, ...) [NSError errorWithDomain:$app.localizedName code:CODE userInfo:$d(NSLocalizedDescriptionKey, $sprintf(MSG, ## __VA_ARGS__))]

Use like so:

*err = $error(errTECustomErr, NSLocalizedString(@"blah blah blah",@""));

Then show the alert with [err localizedDescription]. Don't see a need for an if block or for subclasses...

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

Then show the alert with [err localizedDescription].

@taoeffect Okay... What if your customer wants to see different messages during the login and during the "regular working process" ? I had exactly this behaviour back then.

Contributor

dodikk commented Dec 29, 2014

Then show the alert with [err localizedDescription].

@taoeffect Okay... What if your customer wants to see different messages during the login and during the "regular working process" ? I had exactly this behaviour back then.

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

@taoeffect , I guess, @ChristianKienle will call this PHP-style approach a "misconception" just like he did to my C++ (C#/Java) style )))

Contributor

dodikk commented Dec 29, 2014

@taoeffect , I guess, @ChristianKienle will call this PHP-style approach a "misconception" just like he did to my C++ (C#/Java) style )))

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Dec 29, 2014

Contributor

https://github.com/dropbox/Lists-ios/blob/master/Dropbox.framework/Headers/DBError.h#L75
Actually, looks like I'm not the only one.

You are not the only one but almost nobody else is doing it this way. I mean have a look at Cocoa/Cocoa Touch. There are (ALMOST) NO custom NSError subclasses exposed. Everything is NSError.

// polymorphism will do the job

So every NSError subclass has to implement messageForDataFetchingOperation?

Contributor

ChristianKienle commented Dec 29, 2014

https://github.com/dropbox/Lists-ios/blob/master/Dropbox.framework/Headers/DBError.h#L75
Actually, looks like I'm not the only one.

You are not the only one but almost nobody else is doing it this way. I mean have a look at Cocoa/Cocoa Touch. There are (ALMOST) NO custom NSError subclasses exposed. Everything is NSError.

// polymorphism will do the job

So every NSError subclass has to implement messageForDataFetchingOperation?

@taoeffect

This comment has been minimized.

Show comment
Hide comment
@taoeffect

taoeffect Dec 29, 2014

@taoeffect Okay... What if your customer wants to see different messages during the login and during the "regular working process" ? I had exactly this behaviour back then.

I don't know what situation you're referring to... but simply send different $errors?

@taoeffect Okay... What if your customer wants to see different messages during the login and during the "regular working process" ? I had exactly this behaviour back then.

I don't know what situation you're referring to... but simply send different $errors?

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Dec 29, 2014

Contributor

Okay. Suppose, we launch a composite operation that may somehow fetch the data (either from the network or from the local storage on the device).

In this case you would not pass the exact same error to the caller. Instead you would create a new NSError with your domain/with the domain of the component which does the composite operation and pack the original error inside the newly created error as an underlying error.

Contributor

ChristianKienle commented Dec 29, 2014

Okay. Suppose, we launch a composite operation that may somehow fetch the data (either from the network or from the local storage on the device).

In this case you would not pass the exact same error to the caller. Instead you would create a new NSError with your domain/with the domain of the component which does the composite operation and pack the original error inside the newly created error as an underlying error.

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

"Wrapping NSErrors" approach is ok. Just like "wrapping exceptions" in languages that do have them and let the developer use them. But still, exception hierarchies are an industry standard as well as having an innerException property is.

Why can't we use the same approach for NSError?

The benefits :
  1. No breaking changes
  2. At least one user is happier
  3. Public API remains the same as before (other users will not notice the change since they rely on "domain" and "code" properties).
The cons :
  1. A little bit of effort is required
    • The root error class should be created and exposed in the umbrella header.
    • Maybe some unit tests relying on "isMemberOfClass:" have to be fixed
  2. That's it.
Contributor

dodikk commented Dec 29, 2014

"Wrapping NSErrors" approach is ok. Just like "wrapping exceptions" in languages that do have them and let the developer use them. But still, exception hierarchies are an industry standard as well as having an innerException property is.

Why can't we use the same approach for NSError?

The benefits :
  1. No breaking changes
  2. At least one user is happier
  3. Public API remains the same as before (other users will not notice the change since they rely on "domain" and "code" properties).
The cons :
  1. A little bit of effort is required
    • The root error class should be created and exposed in the umbrella header.
    • Maybe some unit tests relying on "isMemberOfClass:" have to be fixed
  2. That's it.
@taoeffect

This comment has been minimized.

Show comment
Hide comment
@taoeffect

taoeffect Dec 29, 2014

I still don't understand why you need any hierarchies and why $error(errTECustomErr, NSLocalizedString(@"blah blah blah",@"")); isn't enough... w/e. Back to work for me.

I still don't understand why you need any hierarchies and why $error(errTECustomErr, NSLocalizedString(@"blah blah blah",@"")); isn't enough... w/e. Back to work for me.

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

I still don't understand why you need any hierarchies

@taoeffect , I am just trying to follow the best practice such as "Layered architecture", SOLID, etc. The Nslocalizedztring() macro introduces an implicit dependency on NSBundle.mainBundle which is not cool (IMHO).

Contributor

dodikk commented Dec 29, 2014

I still don't understand why you need any hierarchies

@taoeffect , I am just trying to follow the best practice such as "Layered architecture", SOLID, etc. The Nslocalizedztring() macro introduces an implicit dependency on NSBundle.mainBundle which is not cool (IMHO).

@taoeffect

This comment has been minimized.

Show comment
Hide comment
@taoeffect

taoeffect Dec 29, 2014

@taoeffect , I am just trying to follow the best practice such as "Layered architecture", SOLID, etc. The Nslocalizedztring() macro introduces an implicit dependency on NSBundle.mainBundle which is not cool (IMHO).

If you want to follow best practices, you should be using NSLocalizedString from the beginning in all your Objective-C programs (*that you intend to ship), and avoid unnecessary complexity as much as possible.

@taoeffect , I am just trying to follow the best practice such as "Layered architecture", SOLID, etc. The Nslocalizedztring() macro introduces an implicit dependency on NSBundle.mainBundle which is not cool (IMHO).

If you want to follow best practices, you should be using NSLocalizedString from the beginning in all your Objective-C programs (*that you intend to ship), and avoid unnecessary complexity as much as possible.

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Dec 29, 2014

Contributor

If you want to follow best practices, you should be using NSLocalizedString from the beginning in all your Objective-C programs, and avoid unnecessary complexity as much as possible.

Use singletons, fuck the architecture and industry's (computer science) standards, "programming, motherfucker"...
Thanks, man.

Contributor

dodikk commented Dec 29, 2014

If you want to follow best practices, you should be using NSLocalizedString from the beginning in all your Objective-C programs, and avoid unnecessary complexity as much as possible.

Use singletons, fuck the architecture and industry's (computer science) standards, "programming, motherfucker"...
Thanks, man.

@taoeffect

This comment has been minimized.

Show comment
Hide comment
@taoeffect

taoeffect Dec 29, 2014

Use singletons

That's just the way it works in Cocoa. If you ever want your programs to be used by people who speak other languages, that's what ya gotta do.

"programming, motherfucker"...

Да, товарищ. 👍

Use singletons

That's just the way it works in Cocoa. If you ever want your programs to be used by people who speak other languages, that's what ya gotta do.

"programming, motherfucker"...

Да, товарищ. 👍

@ccgus ccgus closed this Dec 29, 2014

@AlexDenisov

This comment has been minimized.

Show comment
Hide comment
@AlexDenisov

AlexDenisov Dec 30, 2014

@ChristianKienle, sorry, but it sounds weird, very weird:

introduce a dependency to C++

I guess you know that Cocoa[Touch] stack built using ObjC++/C++.
I guess you know that ObjC runtime built using ObjC++/C++.

So, what do you mean by 'dependency to C++'?

If you think having a hierarchy of errors in the form of a class hierarchy then you are more or less the only person who thinks that way.

If you or your friends don't use some approach it doesn't mean that community doesn't use it at all. Personally, I use similar approach and, IIRC, it calls inversion of control.

You are obviously not comfortable with Objective-C

You don't have to be an asshole to prove your domain knowledge.

@ChristianKienle, sorry, but it sounds weird, very weird:

introduce a dependency to C++

I guess you know that Cocoa[Touch] stack built using ObjC++/C++.
I guess you know that ObjC runtime built using ObjC++/C++.

So, what do you mean by 'dependency to C++'?

If you think having a hierarchy of errors in the form of a class hierarchy then you are more or less the only person who thinks that way.

If you or your friends don't use some approach it doesn't mean that community doesn't use it at all. Personally, I use similar approach and, IIRC, it calls inversion of control.

You are obviously not comfortable with Objective-C

You don't have to be an asshole to prove your domain knowledge.

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Dec 30, 2014

Contributor

I guess you know that Cocoa[Touch] stack built using ObjC++/C++.
I guess you know that ObjC runtime built using ObjC++/C++.

Yes I know that. But this is an implementation detail of Cocoa(Touch) + the runtime. Only because something that you rely on is using C++ internally does not mean that it is a good idea to switch from Objective-C to Objective-C++ just to be able to use std::map instead of NSDictionary (which is perfectly fine).

So, what do you mean by 'dependency to C++'?

By "dependency to C++" I mean that you should not introduce a direct dependency to C++ without thinking hard about it. It just feels strange. Of course: In the end almost everything depends on C++: Also Ruby and other scripting languages do. But you would always prefer the Ruby data types over the one from C++ wouldn't you?

If you or your friends don't use some approach it doesn't mean that community doesn't use it at all. Personally, I use similar approach and, IIRC, it calls inversion of control.

I have said it already: It is used by more than one person but not by the majority. Especially not in the way this pull request implements the suggested feature. FMError (the NSError subclass) does not add any functionality to base class nor is FMError exposed anywhere in FMDB's public interface. Thus you do not gain anything.

You don't have to be an asshole to prove your domain knowledge.

Can't we all just hug each other? Let me quote myself from earlier:

I had no intention to be disrespectful. If I disagree I simply think that this implies that you have, in my mind, a misconception. And from your point of view I have a misconception as well haven't I? :)

This remark is still true. Just by pointing out a (in my mind) misconception does not make me an "asshole".

Contributor

ChristianKienle commented Dec 30, 2014

I guess you know that Cocoa[Touch] stack built using ObjC++/C++.
I guess you know that ObjC runtime built using ObjC++/C++.

Yes I know that. But this is an implementation detail of Cocoa(Touch) + the runtime. Only because something that you rely on is using C++ internally does not mean that it is a good idea to switch from Objective-C to Objective-C++ just to be able to use std::map instead of NSDictionary (which is perfectly fine).

So, what do you mean by 'dependency to C++'?

By "dependency to C++" I mean that you should not introduce a direct dependency to C++ without thinking hard about it. It just feels strange. Of course: In the end almost everything depends on C++: Also Ruby and other scripting languages do. But you would always prefer the Ruby data types over the one from C++ wouldn't you?

If you or your friends don't use some approach it doesn't mean that community doesn't use it at all. Personally, I use similar approach and, IIRC, it calls inversion of control.

I have said it already: It is used by more than one person but not by the majority. Especially not in the way this pull request implements the suggested feature. FMError (the NSError subclass) does not add any functionality to base class nor is FMError exposed anywhere in FMDB's public interface. Thus you do not gain anything.

You don't have to be an asshole to prove your domain knowledge.

Can't we all just hug each other? Let me quote myself from earlier:

I had no intention to be disrespectful. If I disagree I simply think that this implies that you have, in my mind, a misconception. And from your point of view I have a misconception as well haven't I? :)

This remark is still true. Just by pointing out a (in my mind) misconception does not make me an "asshole".

@AlexDenisov

This comment has been minimized.

Show comment
Hide comment
@AlexDenisov

AlexDenisov Dec 30, 2014

does not mean that it is a good idea to switch from Objective-C to Objective-C++ just to be able to use std::map instead of NSDictionary

it depends, I switched to std::map here and got a serious speed improvement, just by getting rid of boxing/unboxing.

But you would always prefer the Ruby data types over the one from C++ wouldn't you?

Ruby doesn't have such a great interoperability with C++, while ObjC does. I'm not saying that you should use C++ everywhere, but you're free to use it if it makes sense. Most of developers have bias regarding C++ just because of... I don't know why, actually. They just hate C++ without any good reason.

It is used by more than one person but not by the majority

Our majority uses singletons everywhere (which are global variables, aren't they?) just to solve all the architectural issues, that's why I can't rely on majority. I do prefer to rely on experience of more mature communities, such as C++ or Java.

Can't we all just hug each other?

I didn't want to offend you, just wanted to emphasise that we, as a community, can be more polite and friendly and should not blame for nothing.
:hug: 😄

does not mean that it is a good idea to switch from Objective-C to Objective-C++ just to be able to use std::map instead of NSDictionary

it depends, I switched to std::map here and got a serious speed improvement, just by getting rid of boxing/unboxing.

But you would always prefer the Ruby data types over the one from C++ wouldn't you?

Ruby doesn't have such a great interoperability with C++, while ObjC does. I'm not saying that you should use C++ everywhere, but you're free to use it if it makes sense. Most of developers have bias regarding C++ just because of... I don't know why, actually. They just hate C++ without any good reason.

It is used by more than one person but not by the majority

Our majority uses singletons everywhere (which are global variables, aren't they?) just to solve all the architectural issues, that's why I can't rely on majority. I do prefer to rely on experience of more mature communities, such as C++ or Java.

Can't we all just hug each other?

I didn't want to offend you, just wanted to emphasise that we, as a community, can be more polite and friendly and should not blame for nothing.
:hug: 😄

@taoeffect

This comment has been minimized.

Show comment
Hide comment
@taoeffect

taoeffect Dec 31, 2014

They just hate C++ without any good reason.

There's plenty of good reason to hate C++ (mostly because it breeds security holes and is an ugly beast of a language that results in unmaintainable code bases). Lots of good rants out there if you want to search, but that's the skinny of it.

(I put a jihad on the language a few months back. 😉 )

They just hate C++ without any good reason.

There's plenty of good reason to hate C++ (mostly because it breeds security holes and is an ugly beast of a language that results in unmaintainable code bases). Lots of good rants out there if you want to search, but that's the skinny of it.

(I put a jihad on the language a few months back. 😉 )

@dodikk

This comment has been minimized.

Show comment
Hide comment
@dodikk

dodikk Feb 4, 2015

Contributor

@ChristianKienle I've just noticed that apple actually uses class hierarchies.

P.S. I confessed this patch would not be merged. Just thought one might find this fact interesting.

Contributor

dodikk commented Feb 4, 2015

@ChristianKienle I've just noticed that apple actually uses class hierarchies.

P.S. I confessed this patch would not be merged. Just thought one might find this fact interesting.

@ChristianKienle

This comment has been minimized.

Show comment
Hide comment
@ChristianKienle

ChristianKienle Apr 1, 2015

Contributor

@dodikk 2 in a million

Contributor

ChristianKienle commented Apr 1, 2015

@dodikk 2 in a million

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment