RKValueTransformers 1.1.3

RKValueTransformers 1.1.3

测试已测试
语言语言 Obj-CObjective C
许可证 Apache 2
发布最后发布2016年3月

Blake WattersSamuel Giddins维护。



  • Blake Watters 和 Samuel E. Giddins

RestKit 中提取的一个简单、强大的 Objective-C 值转换 API

RKValueTransformers 是一个独立的库,它为 Objective-C 提供了一个简单的值转换 API。值转换是将值在不同表示形式之间转换的过程,它是任何需要以与本地数据模型不同的序列化格式传输和接收数据的系统的核心部分。

在 RESTful API 的一般上下文中,这意味着在 XML 或 JSON 文档中编码的值与您的数据模型本地属性之间的转换。最熟悉且最明显的例子是将日期和时间数据作为 JSON 文档中的字符串编码,并在本地作为 NSDate 属性表示为 NSObjectNSManagedObject 派生类的转换。RKValueTransformers 提供了一个简单、设计良好的 API,用于泛化并简化处理您 iOS 或 Mac OS X 应用程序中的任意复杂值转换要求的任务。

值转换是 RestKit 的一个核心特性,RKValueTransformers 是从父项目中提取出来的,旨在造福更大的 Cocoa 开发社区。如果您在寻找一个全面的解决方案以满足您的 RESTful API 需要,那么请务必仔细研究 RestKit。

特性

RKValueTransformers 是一个“自带电池”的库,包含处理最常见转换的值转换器。转换器核心集可以自定义,可以轻松实现新的转换器以满足任何应用程序的需求。

  • 包含多种转换器,涵盖最常见转换
    • NSString <-> NSURL
    • NSNumber <-> NSString
    • NSArray <-> NSOrderedSet
    • NSArray <-> NSSet
    • NSDecimalNumber <-> NSNumber
    • NSDecimalNumber <-> NSString
    • NSNull <-> nil
    • 任何符合 NSCoding 的类 <-> NSData
    • UNIX 时间间隔,以 NSNumberNSString 编码 <-> NSDate
    • ISO 8601 标记字符串 <-> NSDate(仅支持完整的标记字符串。在 iOS 设备等 32 位系统上(例如 iPhone 5s 之前的设备),仅支持年份小于 2038)
    • 实现 stringValue 的任何对象 <-> NSString
    • 任何单个对象到一个集合(《NSArray》、NSSetNSOrderedSet及其可变形式)
    • 任何对象与一个NSDictionary(对象成为空嵌套字典的键)
    • 任何符合NSMutableCoding的类 -> 自身的可变表示
    • NSStringNSDate通过NSDateFormatter双向转换。默认格式包括
      • RFC 1123格式
      • RFC 850格式
      • ANSI C的asctime()格式
    • NSStringNSNumber通过NSNumberFormatter转换
  • 轻量级。仅通过单一对头文件和实现文件实现。
  • 完全单元测试并记录。
  • 可以通过实现RKValueTransforming协议、继承RKValueTransformer或通过RKBlockValueTransformer使用块来扩展。
  • 可以通过RKCompoundValueTransformer类将多个值转换器组合为一个复合转换器。
  • 通过提供日期格式器的缓存来透明地提高日期转换性能。
  • 与RestKit完全集成。

示例

所有值转换均通过由RKValueTransforming协议定义的抽象通用接口执行

NSString *stringContainingDecimalNumber = @"3.4593895835";
NSError *error = nil;
NSDecimalNumber *decimalNumber = nil;
BOOL success = [[RKValueTransformers decimalNumberToStringValueTransformer] transformValue:stringContainingDecimalNumber toValue:&decimalNumber ofClass:[NSDecimalNumber class] error:&error];

transformValue:toValue:ofClass:error:方法总是相同的,无论底层转换的详细实现如何。它总是返回一个布尔值,指示转换是否成功,值转换器必须在转换无法执行时返回一个NSError

验证转换

在许多情况下,是否可以执行给定的转换可以通过转换中涉及的类型完全确定。在这些情况下,值转换器可以实现可选的RKValueTransforming方法validateTransformationFromClass:(Class)inputValueClass toClass:(Class)outputValueClass

BOOL isTransformationPossible = [[RKValueTransformers arrayToSetValueTransformer] validateTransformationFromClass:[NSSet class] toClass:[NSArray class]];
NSAssert(isTransformationPossible == YES, @"Should be `YES`");
isTransformationPossible = [[RKValueTransformers arrayToSetValueTransformer] validateTransformationFromClass:[NSSet class] toClass:[NSData class]];
NSAssert(isTransformationPossible == NO, @"Should be `NO`");

注意,因为这是一个可选方法,您必须检查给定的实例是否响应该验证选择器。如果不响应该选择器,则无法验证转换,并且必须尝试转换以确定成功或失败。

复合转换器

单个转换器非常方便 -- 它抽象出记住如何实现给定转换的需要,并呈现一个简单的转换接口。但是,RKValueTransformers的真实力量在于您通过RKCompoundValueTransformer类将一系列值转换器组合成一个复合转换器时。复合值转换器也实现了RKValueTransforming协议 -- 但它们不提供任何值转换和验证,而是代理调用到程序员定义顺序下的底层值转换器的调用。这允许您按特定顺序配置一组转换器,以便第一个能够执行给定转换的转换器将处理它。

例如,考虑某个应用程序可能需要与几个API交互,这些API返回多种格式的日期字符串。我们希望能够将任何给定的字符串值转换为NSDate,而无需担心细节。我们可以配置一个复合转换器来处理这个任务,如下所示

NSArray *dateFormats = @[ @"MM/dd/yyyy", @"yyyy-MM-dd'T'HH:mm:ss'Z'", @"yyyy-MM-dd" ];
RKCompoundValueTransformer *compoundValueTransformer = [RKCompoundValueTransformer new];
for (NSString *dateFormat in dateFormats) {
    NSDateFormatter *dateFormatter = [NSDateFormatter new];
    dateFormatter.dateFormat = dateFormat;
    [compoundValueTransformer addValueTransformer:dateFormatter];
}

[compoundValueTransformer addValueTransformer:[RKValueTransformer timeIntervalSince1970ToDateValueTransformer]];

NSArray *dateStrings = @[ @"11/27/1982", @"1378767519.18176508", @"2013-11-27", @"2013-04-23T16:29:05Z" ];
NSError *error = nil;
for (NSString *dateString in dateStrings) {
    NSDate *date = nil;
    BOOL success = [compoundValueTransformer transformValue:dateString toValue:&date ofClass:[NSDate class]];
    NSLog(@"Transformed value '%@' to value '%@' successfully=%@, error=%@", dateString, date, success ? @"YES" : @"NO", error);
}

块值转换器

RKValueTransformers支持通过块创建临时值转换器实例。例如,可以实现在NSString实例上执行所有内容大写转换的值转换器,如下所示

RKValueTransformer *uppercaseStringTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class sourceClass, __unsafe_unretained Class destinationClass) {
    // We transform a `NSString` into another `NSString`
    return ([sourceClass isSubclassOfClass:[NSString class]] && [destinationClass isSubclassOfClass:[NSString class]]);
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, Class outputValueClass, NSError *__autoreleasing *error) {
    // Validate the input and output
    RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSString class], error);
    RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputValueClass, [NSString class], error);

    // Perform the transformation
    *outputValue = [(NSString *)inputValue uppercaseString];
    return YES;
}];

安装

RKValueTransformers 非常轻量,没有直接依赖除外的 Cocoa 基础框架。因此,可以通过直接添加源代码将其轻松安装到任何 Cocoa 项目中。尽管如此,我们建议通过 CocoaPods 进行安装,因为它提供了模块化,并能够轻松安装依赖于 RKValueTransformers 的新的值转换器。

通过源代码

只需将 RKValueTransformers.hRKValueTransformers.m 添加到您的项目中,并 #import "RKValueTransformers.h"

设计与实现细节

RKValueTransformers 被设计成易于集成和使用。整个库由一个协议、三个类和一些类别实现组成

  • RKValueTransferring - 定义值转换 API。任何希望作为值转换器执行行的类都可以采用。
  • RKValueTransformer - 一个实现 RKValueTransforming 的抽象基类。该基类包含用于检索预包装值转换器单例的静态访问器。扩展库可以继承 RKValueTransformer 来提供新的转换器。
  • RKBlockValueTransformer - 一个允许通过块定义的临时值转换器的具体子类。
  • RKCompoundValueTransformer - 一个提供对值转换器组合支持的 RKValueTransforming 具体实现,它代理对一组值转换器的调用。

对于实现值转换器的人来说,包含了一些宏来简化验证和转换方法的实现

  • RKValueTransformerTestInputValueIsKindOfClass - 测试给定输入值是否是给定类或其子类的实例。如果测试结果为负,则返回 NO 并发出适当的 NSError
  • RKValueTransformerTestOutputValueClassIsSubclassOfClass - 测试给定输出值类是否等于给定类或其子类。如果测试结果为负,则返回 NO 并发出适当的 NSError
  • RKValueTransformerTestTransformation - 测试给定的转换是否成功。如果测试结果为负,则返回 NO 并发出适当的 NSError

为何不使用 NSValueTransformer?

在我们开发 RKValueTransformers 的过程中,我们仔细研究了 NSValueTransformer,并最终确定它并不适合我们的需求。具体来说,我们发现以下问题:

  1. NSValueTransformer 定义了 '正向' 和 '反向' 转换的概念,这在主要关注类型变换的系统中并不清晰。哪一面算作正向?当你考虑到可以发生两种以上类型之间的转换时,这个问题变得更加严重。
  2. NSValueTransformer 通过 transformedValueClass 类方法公开了 "输出" 对象的类。这变成了一个烦恼,因为你被迫使用继承来表达类型知识。这需要直接继承 NSValueTransformer 或使用如 TransformerKit 那样的花哨运行时黑客技术。
  3. NSValueTransformer 通过 setValueTransformer:forName:valueTransformerForName: 方法公开了一个基于名字的全局名称注册表。最终,这还不够细粒度,无法提供必要的灵活性,并需要使用名字(而不是类型信息)来查找转换器。

鉴于以上所有问题,从头开始设计一个针对值转换问题的解决方案似乎更有意义。

单元测试

使用Expecta库进行单元测试匹配符测试RKValueTransformers。为了运行测试,您需要执行以下操作:

  1. 通过CocoaPods安装依赖: pod install
  2. 打开工作区: open RKValueTransformers.xcworkspace
  3. 通过产品菜单 > 测试运行spec

致谢

Blake Watters

Samuel E. Giddins

许可证

RKValueTransformers可在Apache 2许可证下使用。有关更多信息,请参阅LICENSE文件。