You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CoreData+CloudKit集成报错:自定义值转换器问题的原因与解决方法

问题:CoreData+CloudKit 中Transformable属性转换器报错

我使用CoreData+CloudKit存储数据,现有AEntity和BEntity两个实体:

  • AEntity的content属性为Transformable类型,用于存储NSAttributedString,已在Xcode中指定转换器为AttributedStringValueTransformer,代码如下:
@implementation AttributedStringValueTransformer

+ (Class)transformedValueClass {
    return [NSAttributedString class];
}

+ (BOOL)allowsReverseTransformation {
    return YES;
}

- (NSData *)transformedValue:(NSAttributedString *)value {
    if (!value) {
        return nil;
    }
    if ([value isKindOfClass:[NSData class]]) {
        return (NSData *)value;
    }
    NSData *stringAsData = [NSKeyedArchiver archivedDataWithRootObject:value];
    return stringAsData;
}

- (NSAttributedString *)reverseTransformedValue:(NSData *)value {
    NSAttributedString *attributedString = [NSKeyedUnarchiver unarchiveObjectWithData:value];
    return attributedString;
}

@end
  • BEntity的images属性为Transformable类型,用于存储图片数组(NSArray),已在Xcode中指定转换器为ArrayValueTransformer,代码如下:
@implementation ArrayValueTransformer

+ (Class)transformedValueClass {
    return [NSArray class];
}

+ (BOOL)allowsReverseTransformation {
    return YES;
}

- (NSData *)transformedValue:(NSAttributedString *)value {
    if (!value) {
        return nil;
    }
    if ([value isKindOfClass:[NSData class]]) {
        return (NSData *)value;
    }
    return [NSKeyedArchiver archivedDataWithRootObject:value];;
}

- (NSAttributedString *)reverseTransformedValue:(NSData *)value {
    return [NSKeyedUnarchiver unarchiveObjectWithData:value];
}

@end

初始化NSPersistentCloudKitContainer的代码如下:

+ (instancetype)containerWithName:(NSString *)name {
    NSPersistentCloudKitContainer *persistentContainer = [[NSPersistentCloudKitContainer alloc] initWithName:name];
    persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES;
    
    NSPersistentStoreDescription *description = [persistentContainer.persistentStoreDescriptions firstObject];
    [description setOption:@(YES) forKey:NSPersistentHistoryTrackingKey];
    [description setOption:@(YES) forKey:NSPersistentStoreRemoteChangeNotificationPostOptionKey];

    description.shouldInferMappingModelAutomatically = YES;
    description.shouldMigrateStoreAutomatically = YES;
    
    
    persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
    [persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
        if (error != nil) {
            NSLog(@"Unresolved error %@, %@", error, error.userInfo);
//            abort();
        }
    }];
    
    return persistentContainer;
}

运行时在loadPersistentStoresWithCompletionHandler的回调中出现以下错误:

Error Domain=NSCocoaErrorDomain Code=134060 "A Core Data error occurred." UserInfo={NSLocalizedFailureReason=CloudKit integration requires that the value transformers for transformable attributes are available via +[NSValueTransformer valueTransformerForName:], return instances of NSData, and allow reverse transformation: AEntity: content - Claims to return instances of NSAttributedString BEntity: images - Claims to return instances of NSArray}

错误原因

CloudKit对CoreData的Transformable属性转换器有三个硬性要求,当前代码全部违反:

  1. 转换器未全局注册,无法通过+[NSValueTransformer valueTransformerForName:]被CoreData和CloudKit访问到
  2. 转换器的transformedValueClass返回的是原始数据类型(NSAttributedString/NSArray),但CloudKit要求转换后必须是NSData类型,因为它只支持二进制数据存储
  3. ArrayValueTransformer存在类型错误:transformedValue:的参数类型错误写为NSAttributedString,reverseTransformedValue:的返回值也错误写为NSAttributedString,和实际处理的NSArray不匹配

解决步骤

1. 修正转换器的transformedValueClass方法

所有用于CloudKit的Transformable属性转换器,transformedValueClass必须返回NSData.class,因为这是最终存储到CloudKit的类型。同时修正ArrayValueTransformer的类型错误,并替换为安全的编码解码API。

修正后的AttributedStringValueTransformer:

@implementation AttributedStringValueTransformer

+ (Class)transformedValueClass {
    return [NSData class]; // 改为返回NSData类型
}

+ (BOOL)allowsReverseTransformation {
    return YES;
}

- (NSData *)transformedValue:(id)value {
    if (!value) {
        return nil;
    }
    if ([value isKindOfClass:[NSData class]]) {
        return (NSData *)value;
    }
    if ([value isKindOfClass:[NSAttributedString class]]) {
        // 使用安全编码API,支持Secure Coding
        return [NSKeyedArchiver archivedDataWithRootObject:value requiringSecureCoding:YES error:nil];
    }
    return nil;
}

- (NSAttributedString *)reverseTransformedValue:(NSData *)value {
    if (!value) {
        return nil;
    }
    // 使用安全解码API,指定目标类型
    return [NSKeyedUnarchiver unarchivedObjectOfClass:[NSAttributedString class] fromData:value error:nil];
}

@end

修正后的ArrayValueTransformer:

@implementation ArrayValueTransformer

+ (Class)transformedValueClass {
    return [NSData class]; // 改为返回NSData类型
}

+ (BOOL)allowsReverseTransformation {
    return YES;
}

- (NSData *)transformedValue:(id)value {
    if (!value) {
        return nil;
    }
    if ([value isKindOfClass:[NSData class]]) {
        return (NSData *)value;
    }
    if ([value isKindOfClass:[NSArray class]]) {
        // 注意:数组中的元素(如UIImage)需要支持NSCoding,或提前转为NSData存储
        return [NSKeyedArchiver archivedDataWithRootObject:value requiringSecureCoding:YES error:nil];
    }
    return nil;
}

- (NSArray *)reverseTransformedValue:(NSData *)value {
    if (!value) {
        return nil;
    }
    return [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:value error:nil];
}

@end

2. 全局注册转换器

在App启动阶段(比如AppDelegate的application:didFinishLaunchingWithOptions:方法,或SwiftUI的App结构体初始化代码中),注册这两个转换器,名称要和Xcode实体属性中设置的Transformer名称完全一致:

// 注册自定义转换器
[NSValueTransformer setValueTransformer:[[AttributedStringValueTransformer alloc] init] forName:@"AttributedStringValueTransformer"];
[NSValueTransformer setValueTransformer:[[ArrayValueTransformer alloc] init] forName:@"ArrayValueTransformer"];

3. (可选)确保数据类型支持安全编码

如果数组中的图片是UIImage,需要确认它支持NSCoding(默认支持),或者提前将图片转为NSData再存入数组,避免编码失败。


内容的提问来源于stack exchange,提问作者Lix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 02:06:06