4

I've watched some of the WWDC videos on Core Data and I'm planning on maintaining a canonicalized text property. Let's say I have the following data:

originalString              normalizedString (+lowercase?)
Ønsker                      onsker
onsker                      onsker
Onsker                      onsker

When I query my model, I want to sort it by 'normalizedString' so that it ignores the case and the Ø (or other characters). I also want to be able to run a query like "starts with 'o'" and have it return the 3 words above.

I was trying to avoid to do something like:

[NSPredicate predicateWithFormat:@"(originalString like[cd] %@)"...

for querying the model. I was also trying to use the 'originalString' for my sorting.

I've tried two different approaches with no success, my normalized string is still saved as the originalString (I override the setter in a category I created):

  1. Calling decomposedStringWithCanonicalMapping:

    // ...
    [normalizedString decomposedStringWithCanonicalMapping];
    // ...
    
  2. Followed this example:

    // ...
    CFStringNormalize((CFMutableStringRef)normalizedString, kCFStringNormalizationFormD);
    CFStringFold((CFMutableStringRef)normalizedString, kCFCompareCaseInsensitive | kCFCompareDiacriticInsensitive | kCFCompareWidthInsensitive, NULL);
    

Any ideas on how I can accomplish my goal?

Edit: Here's my overridden setter, which I know it gets called:

- (void) setNormalizedName:(NSString *)newNormalizedName
{
    NSMutableString *normalizedString;
    if (![self.lastName length] == 0) {
        normalizedString = [NSMutableString stringWithString:self.lastName];
    } else {
        normalizedString = [NSMutableString stringWithString:self.firstName];
    }

//    CFStringNormalize((CFMutableStringRef)normalizedString, kCFStringNormalizationFormD);
//    CFStringFold((CFMutableStringRef)normalizedString, kCFCompareCaseInsensitive | kCFCompareDiacriticInsensitive | kCFCompareWidthInsensitive, NULL);
    [normalizedString decomposedStringWithCanonicalMapping];

    [self willChangeValueForKey:@"normalizedName"];
    [self setPrimitiveValue:normalizedString forKey:@"normalizedName"];
    [self didChangeValueForKey:@"normalizedName"];
}
Eric
  • 3,301
  • 4
  • 33
  • 39

1 Answers1

0

You should override the setters for the "primary" properties (e.g. firstName, lastName), and not the setter for the "derived" property.

Also note that decomposedStringWithCanonicalMapping returns a new string, it does not modify the receiver.

The code could roughly look like this (not compiler checked):

- (void) setFirstName:(NSString *)firstName
{
    [self willChangeValueForKey:@"firstName"];
    [self setPrimitiveValue:firstName forKey:@"firstName"];
    [self didChangeValueForKey:@"firstName"];
    [self updateNormalizedName];
}

- (void) setLastName:(NSString *)lastName
{
    [self willChangeValueForKey:@"lastName"];
    [self setPrimitiveValue:lastName forKey:@"lastName"];
    [self didChangeValueForKey:@"lastName"];
    [self updateNormalizedName];
}

- (void) updateNormalizedName
{
    NSString *normalizedString;
    if ([self.lastName length] > 0) {
        normalizedString = [self.lastName decomposedStringWithCanonicalMapping];
    } else {
        normalizedString = [self.firstName decomposedStringWithCanonicalMapping];
    }
    self.normalizedString = normalizedString;
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • This is what I was trying to do, but I was overriding setFirstName, setLastName and setNormalizedName so I probably created a problem there. I'll give it another try with this new method 'updateNormalizedName'. I will mark your answer as correct once I get to test it. – Eric Jul 03 '13 at 22:31
  • I tried doing what you suggested, but decomposedStringWithCanonicalMapping returns the same string. I added the commented lines above, but that only transforms Ønsker into ønsker. I was hoping on getting Ønsker to be onsker. Any ideas? – Eric Jul 11 '13 at 03:33
  • @Eric: As I see now, my answer is more focused on "how to write the setter for a normalized attribute", but that does not solve your concrete problem "how to normalize Ø to o". - It might be that the latter is not possible, compare http://stackoverflow.com/questions/16836975/ios-cfstringtransform-and for a similar question and http://stackoverflow.com/questions/9376621/folding-normalizing-ligatures-e-g-to-ae-using-corefoundation for possible explanations. – Martin R Jul 11 '13 at 18:56