Core Data: Automatically Trim String Properties - ios

For my Core Data NSManagedObject, I would like to ensure any NSString properties only contain strings that have been trimmed of whitespace.
I'm aware that I could achieve this by overriding each setter method, like so:
- (void)setSomeProperty:(NSString *)someProperty
{
someProperty = [someProperty stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ((!someProperty && !self.someProperty) || [someProperty isEqualToString:self.someProperty]) return;
[self willChangeValueForKey:#"someProperty"];
[self setPrimitiveValue:someProperty forKey:#"someProperty"];
[self didChangeValueForKey:#"someProperty"];
}
However, this seems like a lot of code to have to write, especially since my managed object is likely to have quite a few NSString properties.
Is there an easier way?

You could create a custom NSValueTransformer for NSString and assign all of your NSString properties to the new transformer in the model editor:
#interface StringTransformer: NSValueTransformer {}
#end
#implementation StringTransformer
+ (Class)transformedValueClass {
return [NSString class];
}
+ (BOOL)allowsReverseTransformation {
return YES;
}
- (id)transformedValue:(id)value {
return value;
}
- (id)reverseTransformedValue:(id)value {
return [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
#end

If you only need to ensure that the saved data is trimmed then you can implement willSave and use changedValues to check only the changed values. This will also make it easy to do in a loop to minimise code duplication.

You could do it during property validation:
- (BOOL)validateSomeProperty:(id *)inOutValue error:(NSError **)error
{
if (inOutValue)
{
NSString *value = *inOutValue;
*inOutValue = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
return YES;
}
Core data will automatically call validateSomeProperty:error: before saving your record, so this will make sure that any data that gets saved is trimmed. It won't stop the on-change events firing if someone changes it from, say, foo to \n\nfoo\n\n, but it does mean that you don't have to fire them by hand.

Related

Parsing id to NSString

When parsing API responses, sometimes I can not rely on strings being embedded in quotation marks. ID's are a good example of this, where some API's will send the numerical ID as a string while some will send it as a number.
What is a good practice when parsing such a value? If I simply parse it to an NSString like so:
NSString *myID = (NSString *)message["myID"];
I can end up with an NSString object that somehow contains (long)123.
And using stringValue would cause issues when the value is actually already sent as a string (since NSString does not have a stringValue function).
A way that works, but is somewhat ugly, is this:
id myID = (NSString *)message["myID"];
if ([myID respondsToSelector:#selector(stringValue)])
{
myID = [myID stringValue];
}
You could do something like:
id myID = message["myID"];
if ([myID isKindOfClass:[NSString class]]) { ... }
else { ... }
As long as this logic is encapsulated inside data parser and is opaque for your api users (i.e. they will always get a string) any approach is fine, e.g.:
- (NSString*)parseID:(NSDictionary*)message {
id rawID = message["myID"];
if ([rawID isKindOfClass:[NSString class]]){
return rawID;
} else if ([rawID isKindOfClass:[NSNumber class]]) {
return [(NSNumber*)rawID stringValue];
} else {
// We might still want to handle this case.
NSAssert(false, #"Unexpected id type");
return nil;
}
}
Alternative is to define stringValue in extension, so any possible objet will respond to selector:
#implementation NSString(JSONStringParsing)
- (NSString *)stringValue {
return [self copy];
}
#end
Why not just use description?
NSArray *objects = #[
#NSIntegerMin,
#NSIntegerMax,
#"123456789"
];
for (id object in objects) {
NSString *stringObject = [object description];
NSLog(#"%# -> %# | %#", [object className], [stringObject className], stringObject);
}

Recursive method Objective-C

I'm checking is first letter of string is 0, if it is remove it and call again method to check is there is still 0. I've debugged this and it seems like when it accomplish number without 0, it goes backwards. Code:
-(NSString *)deleteZerosOnFirst:(NSString *)card
{
NSString *firstLetter = [card substringToIndex:1];
if ([firstLetter isEqualToString:#"0"]) {
card = [card substringFromIndex:1];
[self deleteZerosOnFirst:card];
NSLog(#"CARD: %#", card);
return card;
}
else {
NSLog(#"CARD: %#", card);
return card;
}
}
The main problem is that you're not using the result of the recursion. The line of code where you call yourself should say this:
card = [self deleteZerosOnFirst:card];
Also, you're calling deleteZerosOnFirst before you do the NSLog. Reverse the order of these two lines. That will at least give you your debug output in the right sequence.
Here's your recursive call:
[self deleteZerosOnFirst:card];
That doesn't modify the string that card references. It creates and returns a new string. You're ignoring the returned string. You want this:
card = [self deleteZerosOnFirst:card];
But this is really a lot simpler:
#implementation NSString (withoutLeadingZeroes)
- (NSString *)withoutLeadingZeroes {
NSString *s = self;
while ([s hasPrefix:#"0"]) {
s = [s substringFromIndex:1];
}
return s;
}
#end

Objective C static class member having bad access

I am trying to use the static variable here tagsToCheck, accessible from all static methods in this class.
What is the best way?
A retain here solve the problem, but is it the right way?
tagsToCheck = [#[#"<html>", #"<br>", #"<br />", #"<br/>", #"<p>", #"<div>", #"<b>",
#"<i>", #"<font>", #"<ul>", #"<li>"] retain];
The original code is,
static NSArray * tagsToCheck = nil;
#implementation DictionaryUtil
+ (void) initialize {
tagsToCheck = #[#"<html>", #"<br>", #"<br />", #"<br/>", #"<p>", #"<div>", #"<b>",
#"<i>", #"<font>", #"<ul>", #"<li>"];
}
+ (BOOL) isHtml:(NSString *)string
{
if (!string) return NO;
for (NSString *tag in tagsToCheck) { // bad access here for tagsToCheck
if ([string rangeOfString:tag options:NSCaseInsensitiveSearch].location != NSNotFound) {
return YES;
}
}
return NO;
}
Yes the Objective-C array literal is an autoreleased object, so using retain is correct.
Using retain is not the best way. The best way is to enable Automatic Reference Counting.

is there way to replace the Core Data implementation of add and remove methods for relationships in a generic way?

PLEASE NOTE: This is not the same question as this question, as I already know that this stems from an Apple bug. Mind you, overriding the specific methods (say, -addFriendsObject: if I had a friends relationship) is not an option, since I need to do this in a category so it works for any managed object and regardless of modifying the model and rebuildiong the autogenerated classes from it.
The need for this stems from the fact that apparently the minute one makes relationships ordered and to-many (NSMutableOrderedSets) Core Data dynamic methods go to hell:
methods -add<Relationship>Object, -add<Relationship>, -remove<Relationship>Object and -remove<Relationship> will all crash with an exception alike 'NSInvalidArgumentException', reason: '*** -[NSSet intersectsSet:]: set argument is not an NSSet'
methods along the lines of insertObject:in<Relationship>AtIndex: will instead crash because no implementation for them was actually provided by CoreData.
As far as 2, I wrote a category that overrides -methodSignatureForSelector: and -forwardInvocation to instead do something like mutableOrderedSetValueForKey for the relationship name followed by the actual adding or removing.
Now for 1, the problem is that CoreData is actually providing implementations for those methods (though they are not the right implementations for ordered sets). So I need a way of intercepting those selectors too, so I can implement the behavior over mutableOrderedSetValueForKey.
Any ideas how to pull it off?
I don't know that this is the best approach (it's certainly not the prettiest thing ever), but it gets the job done:
#import "NSManagedObject+OrderedSets.h"
#import <objc/runtime.h>
#implementation NSManagedObject (OrderedSets)
+ (void)swizzleMethod:(SEL)originalSelector with:(SEL)replacementSelector
{
const char *methodTypeEncoding = method_getTypeEncoding(class_getInstanceMethod([self class], originalSelector));
class_replaceMethod(self,
originalSelector,
class_getMethodImplementation(self, replacementSelector),
methodTypeEncoding);
}
+ (void)initialize
{
NSEntityDescription *selfEntity = // get a hold of your NSManagedObjectContext and from its entities grab model.entitiesByName[NSStringFromClass(self)] ...
for (NSString *rKey in [selfEntity relationshipsByName]) {
NSRelationshipDescription *r = selfEntity.relationshipsByName[rKey];
if (r.isOrdered) {
NSString *rKeyFirstCaps = [[rKey substringToIndex:1] capitalizedString];
NSString *capitalizedKey = [rKey stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:rKeyFirstCaps];
NSString *addSelectorString = [NSString stringWithFormat:#"add%#Object:", capitalizedKey];
NSString *removeSelectorString = [NSString stringWithFormat:#"remove%#Object:", capitalizedKey];
[self swizzleMethod:NSSelectorFromString(addSelectorString) with:#selector(addOrRemoveObjectInOrderedSet:)];
[self swizzleMethod:NSSelectorFromString(removeSelectorString) with:#selector(addOrRemoveObjectInOrderedSet:)];
}
}
}
- (void)addOrRemoveObjectInOrderedSet:(NSManagedObject*)object
{
NSString *selectorString = NSStringFromSelector(_cmd);
NSString *prefix = [selectorString hasPrefix:#"add"] ? #"add" : #"remove";
selectorString = [selectorString stringByReplacingCharactersInRange:[selectorString rangeOfString:prefix] withString:#""];
selectorString = [selectorString stringByReplacingCharactersInRange:[selectorString rangeOfString:#"Object" options:NSBackwardsSearch] withString:#""];
selectorString = [selectorString substringToIndex:selectorString.length - 1];
NSString *selectorFirstLowerCase = [[selectorString substringToIndex:1] lowercaseString];
NSString *camelCasedKey = [selectorString stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:selectorFirstLowerCase];
if ([prefix isEqualToString:#"add"]) {
[[self mutableOrderedSetValueForKey:camelCasedKey] addObject:object];
}
else {
[[self mutableOrderedSetValueForKey:camelCasedKey] removeObject:object];
}
}
#end

Why do I have to use self. to reference own object

I'm learning iOS development through Stanford's iTunesU program. I am stuck on an unexpected problem I am having.
I have added a clear method, but I am getting this error
//Use of undeclared identifier 'operandStack'; did you mean '_operandStack'?
I know I can fix the problem by using [self.operandStack ...etc instead of [operandStack
Why do I need self? Isn't it implied? Why do I not need to use self when referencing _operandStack?
#import "CalculatorBrain.h"
#interface CalculatorBrain()
//string because we are the only ones interested
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
#implementation CalculatorBrain
#synthesize operandStack = _operandStack;
- (void) setOperandStack:(NSMutableArray *)operandStack
{
_operandStack = operandStack;
}
- (NSMutableArray *) operandStack
{
if(_operandStack==nil) _operandStack = [[NSMutableArray alloc] init];
return _operandStack;
}
- (void) pushOperand:(double)operand
{
NSNumber *operandObject = [NSNumber numberWithDouble:operand];
[self.operandStack addObject:operandObject];
}
- (double) popOperand
{
NSNumber *operandObject = [self.operandStack lastObject];
if (operandObject !=nil)
{
[self.operandStack removeLastObject];
}
return [operandObject doubleValue];
}
- (void) clear
{
//clear everything
[operandStack removeAllObjects];
//***************************
//Use of undeclared identifier 'operandStack'; did you mean '_operandStack'?
}
- (double) performOperation:(NSString *)operation
{
double result =0;
//calculate result
if ([operation isEqualToString:#"+"]) {
result = [self popOperand] + [self popOperand];
} else if ([operation isEqualToString:#"*"]) {
result = [self popOperand] * [self popOperand];
} else if ([operation isEqualToString:#"π"]) {
[self pushOperand:3.14159];
NSNumber *operandObject = [self.operandStack lastObject];
return [operandObject doubleValue];
}
[self pushOperand:result];
return result;
}
#end
Because you have synthesized it (note: in newer Objective-C version the synthesis is automatic) :
#synthesize operandStack = _operandStack;
It means that you generated getter and setter, and that you access the property by calling it _operandStack. If you want to call it operantStack change it to that:
#synthesize operandStack;
If instead you use self.operandStack, you are using the getter/setter generated by the property, not the synthesized one.
Using synthesized and not synthesized properties is different, there isn't a "recommended way" of accessing properties like many people think, they just have different meaning. For example here:
- (void) setOperandStack:(NSMutableArray *)operandStack
{
_operandStack = operandStack;
}
You must use the synthesized property, otherwise you go into an infinite loop. The synthesized property is automatically generated, the non synthesized property is also automatically generated but it can be overridden, like you did in that case, and it can be also accessed externally.

Resources