Search code examples
iosdbaccess

DBAccess - Setting a custom class object as DBObject property


I am a beginner in both iOS development and DBAccess framework and I got a problem which is probably real easy to solve but I do not really know how to present it.

My DBObject class extention:

Header

//FavouriteProduct.h

#import <UIKit/UIKit.h>
#import <DBAccess/DBAccess.h>
#import "Product.h"

@interface FavouriteProduct : DBObject
@property Product *product;
@property NSString *userID;
@property NSString *productID;
@end

Implementation

//FavouriteProduct.m

#import "FavouriteProduct.h"
#import "Product.h"

@implementation FavouriteProduct
@dynamic productID;
@dynamic userID;
@dynamic product;
@end

Product.m file:

@implementation Product{
NSString *id;
float price;
float discount;
NSDictionary *ownerProduct;
NSDictionary *originalDict;
}

//I create my Product objects from JSON
-(instancetype)initWithJSONDictionary:(NSDictionary *)JSONDict{
  self = [super init];
  if (self) {
      [self setValuesForKeysWithDictionary:JSONDict];
  }
  originalDict = [[NSDictionary alloc] initWithDictionary:JSONDict];
  return self;
}
// + some more getter methods

Product.h file contains interface declaration for getter methods and the above initWithJSONDictionary method.

I tried using the following code to set product property:

FavouriteProduct *fav = [FavouriteProduct new];
fav.product = self.product; //self.product is an object of Product class
fav.productID = self.product.getProductID;
[fav commit];

I get this error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FavouriteProduct setProduct:]: unrecognized selector sent to instance 0x16e5bf10'

I also tried it this way:

FavouriteProduct *fav = [FavouriteProduct new];
fav.product = [[Product alloc]initWithJSONDictionary:self.product.getOriginalDictionary]; //set a Product object from NSDictionary
fav.productID = self.product.getProductID;
[fav commit];

I get the same error.

What am I missing? Thank you for your time.


Solution

  • Because the properties in your FavouriteProduct are (I am guessing) implemented as @dynamic, there is no automatically generated get/set methods generated by objc. So when the assignment happens, an exception is thrown as it tries to call setProduct:

    Normally DBAccess will create these for you, but it can only do these for known types.

    DBAccess inspects the class and allocates a storage type for any properties it sees defined as @dynamic, the list is comprehensive in it's support but it can't persist custom classes that it has no idea how to store/reconstruct them.

    Now, what I would have expected it to do, is create a BLOB column and try to store it anyway, and then raise an exception saying there was no support for NSKeyedArchiver for class 'xyz'. I will look into why this is not happening.

    How to get it working?

    Well, you could make the Product class a DBObject, which would enable DBAccess to know what to do with it and how to store it. Or you could leave the .product property as @sythensized, which would mean that DBAccess would not try to store it. Then add an additional property such as, .productDictionary and store the NSDictionary that was used to create it, and every time you get the product property, reconstruct the full object and cache it somewhere.

    For reference, these are the types of object that DBAccess can persist with no additional extensions.

    NSNumber, NSString, NSImage/UIImage, NSArray, NSDictionary, NSDate, int, bool, long, float, char, short, long long, uchar, ushort, ulong, ulong long, double, char*, NSURL, NSData, NSMutableData, NSMutableArray, NSMutableDictionary, DBObject derived classes, NSObject (where the class implements an NSKeyedArchiver), int64, uint64.

    If you are storing things within an NSArray or NSDictionary properties, these can only contain simple data types and not custom types. So Arrays of NSNumbers, dates, images, dictionaries and other base types are fine. But arrays and dictionaries that contain your own custom types will fail to persist.

    I hope this helps,

    Adrian