このエラーは SO に何度も表示されているようです。この問題に対する最も顕著な答えは、 RestKit 0.20 のようです — CoreData: エラー: NSManagedObject クラスで指定された初期化子を呼び出すことができませんでした
ただし、投稿された解決策は私にとってはうまくいきません。しかし、最初から始めましょう:
サーバーにファイルをアップロードしたい。応答を解析して、正常にアップロードされたことを確認したいと思います。そうでない場合は、後でもう一度試します。私のモデルには、NSNumber *sentToServer
この目的のために呼び出されるブール変数があります(CoreData では、BOOL
として保存されますNSNumber
)。
送信が成功するかどうかわからないため、RestKit 経由で送信する前に、エンティティを CoreData モデルに保存します。
POST リクエストが成功すると、サーバーは次のような JSON 文字列を返します。
{"id":14,"created":true}
もちろん、ID は CoreData モデルにはまだ設定されていないことに注意してください。そのため、id
との両方created
が私のモデルの新しい情報です。
これが私のモデルです:
@interface Recording : NSManagedObject
@property (nonatomic, retain) NSString * audioFilePath;
@property (nonatomic, retain) NSDate * createdAt;
@property (nonatomic, retain) NSString * deviceId;
@property (nonatomic, retain) NSString * deviceType;
@property (nonatomic, retain) NSNumber * recordingId;
@property (nonatomic, retain) NSNumber * sentToServer;
@end
そして、これが私がそれを設定する方法です:
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"HTRecording" ofType:@"momd"]];
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"AudioModel.sqlite"];
NSError *error = nil;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![managedObjectStore addSQLitePersistentStoreAtPath:storePath
fromSeedDatabaseAtPath: nil
withConfiguration:nil
options:options
error:&error]) {
NSAssert(NO, @"Managed object store failed to create persistent store coordinator: %@", error);
}
[managedObjectStore createManagedObjectContexts];
// Configure MagicalRecord to use RestKit's Core Data stack
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:managedObjectStore.persistentStoreCoordinator];
[NSManagedObjectContext MR_setRootSavingContext:managedObjectStore.persistentStoreManagedObjectContext];
[NSManagedObjectContext MR_setDefaultContext:managedObjectStore.mainQueueManagedObjectContext];
NSString *databaseUrl = @"http://localhost:3000";
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:databaseUrl]];
objectManager.managedObjectStore = managedObjectStore;
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
// Setup mapping
RKEntityMapping* recordingRequestMapping = [RKEntityMapping mappingForEntityForName:@"Recording" inManagedObjectStore:managedObjectStore];
[recordingRequestMapping addAttributeMappingsFromDictionary:@{
@"device_id": @"deviceId",
@"device_type": @"deviceType" }];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[recordingRequestMapping inverseMapping]
objectClass:[Recording class]
rootKeyPath:@"recording"];
[objectManager addRequestDescriptor:requestDescriptor];
RKEntityMapping* recordingResponseMapping = [RKEntityMapping mappingForEntityForName:@"Recording" inManagedObjectStore:managedObjectStore];
[recordingResponseMapping addAttributeMappingsFromDictionary:@{
@"created": @"sentToServer",
@"id": @"recordingId"}];
recordingResponseMapping.identificationAttributes = @[@"recordingId"];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:recordingResponseMapping
pathPattern:@"/audio.json"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
まず、これは非常に一般的であるべきもののセットアップ コードが驚くほど長くなります (RestKit と MagicalRecord を初期化するだけです)。しかし、私は脱線します...
アップロードプロセス全体が正常に機能します。リクエストのマッピングは適切で、添付ファイルを含め、すべてがサーバーに保存されます。ファイルはマルチパート ポスト メソッドを介して送信されるため、リクエスト マッピングにはファイルが含まれていないことに注意してください。繰り返しますが、その部分は正常に機能します。ファイルをアップロードするためのコードは次のとおりです。
RKObjectManager *manager = [RKObjectManager sharedManager];
NSMutableURLRequest *request;
request = [manager multipartFormRequestWithObject:recording
method:RKRequestMethodPOST
path:@"audio.json"
parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:[[NSFileManager defaultManager] contentsAtPath:recording.audioFilePath]
name:@"recording[audio]"
fileName:[recording.audioFilePath lastPathComponent]
mimeType:@"audio/m4a"];
}];
RKObjectRequestOperation *operation = [manager objectRequestOperationWithRequest:request
success:^(RKObjectRequestOperation *op, RKMappingResult *mapping) {
NSLog(@"successfully saved file on the server");
}
failure:^(RKObjectRequestOperation *op, NSError *error){
NSLog(@"some weird error");
}];
[manager enqueueObjectRequestOperation:operation];
オブジェクトへの応答のマッピングが難しくなります。応答マッピングが一致しない場合、ログ出力には、応答を処理できなかったというエラーが含まれます (驚き)。ただし、応答マッピングが一致すると、アプリがクラッシュします。スタック トレースは次のとおりです。
#11 0x2f6482d6 in -[NSObject(NSKeyValueCoding) valueForKeyPath:] ()
#12 0x00113b9a in -[RKMappingOperation shouldSetValue:forKeyPath:usingMapping:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:436
#13 0x00114e40 in -[RKMappingOperation applyAttributeMapping:withValue:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:536
#14 0x00115fd4 in -[RKMappingOperation applyAttributeMappings:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:577
#15 0x0011c988 in -[RKMappingOperation main] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:948
#16 0x2f655d0a in -[__NSOperationInternal _start:] ()
#17 0x0010cd5a in -[RKMapperOperation mapRepresentation:toObject:atKeyPath:usingMapping:metadata:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:256
#18 0x0010b6aa in -[RKMapperOperation mapRepresentation:atKeyPath:usingMapping:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:176
#19 0x0010ddc8 in -[RKMapperOperation mapRepresentationOrRepresentations:atKeyPath:usingMapping:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:314
#20 0x0010e552 in -[RKMapperOperation mapSourceRepresentationWithMappingsDictionary:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:359
#21 0x0010ee72 in -[RKMapperOperation main] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:398
#22 0x2f655d0a in -[__NSOperationInternal _start:] ()
#23 0x00100e70 in -[RKObjectResponseMapperOperation performMappingWithObject:error:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/Network/RKResponseMapperOperation.m:345
#24 0x000ff4e6 in -[RKResponseMapperOperation main] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/Network/RKResponseMapperOperation.m:297
#25 0x2f655d0a in -[__NSOperationInternal _start:] ()
デバッガーがSIGABRT
in で停止する行[RKMappingOperation shouldSetValue:forKeyPath:usingMapping:]
は次のとおりです。
id currentValue = [self.destinationObject valueForKeyPath:keyPath];
https://stackoverflow.com/a/13734348/487556で提案されているようにブレークポイントを設定すると、そのブレークポイントに到達しないことに注意してください。これは、応答マッピングのパスに問題がないことを意味します。
ここでいくつかのことがうまくいかないことが想像できます。
- RestKit は、CoreData エンティティがまだ存在しないと想定し、それを作成したいと考えています。これはエンティティの重複につながります (これは残念です!) が、モデルのすべてのフィールドがオプションであるため、クラッシュすることはありません。
- RestKit は ID に基づいて CoreData エンティティを見つけようとしますが (これは識別子変数であるため)、もちろん ID はまだ設定されていません。
- 何か他の...
助言がありますか?