How to store NSRect as Core Data attribute?

  • Hello everyone,

    I'm trying to get to grips with non-standard persistent attributes in
    Core Data. I've read through the docs I can find (http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles
    /cdNSAttributes.html

    )  but I'm still having some issues storing an NSRect in an entity.
    I've set the type to be transformable, and left the value transformer
    field empty using the model editor.

    To actually set and get the NSRect values, I've been using NSValue's
    valueWithRect: and rectValue methods. However, when CD trys to saving
    the entities to the store, I get this error:

    *** -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot
    encode structs

    I've also changed the property defined in the header file for the
    entity to:
    @property(retain) NSValue *extent;

    from
    @property(retain) id extent;

    What am I doing wrong here? The docs seem to suggest for KVO/KVC
    compliant structs, NSRect, NSPoint, NSSize and NSRange, you don't need
    to overwrite the getters and setters?

    Any help is much appreciated!

    Cheers
    Dan
  • On 02.04.2008, at 13:28, Daniel Thorpe wrote:

    > Hello everyone,
    >
    > I'm trying to get to grips with non-standard persistent attributes
    > in Core Data. I've read through the docs I can find (http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles
    /cdNSAttributes.html

    > )  but I'm still having some issues storing an NSRect in an entity.
    > I've set the type to be transformable, and left the value
    > transformer field empty using the model editor.

    If you leave the transformer field empty it uses
    NSKeyedUnarchiveFromDataTransformerName as
    default. But unfortunately NSKeyedArchiver can not encode/decode
    NSValues containing
    a struct. You could use NSUnarchiveFromDataTransformerName as a
    workaround. (NSArchiver/NSUnarchiver)
    is able to encode/decode a NSValue containing a NSRect).

    Or implement the approach described in the section "Scalar Values" in
    the documentation you
    mentioned (it contains an example using a NSRect).

    Cheers,

    felix
  • I think the easiest way to encode/decode NSRects for storage is
    NSStringFromRect() and NSRectFromString().  Then you can just
    implement a custom accessor for the property that converts in the
    direction you need.

    ->Ben
    --
    Ben Lachman
    Acacia Tree Software

    http://acaciatreesoftware.com

    <blachman...>
    740.590.0009

    On Apr 2, 2008, at 7:28 AM, Daniel Thorpe wrote:
    > Hello everyone,
    >
    > I'm trying to get to grips with non-standard persistent attributes
    > in Core Data. I've read through the docs I can find (http://
    > developer.apple.com/documentation/Cocoa/Conceptual/CoreData/
    > Articles/cdNSAttributes.html)  but I'm still having some issues
    > storing an NSRect in an entity. I've set the type to be
    > transformable, and left the value transformer field empty using the
    > model editor.
    >
    > To actually set and get the NSRect values, I've been using
    > NSValue's valueWithRect: and rectValue methods. However, when CD
    > trys to saving the entities to the store, I get this error:
    >
    > *** -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver
    > cannot encode structs
    >
    > I've also changed the property defined in the header file for the
    > entity to:
    > @property(retain) NSValue *extent;
    >
    > from
    > @property(retain) id extent;
    >
    > What am I doing wrong here? The docs seem to suggest for KVO/KVC
    > compliant structs, NSRect, NSPoint, NSSize and NSRange, you don't
    > need to overwrite the getters and setters?
    >
    > Any help is much appreciated!
    >
    > Cheers
    > Dan
  • On 02.04.2008, at 18:12, Ben Lachman wrote:
    > I think the easiest way to encode/decode NSRects for storage is
    > NSStringFromRect() and NSRectFromString().  Then you can just
    > implement a custom accessor for the property that converts in the
    > direction you need.

    That potentially means opening Pandoras can to localisation issues.
    Any time you go that route, be sure to check that code with switching
    decimal separators.

    In my opinion it sucks like hell when your Pages document containing a
    table has all numeric data messed up due to "unparseable" numbers...

    Regards,
    Tom_E
  • On 02.04.2008, at 18:58, Thomas Engelmeier wrote:
    > That potentially means opening Pandoras can to localisation issues.
    > Any time you go that route, be sure to check that code with
    > switching decimal separators.

      Potentially? Maybe. But I just tried it, and for me
    NSStringFromRect() never uses localized separators. So, since it
    always gives and takes periods as the decimal separator, I see no
    problem. Do you have a particular test case where it behaves
    differently?

    Cheers,
    -- Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
    http://www.zathras.de
  • Thanks for the feedback on this.

    I have gone with using a ...AsString attribute and using
    NSRectFromString. Seems to work okay, although I've got no idea if
    it's the most efficient method. I think Core Data seems a little
    limited in that you can't store an NSValue object as an attribute, I'd
    have thought that would be obvious, as we use that class to store
    structs in collection objects....

      I hadn't considered localisation issues, but on my machine it stores
    the data like so in an XML store:

            <attribute name="extentasstring" type="string">{{368, 260},
    {2, 5}}</attribute>
            <attribute name="centreofmassasstring" type="string">{368.75,
    263.25}</attribute>

    which seems to make sense.

    Cheers
    Dan

    On 2 Apr 2008, at 18:33, Uli Kusterer wrote:

    > On 02.04.2008, at 18:58, Thomas Engelmeier wrote:
    >> That potentially means opening Pandoras can to localisation issues.
    >> Any time you go that route, be sure to check that code with
    >> switching decimal separators.
    >
    >
    > Potentially? Maybe. But I just tried it, and for me
    > NSStringFromRect() never uses localized separators. So, since it
    > always gives and takes periods as the decimal separator, I see no
    > problem. Do you have a particular test case where it behaves
    > differently?
    >
    > Cheers,
    > -- Uli Kusterer
    > "The Witnesses of TeachText are everywhere..."
    > http://www.zathras.de
  • Actually on Leopard you should be able to store an NSValue as an
    attribute, simply by using an attribute of type Transformable, and
    setting the Value Transformer Name to NSUnarchiveFromData.

    I just tried it though, and it seems like there is a bug which
    prevents it from working.  Basically it seems like any custom value
    transformer you set for a Transformable attribute is simply ignored,
    and it still just uses the default transformer, which tries to use a
    KeyedArchiver and KeyedUnarchiver.  I even tried creating my own
    NSValueTransformer subclass and setting it as the value transformer
    for a Transformable attribute.  I can see that its init method gets
    called, but none of its other methods ever get called when saving a
    document.  I just filed a bug with Apple about this, radar 5851442.

    On Apr 3, 2008, at 5:31 AM, Daniel Thorpe wrote:

    > Thanks for the feedback on this.
    >
    > I have gone with using a ...AsString attribute and using
    > NSRectFromString. Seems to work okay, although I've got no idea if
    > it's the most efficient method. I think Core Data seems a little
    > limited in that you can't store an NSValue object as an attribute,
    > I'd have thought that would be obvious, as we use that class to
    > store structs in collection objects....
    >
    > I hadn't considered localisation issues, but on my machine it stores
    > the data like so in an XML store:
    >
    > <attribute name="extentasstring" type="string">{{368, 260},
    > {2, 5}}</attribute>
    > <attribute name="centreofmassasstring" type="string">{368.75,
    > 263.25}</attribute>
    >
    > which seems to make sense.
    >
    > Cheers
    > Dan
    >
    > On 2 Apr 2008, at 18:33, Uli Kusterer wrote:
    >
    >> On 02.04.2008, at 18:58, Thomas Engelmeier wrote:
    >>> That potentially means opening Pandoras can to localisation
    >>> issues. Any time you go that route, be sure to check that code
    >>> with switching decimal separators.
    >>
    >>
    >> Potentially? Maybe. But I just tried it, and for me
    >> NSStringFromRect() never uses localized separators. So, since it
    >> always gives and takes periods as the decimal separator, I see no
    >> problem. Do you have a particular test case where it behaves
    >> differently?
    >>
    >> Cheers,
    >> -- Uli Kusterer
    >> "The Witnesses of TeachText are everywhere..."
    >> http://www.zathras.de

  • On Thu, Apr 3, 2008 at 4:31 AM, Daniel Thorpe <danthorpe...> wrote:

    > I have gone with using a ...AsString attribute and using NSRectFromString.
    > Seems to work okay, although I've got no idea if it's the most efficient
    > method. I think Core Data seems a little limited in that you can't store an
    > NSValue object as an attribute, I'd have thought that would be obvious, as
    > we use that class to store structs in collection objects....

    I'm not sure, but I think the $64,000 question is how to handle (a) struct
    padding and (b) endieness issues. With something like a NSRect it should be
    easy (write a special case in NSValue) but for arbitrary structs the problem
    is insurmountable.
  • Hmm, seems I was misunderstanding what the built-in value transformers
    do, and also had a problem with my custom transformer.  The
    NSUnarchiveFromData transformer won't work for this purpose.  However
    you can make it possible to store NSValue objects as Core Data
    attributes by using a Transformable attribute, and writing your own
    ValueTransformer.  Here's a trivial value transformer that I just
    tried, which allows storing any NSCoding compliant object as an
    attribute value, including NSValues containing an NSRect.  To use it,
    add this class to your project, then create a Core Data attribute of
    type Transformable, and set its Value Transformer Name to
    MyValueTransformer.

    // MyValueTransformer.h
    #import <Cocoa/Cocoa.h>

    @interface MyValueTransformer : NSValueTransformer {

    }

    @end

    // MyValueTransformer.m
    #import "MyValueTransformer.h"

    @implementation MyValueTransformer

    + (Class)transformedValueClass
    {
        return [NSData class];
    }

    + (BOOL)allowsReverseTransformation
    {
        return YES;
    }

    - (id)transformedValue:(id)value
    {
        return [NSArchiver archivedDataWithRootObject:value];
    }

    // input is expected to be an NSData object
    - (id)reverseTransformedValue:(id)data
    {
        return [NSUnarchiver unarchiveObjectWithData:data];
    }

    @end

    On Apr 9, 2008, at 1:16 AM, Adam P Jenkins wrote:
    > Actually on Leopard you should be able to store an NSValue as an
    > attribute, simply by using an attribute of type Transformable, and
    > setting the Value Transformer Name to NSUnarchiveFromData.
    >
    > I just tried it though, and it seems like there is a bug which
    > prevents it from working.  Basically it seems like any custom value
    > transformer you set for a Transformable attribute is simply ignored,
    > and it still just uses the default transformer, which tries to use a
    > KeyedArchiver and KeyedUnarchiver.  I even tried creating my own
    > NSValueTransformer subclass and setting it as the value transformer
    > for a Transformable attribute.  I can see that its init method gets
    > called, but none of its other methods ever get called when saving a
    > document.  I just filed a bug with Apple about this, radar 5851442.
    >
    >
    > On Apr 3, 2008, at 5:31 AM, Daniel Thorpe wrote:
    >
    >> Thanks for the feedback on this.
    >>
    >> I have gone with using a ...AsString attribute and using
    >> NSRectFromString. Seems to work okay, although I've got no idea if
    >> it's the most efficient method. I think Core Data seems a little
    >> limited in that you can't store an NSValue object as an attribute,
    >> I'd have thought that would be obvious, as we use that class to
    >> store structs in collection objects....
    >>
    >> I hadn't considered localisation issues, but on my machine it
    >> stores the data like so in an XML store:
    >>
    >> <attribute name="extentasstring" type="string">{{368, 260},
    >> {2, 5}}</attribute>
    >> <attribute name="centreofmassasstring" type="string">{368.75,
    >> 263.25}</attribute>
    >>
    >> which seems to make sense.
    >>
    >> Cheers
    >> Dan
    >>
    >> On 2 Apr 2008, at 18:33, Uli Kusterer wrote:
    >>
    >>> On 02.04.2008, at 18:58, Thomas Engelmeier wrote:
    >>>> That potentially means opening Pandoras can to localisation
    >>>> issues. Any time you go that route, be sure to check that code
    >>>> with switching decimal separators.
    >>>
    >>>
    >>> Potentially? Maybe. But I just tried it, and for me
    >>> NSStringFromRect() never uses localized separators. So, since it
    >>> always gives and takes periods as the decimal separator, I see no
    >>> problem. Do you have a particular test case where it behaves
    >>> differently?
    >>>
    >>> Cheers,
    >>> -- Uli Kusterer
    >>> "The Witnesses of TeachText are everywhere..."
    >>> http://www.zathras.de

    >
previous month april 2008 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