0
votes

I have an object that conforms to NSCoding (and is archived to disk). One property of this object (myMetrics) is an NSDictionary. In a new version of the software, I need to add a key to this dictionary, but I need to do it before the rest of the app is using the main object as it is a required key.

My thought was to do something like:

-(id)initWithCoder:(NSCoder *)coder
{
    if (self = [super init])
    {
       self.myMetrics = [coder decodeObjectForKey:@"myMetrics"];
       ... other properties go here ....
    }

    .... add the new key to myMetrics with default value if it is missing in myMetrics ...

    return (self);
}

What I would really like to do is convert myMetrics from a dictionary to it's own class. I am not sure how I would handle this for archived objects that already exist. My thought was:

  1. add a new property of class NewMetrics called newMetrics.
  2. in initWithCoder if a myMetrics property is found, move its data into a newMetrics property
  3. in encodeWithCoder only handle newMetrics and never reencode the old dictionary.

In this way the software uses NewMetrics objects throughout but the initWithCoder is able to handle the old style myMetrics.

Is there any good way to do this while keeping the same property name?

@property (atomic, retain) NSDictionary* myMetrics; //  dictionary of parameters

becomes

@property (atomic, retain) NewMetrics* myMetrics; // parameters

rather than

@property (atomic, retain) NewMetrics* newMetrics; // parameters

Is there a better way?

1
You call your new class NewMetrics in some places and MyMetrics in others. Could you edit your question to use one or the other? - Jim Matthews
Sorry about that - it's fixed. - Trygve

1 Answers

1
votes

Yes, you can convert myMetrics from a dictionary to its own class, without renaming the property, while still handling existing archived objects. The new property declaration would be:

@property (atomic, retain) NewMetrics* myMetrics; // parameters

The initWithCoder: function would be something like:

-(id)initWithCoder:(NSCoder *)coder
{
    if (self = [super init])
    {
        // decode saved NewMetrics object
        self.myMetrics = [coder decodeObjectForKey:@"newMyMetrics"];
        if (!self.myMetrics) {
            // fall back to legacy saved metrics NSDictionary
            NSDictionary *legacyMetrics = [coder decodeObjectForKey:@"myMetrics"];
            if (legacyMetrics) {
                self.myMetrics = [[NewMetrics alloc] initWithDictionary:legacyMetrics];
            } else {
                // handle having no saved metrics
            }
        }

        ... other properties go here ....
    }

    return self;
}

You would have to start encoding the myMetrics property under a new key, e.g. @"myNewMetrics", and provide a way to convert the old-style metrics dictionary into an instance of the new NewMetrics class, e.g. initWithDictionary: as shown above.