Open mgorbach opened 10 years ago
@mgorbach Actually considering using an approach for these other semi-common cases where you can register an NSValueTransformer class to handle the conversion. Otherwise for now you can use rzi_shouldImportValue:forKey:
to manually convert it. Tedious, but reliable.
@ndonald2 Yep, already implemented rzi_shouldImportValue:forKey:
easily enough.
I think an NSValueTransformer
feature would be great in terms of flexibility.
I would also however handle URL
s directly. It seems like something that doesn't increase outside-visible complexity of the library at all, because it doesn't affect API and it "makes sense" to an API consumer. The implementation is very non-magical, too. By the way, I would also document all the auto-handled types (if you haven't yet).
I like the idea of adding support for NSValueTransformer
and think it is definitely a feature that we should include. I think including NSURL
might be a bit of overkill and become harder to draw the line on what to support or not. Also because a lot of the time NSURL's are stored as strings anyway. We could always bundle up a few common transformers for people.
Leaving open as an enhancement.
I did a quick implementation with NSValueTransformer
, and I'm not sure I really like NSValueTransformer. It's a heavy abstraction that works great when you're dynamically modeling things (IE: Interface Builder), but it seems pretty painful here.
2 alternatives:
1) +(id)rzi_transformValue:(id)originalValue forKey:(NSString *)key
2) +(RZITransformationBlock)rzi_transformationBlockForKey:(NSString *)key
, where RZITransformBlock is just a block that takes an ID and returns an ID.
I'm leaning toward @KingOfBrian's second suggestion. Adopting NSValueTransformer
, while platform-standard, is a bit of a kluge in an era of block-based design. The two primary benefits of NSValueTransformer
aren't required: globally accessible transformers and reverse-transformations. I'd suggest that we illustrate the use of a value transformer in the docs rather than support it explicitly.
Just to chip in my 2 cents, the one nice thing about NSValueTransformer
is that it's a concise and easy way to encapsulate common transforms via subclasses and instantiate/associate them in a declarative manner. In some of my own code I've done things like this:
+ (NSDictionary *)transformersForKeys {
return @{
@"iso_timestamp": [NDDateTransformer iso8601],
@"rfc_timestamp": [NDDateTransformer rfc3339]
};
}
The equivalent imperative code using a block-based transform (ala @KingOfBrian's suggestions) is much less readable by comparison, especially since you have to compare the key strings one-by-one. The best you can do to keep it DRY in that approach is call some utility functions from within the blocks or return blocks created elsewhere which is much more gnarly:
+ (RZITransformationBlock)rzi_transformationBlockForKey:(NSString *)key {
if ([key isEqualToString:@"iso_timestamp"]) {
return ^id(id value) {
return [MyDateConversionUtils iso8601ToDate:value];
}
} else if ([key isEqualToString:@"rfc_timestamp"]) {
return ^id(id value) {
return [MyDateConversionUtils rfc3339ToDate:value];
}
}
}
Or, at best, cutting out a few lines by returning blocks from some factory class:
+ (RZITransformationBlock)rzi_transformationBlockForKey:(NSString *)key {
if ([key isEqualToString:@"iso_timestamp"]) {
return [MyDateConversionUtils iso8601ConverterBlock];
} else if ([key isEqualToString:@"rfc_timestamp"]) {
return [MyDateConversionUtils rfc3339ConverterBlock];
}
}
Breaking that down, it seems like there are two concepts being discussed here - declarative vs. imperative and NSValueTransformer
vs. blocks. Given that, there is also a third option to make the API more concise and declarative while still not using NSValueTransformer
:
+ (NSDictionary*)rz_transformationBlocksForKeys;
Here you'd just return a dictionary of keys in the raw data mapped to transformation blocks. Granted, assigning a block as the value of a key in a dictionary is a bit unusual, but not impossible.
Thanks for the input Nick! I agree the blocks in Objc, look pretty terrible. I was thinking about them mostly from the swift world, where the gap between methods and blocks sort of disappear. I also agree that the declarative API is a bit nicer.
Another thing that I'm considering now, is this is also overlapping a large chunk of other API bits. For instance, rzi_dateFormatForKey
and possibly even rzi_nestedObjectKeys
could be handled by these transformers easily (or atleast be backed similarly)
Not sure I have a clear development path here so I'm going to keep sitting on it for a few.
It's pretty common to have an API return a URL as a JSON string. It would be useful to have
RZImport
automatically be able to convert that string as needed and assign it to a property with typeNSURL.
Currently, expecting this I get the following error: