How to avoid excessive hard coding when programatically inserting Core Data entity ?

  • There are many times that no code is required to use Core Data.
    Unfortunately, as far as I have ever determined, there are cases
    where code is needed, and it is icky hard coded stuff too.

    I have been using Core Data for quite a while now, and I have just
    persevered through the icky parts.  I am hopeful that better patterns
    exist though.

    Consider for example a drawing program that uses Core Data to store
    graphical elements to draw.  I have a bunch of VT4Object (or
    subclass) entities that store points, images, font identifiers,
    strings, etc.  I then have VT4ObjectReference entities.
    VT4ObjectReference has a to-one relationship with VT4Object which in
    turn has a to-many reciprocal relationship.  The reason for
    VT4ObjectReference is that one point object might be used in several
    graphical objects.  Several graphical objects might draw the same
    image.  Only one instance of each particular point or the image
    exists in the data store, but multiple graphics may reference the
    same point or image.

    So, one particular kind of graphic is the VT4ImageGraphic whose job
    is to draw an image with a size at a position.  The image, size, and
    position are all VT4ObjectReferences which relationships with the
    actual point, size, and image entities.

    Now, when the user drags and drops an image into the drawing
    application, the VT4ImageGraphic
    +insertWithImage:atPoint:inManagedObjectContext: is called and the
    VT4ImageGraphic as well as all of the related entities have to be
    created and inserted into a managed object context.  See the code
    below.  Does anyone have suggestions for how to reduce the amount of
    hard coding going on ?

    @implementation VT4ImageGraphic (VT4Utility)

    + (void)insertWithImage:(NSImage *)anImage atPoint:(NSPoint)aPoint
    inManagedObjectContext:(NSManagedObjectContext *)aManagedObjectContext
    {
        if(nil != anImage)
        {
          NSAssert(nil != aManagedObjectContext, @"Invalid managed
    object context");

          // Create an entity to store the position of the image
          VT4PointVector      *newPoint = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4PointVector"

    inManagedObjectContext:aManagedObjectContext];
          [newPoint setX:[NSNumber numberWithFloat:aPoint.x]];
          [newPoint setY:[NSNumber numberWithFloat:aPoint.y]];

          // Create an entity to store the desired size of the image
          VT4Vector            *newSize = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4Vector"

    inManagedObjectContext:aManagedObjectContext];
          [newSize setX:[NSNumber numberWithFloat:[anImage size].width]];
          [newSize setY:[NSNumber numberWithFloat:[anImage size].height]];

          // Create an entity to store the image data
          VT4Image            *newImage = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4Image"

    inManagedObjectContext:aManagedObjectContext];
          [newImage setImageData:[anImage TIFFRepresentation]];

          // Create an entity to draw the image with the size at the
    position
          VT4ImageGraphic      *newImageGraphic = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4ImageGraphic"

          inManagedObjectContext:aManagedObjectContext];

          // Create an entities to reference the image data, position,
    and size entities
          // and establish the relationships between the entities
          VT4ObjectReference  *imageReference = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4ObjectReference"

        inManagedObjectContext:aManagedObjectContext];
          [imageReference setObject:newImage];
          [imageReference setName:@"image"];
          [imageReference setOwner:newImageGraphic];

          VT4ObjectReference  *positionReference = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4ObjectReference"

            inManagedObjectContext:aManagedObjectContext];
          [positionReference setObject:newPoint];
          [positionReference setName:@"position"];
          [positionReference setOwner:newImageGraphic];

          VT4ObjectReference  *sizeReference = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4ObjectReference"

            inManagedObjectContext:aManagedObjectContext];
          [sizeReference setObject:newSize];
          [sizeReference setName:@"size"];
          [sizeReference setOwner:newImageGraphic];
        }
    }

    @end
  • On Sep 16, 2007, at 7:38 AM, Erik Buck wrote:

    > Does anyone have suggestions for how to reduce the amount of hard
    > coding going on ?

    Not knowing exactly what you mean by hard coding in this context...

    > @implementation VT4ImageGraphic (VT4Utility)

    + (VT4PointVector*) pointVectorWithPoint:(NSPoint)aPoint inContext:
    (NSManagedObjectContext*)
    {
          // Create an entity to store the position of the image
          VT4PointVector      *newPoint = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4PointVector"

    inManagedObjectContext:aManagedObjectContext];
          [newPoint setX:[NSNumber numberWithFloat:aPoint.x]];
          [newPoint setY:[NSNumber numberWithFloat:aPoint.y]];
    }

    etc.

    > + (void)insertWithImage:(NSImage *)anImage atPoint:(NSPoint)aPoint
    > inManagedObjectContext:(NSManagedObjectContext *)aManagedObjectContext
    > {
    > if(nil != anImage)
    > {
    > NSAssert(nil != aManagedObjectContext, @"Invalid managed
    > object context");

    VT4PointVector* point = [self pointVectorWithPoint:aPoint inContext:
    aManagedObjectContext];

    etc.
  • On Sep 16, 2007, at 8:01 AM, Shawn Erickson wrote:

    >
    > On Sep 16, 2007, at 7:38 AM, Erik Buck wrote:
    >
    >> Does anyone have suggestions for how to reduce the amount of hard
    >> coding going on ?
    >
    > Not knowing exactly what you mean by hard coding in this context...
    >
    >> @implementation VT4ImageGraphic (VT4Utility)
    >
    > + (VT4PointVector*) pointVectorWithPoint:(NSPoint)aPoint inContext:
    > (NSManagedObjectContext*)
    > {
    > // Create an entity to store the position of the image
    > VT4PointVector      *newPoint = [NSEntityDescription
    > insertNewObjectForEntityForName:@"VT4PointVector"
    > i
    > nManagedObjectContext:aManagedObjectContext];
    > [newPoint setX:[NSNumber numberWithFloat:aPoint.x]];
    > [newPoint setY:[NSNumber numberWithFloat:aPoint.y]];
    return newPoint;
    > }

    I should also note that since you appear to be subclassing
    NSManagedObject to provide named accessors for your various objects
    consider having those subclasses provide methods like the above. This
    would hide the entity aspects of things from clients.

    -Shawn
  • On Sep 16, 2007, at 7:38 AM, Erik Buck wrote:
    >
    > Consider for example a drawing program that uses Core Data to store
    > graphical elements to draw.  I have a bunch of VT4Object (or
    > subclass) entities that store points, images, font identifiers,
    > strings, etc.  I then have VT4ObjectReference entities.
    > VT4ObjectReference has a to-one relationship with VT4Object which
    > in turn has a to-many reciprocal relationship.  The reason for
    > VT4ObjectReference is that one point object might be used in
    > several graphical objects.  Several graphical objects might draw
    > the same image.  Only one instance of each particular point or the
    > image exists in the data store, but multiple graphics may reference
    > the same point or image.

    One thing I notice, is that your code doesn't look like it attempts
    to do any of the point/image uniquing you mention here.

    > So, one particular kind of graphic is the VT4ImageGraphic whose job
    > is to draw an image with a size at a position.  The image, size,
    > and position are all VT4ObjectReferences which relationships with
    > the actual point, size, and image entities.
    >
    > Does anyone have suggestions for how to reduce the amount of hard
    > coding going on ?

    I'd go a step farther than Shawn's suggestion of factory methods for
    the VT4Objects, and have a VT4Graphic superclass of VT4ImageGraphic
    (which you probably already have), which builds both the objects and
    the object references. VT4Graphic would have a user defaults-style
    API with methods like:

    - (void)setPoint:(NSPoint)aPoint withName:(NSString *)name;
    - (void)setSize:(NSSize)aSize withName:(NSString *)name;
    - (void)setImageData:(NSData *)data withName:(NSString *)name;

    Example implementation:

    - (void)_setObject:(VT4Object *)object withName:(NSString *)name;
    {
          VT4ObjectReference  *positionReference = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4ObjectReference"
    inManagedObjectContext:[self managedObjectContext]];
          [positionReference setObject:object];
          [positionReference setName:name];
          [positionReference setOwner:self];
    }

    - (void)setPoint:(NSPoint)aPoint withName:(NSString *)name;
    {
          VT4PointVector      *newPoint = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4PointVector"

    inManagedObjectContext:[self managedObjectContext]];
          [newPoint setX:[NSNumber numberWithFloat:aPoint.x]];
          [newPoint setY:[NSNumber numberWithFloat:aPoint.y]];
          [self _setObject:newPoint withName:name];
    }

    Then your utility method is reduced to:

    + (void)insertWithImage:(NSImage *)anImage atPoint:(NSPoint)aPoint
    inManagedObjectContext:(NSManagedObjectContext *)aManagedObjectContext
    {
        if(nil != anImage)
        {
          NSAssert(nil != aManagedObjectContext, @"Invalid managed
    object context");

          // Create an entity to draw the image with the size at the
    position
          VT4ImageGraphic      *newImageGraphic = [NSEntityDescription
    insertNewObjectForEntityForName:@"VT4ImageGraphic"

          inManagedObjectContext:aManagedObjectContext];
          [newImageGraphic setPoint:aPoint withName:@"position"];
          [newImageGraphic setSize:[anImage size] withName:@"size"];
          [newImageGraphic setImageData:[anImage TIFFRepresentation]
    withName:@"image"];
        }
    }

    Hope this helps,
    - Greg
  • On Sep 16, 2007, at 12:18 PM, Greg Titus wrote:

    > I'd go a step farther than Shawn's suggestion of factory methods
    > for the VT4Objects, and have a VT4Graphic superclass of
    > VT4ImageGraphic (which you probably already have), which builds
    > both the objects and the object references. VT4Graphic would have a
    > user defaults-style API with methods like:
    >
    > - (void)setPoint:(NSPoint)aPoint withName:(NSString *)name;
    > - (void)setSize:(NSSize)aSize withName:(NSString *)name;
    > - (void)setImageData:(NSData *)data withName:(NSString *)name;
    >
    > Example implementation:
    >
    > - (void)_setObject:(VT4Object *)object withName:(NSString *)name;
    > {
    > VT4ObjectReference  *positionReference =
    > [NSEntityDescription
    > insertNewObjectForEntityForName:@"VT4ObjectReference"
    > inManagedObjectContext:[self managedObjectContext]];
    > [positionReference setObject:object];
    > [positionReference setName:name];
    > [positionReference setOwner:self];
    > }
    >
    > - (void)setPoint:(NSPoint)aPoint withName:(NSString *)name;
    > {
    > VT4PointVector      *newPoint = [NSEntityDescription
    > insertNewObjectForEntityForName:@"VT4PointVector"
    > i
    > nManagedObjectContext:[self managedObjectContext]];
    > [newPoint setX:[NSNumber numberWithFloat:aPoint.x]];
    > [newPoint setY:[NSNumber numberWithFloat:aPoint.y]];
    > [self _setObject:newPoint withName:name];
    > }
    >
    > Then your utility method is reduced to:
    >
    > + (void)insertWithImage:(NSImage *)anImage atPoint:(NSPoint)aPoint
    > inManagedObjectContext:(NSManagedObjectContext *)aManagedObjectContext
    > {
    > if(nil != anImage)
    > {
    > NSAssert(nil != aManagedObjectContext, @"Invalid managed
    > object context");
    >
    > // Create an entity to draw the image with the size at the
    > position
    > VT4ImageGraphic      *newImageGraphic = [NSEntityDescription
    > insertNewObjectForEntityForName:@"VT4ImageGraphic"
    >
    > inManagedObjectContext:aManagedObjectContext];
    > [newImageGraphic setPoint:aPoint withName:@"position"];
    > [newImageGraphic setSize:[anImage size] withName:@"size"];
    > [newImageGraphic setImageData:[anImage TIFFRepresentation]
    > withName:@"image"];
    > }
    > }
    >
    > Hope this helps,
    > - Greg
    I think this is a glorious idea.  I wasn't thinking along the lines
    of the preferences API, but that is actually apropos.  I particularly
    like the technique for hiding entity names from objects that don't
    really need to know them.  It was the specific entity names and
    having to know the names of their attributes that I was calling "hard
    coding."  It seems to me that a VT4ImageGraphic shouldn't necessarily
    have to know the deatials about a VT4PointVector's attributes.

    Thanks.
previous month september 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today