Skip to content

Loading…

Added a base class for all FMDatabase errors. #110

Closed
wants to merge 1 commit into from

5 participants

@dodikk

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
Owner

Not sure why this is needed?

@dodikk

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

@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

@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

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

2) A category has been added to the error classes to conform the protocol from [1]

@interface NSError (DashboardErrors)<SDErrorMessageBuilder>
@end

3) 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

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

@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

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

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

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

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

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

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 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

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

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

@ChristianKienle

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

@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

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

"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

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

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

@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

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

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"...

Да, товарищ. :+1:

@ccgus ccgus closed this
@AlexDenisov

@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

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

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: :smile:

@taoeffect

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. :wink: )

@dodikk

@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

@dodikk 2 in a million

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 10, 2013
  1. @dodikk

    Added a base class for all FMDatabase errors. Fixed error domain for …

    dodikk committed
    …SQL validation function
Showing with 55 additions and 19 deletions.
  1. +13 −0 FMError.h
  2. +13 −0 FMError.m
  3. +22 −15 fmdb.xcodeproj/project.pbxproj
  4. +5 −3 src/FMDatabase.m
  5. +2 −1 src/FMDatabaseAdditions.m
View
13 FMError.h
@@ -0,0 +1,13 @@
+//
+// FMError.h
+// fmdb
+//
+// Created by Oleksandr Dodatko on 1/10/13.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+@interface FMError : NSError
+
+@end
View
13 FMError.m
@@ -0,0 +1,13 @@
+//
+// FMError.m
+// fmdb
+//
+// Created by Oleksandr Dodatko on 1/10/13.
+//
+//
+
+#import "FMError.h"
+
+@implementation FMError
+
+@end
View
37 fmdb.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 7E98261F169F252F00013023 /* FMError.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E98261D169F252F00013023 /* FMError.h */; };
+ 7E982620169F252F00013023 /* FMError.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E98261E169F252F00013023 /* FMError.m */; };
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
8DD76F9F0486AA7600D96B5E /* fmdb.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* fmdb.1 */; };
CC47A00F148581E9002CCDAB /* FMDatabaseQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = CC47A00D148581E9002CCDAB /* FMDatabaseQueue.h */; };
@@ -51,6 +53,8 @@
/* Begin PBXFileReference section */
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
32A70AAB03705E1F00C91783 /* fmdb_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmdb_Prefix.pch; sourceTree = "<group>"; };
+ 7E98261D169F252F00013023 /* FMError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMError.h; sourceTree = "<group>"; };
+ 7E98261E169F252F00013023 /* FMError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMError.m; sourceTree = "<group>"; };
8DD76FA10486AA7600D96B5E /* fmdb */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fmdb; sourceTree = BUILT_PRODUCTS_DIR; };
C6859EA3029092ED04C91782 /* fmdb.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = fmdb.1; sourceTree = "<group>"; };
CC47A00D148581E9002CCDAB /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabaseQueue.h; path = src/FMDatabaseQueue.h; sourceTree = "<group>"; };
@@ -128,6 +132,8 @@
CC9E4EB813B31188005F9210 /* FMDatabasePool.m */,
32A70AAB03705E1F00C91783 /* fmdb_Prefix.pch */,
CCC24EBE0A13E34D00A6D3E3 /* fmdb.m */,
+ 7E98261D169F252F00013023 /* FMError.h */,
+ 7E98261E169F252F00013023 /* FMError.m */,
);
name = Source;
sourceTree = "<group>";
@@ -171,15 +177,16 @@
EE42910912B42FD00088BD94 /* FMResultSet.h in Headers */,
CC9E4EBA13B31188005F9210 /* FMDatabasePool.h in Headers */,
CC47A00F148581E9002CCDAB /* FMDatabaseQueue.h in Headers */,
+ 7E98261F169F252F00013023 /* FMError.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
- 8DD76F960486AA7600D96B5E /* fmdb */ = {
+ 8DD76F960486AA7600D96B5E /* fmdb-demo */ = {
isa = PBXNativeTarget;
- buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "fmdb" */;
+ buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "fmdb-demo" */;
buildPhases = (
8DD76F990486AA7600D96B5E /* Sources */,
8DD76F9B0486AA7600D96B5E /* Frameworks */,
@@ -189,7 +196,7 @@
);
dependencies = (
);
- name = fmdb;
+ name = "fmdb-demo";
productInstallPath = "$(HOME)/bin";
productName = fmdb;
productReference = 8DD76FA10486AA7600D96B5E /* fmdb */;
@@ -234,7 +241,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
- 8DD76F960486AA7600D96B5E /* fmdb */,
+ 8DD76F960486AA7600D96B5E /* fmdb-demo */,
EE4290EE12B42F870088BD94 /* FMDB */,
);
};
@@ -263,6 +270,7 @@
EE42910A12B42FD20088BD94 /* FMResultSet.m in Sources */,
CC9E4EBB13B31188005F9210 /* FMDatabasePool.m in Sources */,
CC47A011148581E9002CCDAB /* FMDatabaseQueue.m in Sources */,
+ 7E982620169F252F00013023 /* FMError.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -282,9 +290,7 @@
GCC_PREFIX_HEADER = fmdb_Prefix.pch;
INSTALL_PATH = "$(HOME)/bin";
LIBRARY_SEARCH_PATHS = "$(LIBRARY_SEARCH_PATHS)";
- MACOSX_DEPLOYMENT_TARGET = 10.7;
PRODUCT_NAME = fmdb;
- SDKROOT = macosx;
};
name = Debug;
};
@@ -300,9 +306,7 @@
GCC_PREFIX_HEADER = fmdb_Prefix.pch;
INSTALL_PATH = "$(HOME)/bin";
LIBRARY_SEARCH_PATHS = "$(LIBRARY_SEARCH_PATHS)";
- MACOSX_DEPLOYMENT_TARGET = 10.7;
PRODUCT_NAME = fmdb;
- SDKROOT = macosx;
};
name = Release;
};
@@ -310,26 +314,22 @@
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
- GCC_C_LANGUAGE_STANDARD = c99;
- GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_MISSING_PARENTHESES = YES;
GCC_WARN_PEDANTIC = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.7;
};
name = Debug;
};
1DEB927A08733DD40010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- GCC_C_LANGUAGE_STANDARD = c99;
- GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.7;
};
name = Release;
};
@@ -337,10 +337,14 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = FMDB;
+ SDKROOT = iphoneos;
};
name = Debug;
};
@@ -348,9 +352,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = FMDB;
+ SDKROOT = iphoneos;
ZERO_LINK = NO;
};
name = Release;
@@ -358,7 +365,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
- 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "fmdb" */ = {
+ 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "fmdb-demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB927508733DD40010E9CD /* Debug */,
View
8 src/FMDatabase.m
@@ -2,6 +2,8 @@
#import "unistd.h"
#import <objc/runtime.h>
+#import "FMError.h"
+
@interface FMDatabase ()
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
@@ -294,9 +296,9 @@ -(NSError*)errorWithMessage:( NSString* )message_
NSDictionary* errorMessage_ = [ NSDictionary dictionaryWithObject: message_
forKey: NSLocalizedDescriptionKey];
- return [NSError errorWithDomain:@"FMDatabase"
- code:sqlite3_errcode(_db)
- userInfo:errorMessage_];
+ return [ FMError errorWithDomain:@"FMDatabase"
+ code:sqlite3_errcode(_db)
+ userInfo:errorMessage_];
}
-(NSError*)lastError
View
3 src/FMDatabaseAdditions.m
@@ -8,6 +8,7 @@
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
+#import "FMError.h"
@interface FMDatabase (PrivateStuff)
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
@@ -139,7 +140,7 @@ - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error {
else if (rc != SQLITE_OK) {
validationSucceeded = NO;
if (error) {
- *error = [NSError errorWithDomain:NSCocoaErrorDomain
+ *error = [FMError errorWithDomain: @"FMDatabase"
code:[self lastErrorCode]
userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage]
forKey:NSLocalizedDescriptionKey]];
Something went wrong with that request. Please try again.