【问题标题】:RestKit 0.20 - POST multiple objectsRestKit 0.20 - 发布多个对象
【发布时间】:2014-05-06 04:00:42
【问题描述】:

我正在创建一个具有离线模式的任务应用程序。我几乎已经使用 RestKit 成功创建了在线模式。

在离线模式下,我将每个对象的isSync 属性设置为false,这样当网络可用时,我可以使用谓词获取这些未同步的对象并将它们发布到服务器。

不幸的是,我找不到使用 RestKit 一次性将这些对象的数组POST 发送到服务器的方法。

RestKit 支持这个吗?或者有没有更好的方法来使用 RestKit 实现离线支持?

更新:

我找到了一种方法here,可以创建一个中间Entity(带有自己的映射),它的属性为NSArray,可以发布到服务器。

但是我有许多需要离线功能的实体(笔记、任务、cmets 等),我是否必须为每个原始实体创建一个额外的中间实体? (这样每个人的响应都可以在我原来的实体中正确映射)

更新 2:

访问here 后,我发现现在在 RestKit 0.20.0 中支持 POST 一个包含多个对象的数组。但是每当我发布一组对象时,都不会向服务器发送任何参数。我就是这样做的:

[DBTasks createTask:task attachments:nil completionHandler:^(DBTasks *task, NSError *error) {
            isPosting = NO;
            [self setLoadingState:NO];
            if (!error) {
                KLog(@"task is %@", task); // works perfect
                [self.tasksArray insertObject:task atIndex:0];
                [self reloadTasks];

            } else {
                KLog(@"error %@", error);
            }
        }];

这是我实际发送 POST 请求的方法:

(只需考虑else 部分)

+ (void)createTask:(DBTasks *)task attachments:(NSArray *)attachments completionHandler:(void (^)(DBTasks *, NSError *))completionHandler {

    if (attachments != nil && attachments.count > 0) {
        NSMutableURLRequest *request =[[RKObjectManager sharedManager] multipartFormRequestWithObject:task
                                                                                               method:RKRequestMethodPOST
                                                                                                 path:URL_TASKS
                                                                                           parameters:@{@"total_attachments": [NSNumber numberWithInt:attachments.count]}
                                                                            constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

                                                                                int counter = 0;
                                                                                for (NSDictionary *dic in attachments) {
                                                                                    [formData appendPartWithFileData:UIImageJPEGRepresentation([dic objectForKey:@"image"], 0.7)
                                                                                                                name:[NSString stringWithFormat:@"attachment[%i]", counter]
                                                                                                            fileName:[dic objectForKey:@"name"]
                                                                                                            mimeType:@"image/jpg"];

                                                                                    counter++;
                                                                                }
                                                                            }];

        RKObjectRequestOperation *operation = [[RKObjectManager sharedManager] managedObjectRequestOperationWithRequest:request
                                                                                                   managedObjectContext:[NSManagedObjectContext MR_defaultContext]
                                                                                                                success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                                                                                                    KLog(@"success");
                                                                                                                    completionHandler((DBTasks *)[mappingResult firstObject], nil);

                                                                                                                } failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                                                                                                    KLog(@"fail");
                                                                                                                    completionHandler(nil, error);
                                                                                                                }];
        [operation start];

    }

    // without attachment
    else {
        [[RKObjectManager sharedManager] postObject:task path:URL_TASKS parameters:nil
                                            success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                                completionHandler((DBTasks *)[mappingResult firstObject], nil);
                                            }
                                            failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                                completionHandler(nil, error);
                                            }];
    }
}

以下是映射:

- (RKEntityMapping *)tasksMapping {

    RKEntityMapping *tasksMapping = [RKEntityMapping mappingForEntityForName:@"DBTasks" inManagedObjectStore:objectManager.managedObjectStore];
    tasksMapping.setDefaultValueForMissingAttributes = NO;
    tasksMapping.deletionPredicate = [NSPredicate predicateWithFormat:@"shouldBeDeleted = 1"];
    [tasksMapping setModificationAttributeForName:@"updated_at"];
    tasksMapping.identificationAttributes = @[@"id"];

    [tasksMapping addAttributeMappingsFromArray:@[@"completed_at", @"created_at", @"due_date", @"id", @"note", @"private", @"send_email", @"status", @"title", @"type", @"updated_at", @"user_id", @"parent_id", @"total_attachments", @"url", @"from", @"total_comments"]];

    [tasksMapping addAttributeMappingsFromDictionary:@{@"deleted": @"shouldBeDeleted"}];


    [tasksMapping addRelationshipMappingWithSourceKeyPath:@"attachments" mapping:[self attachmentsMapping]];
    [tasksMapping addRelationshipMappingWithSourceKeyPath:@"owner" mapping:[self contactsMapping]];
    [tasksMapping addRelationshipMappingWithSourceKeyPath:@"additional_owners" mapping:[self contactsMapping]];
    [tasksMapping addRelationshipMappingWithSourceKeyPath:@"tags" mapping:[self tagsMapping]];

    return tasksMapping;
}

POST/PUT/DELETE 使用相同的映射,但调用inverseMapping 方法。

现在,当我使用数组调用 postObject 时:

[DBTasks createTasks:[NSArray arrayWithObjects:task, task, nil] attachments:nil completionHandler:^(NSArray *array, NSError *error) {
            isPosting = NO;
            [self setLoadingState:NO];
            if (!error) {
                KLog(@"Array of objects is %@", array);
                [self.tasksArray insertObject:task atIndex:0];
                [self reloadTasks];

            } else {
                KLog(@"error %@", error);
            }
        }];

使用几乎以前的方法:

+ (void)createTasks:(NSArray *)tasksArray attachments:(NSArray *)attachments completionHandler:(void (^)(NSArray *, NSError *))completionHandler {
    [[RKObjectManager sharedManager] postObject:tasksArray path:URL_TASKS parameters:nil
                                        success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                            completionHandler(mappingResult.array, nil);
                                        }
                                        failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                            completionHandler(nil, error);
                                        }];
}

应用程序被终止

2014-05-05 16:18:30.692 MyApp[8195:f03] D restkit.object_mapping:RKMapperOperation.m:377 Executing mapping operation for representation: (
        {
        attachments =         (
        );
        "completed_at" = "<null>";
        "created_at" = "<null>";
        deleted = 0;
        "due_date" = "<null>";
        from = "Stand-alone task";
        id = "<null>";
        note = "<null>";
        owner = "<null>";
        private = 0;
        "send_email" = "<null>";
        status = "<null>";
        tags =         (
        );
        title = "<null>";
        "total_attachments" = 0;
        "total_comments" = 0;
        type = Task;
        "updated_at" = "<null>";
        url = "http://10.28.79.98:3000/workspace/tasks?open=task_";
        "user_id" = 26894;
    }
)
 and targetObject: (null)
2014-05-05 16:18:30.693 MyApp[8195:f03] D restkit.object_mapping:RKMapperOperation.m:297 Found mappable collection at keyPath '': (
        {
        attachments =         (
        );
        "completed_at" = "<null>";
        "created_at" = "<null>";
        deleted = 0;
        "due_date" = "<null>";
        from = "Stand-alone task";
        id = "<null>";
        note = "<null>";
        owner = "<null>";
        private = 0;
        "send_email" = "<null>";
        status = "<null>";
        tags =         (
        );
        title = "<null>";
        "total_attachments" = 0;
        "total_comments" = 0;
        type = Task;
        "updated_at" = "<null>";
        url = "http://10.28.79.98:3000/workspace/tasks?open=task_";
        "user_id" = 26894;
    }
)
2014-05-05 16:18:30.694 MyApp[8195:f03] CoreData: error: Failed to call designated initializer on NSManagedObject class 'DBTasks' 
2014-05-05 16:18:30.694 MyApp[8195:f03] D restkit.object_mapping:RKMapperOperation.m:231 Asked to map source object {
    attachments =     (
    );
    "completed_at" = "<null>";
    "created_at" = "<null>";
    deleted = 0;
    "due_date" = "<null>";
    from = "Stand-alone task";
    id = "<null>";
    note = "<null>";
    owner = "<null>";
    private = 0;
    "send_email" = "<null>";
    status = "<null>";
    tags =     (
    );
    title = "<null>";
    "total_attachments" = 0;
    "total_comments" = 0;
    type = Task;
    "updated_at" = "<null>";
    url = "http://10.28.79.98:3000/workspace/tasks?open=task_";
    "user_id" = 26894;
} with mapping <RKEntityMapping:0x10598240 objectClass=DBTasks propertyMappings=(
    "<RKAttributeMapping: 0x10599660 completed_at => completed_at>",
    "<RKAttributeMapping: 0x10599670 created_at => created_at>",
    "<RKAttributeMapping: 0x10599680 due_date => due_date>",
    "<RKAttributeMapping: 0x10599690 id => id>",
    "<RKAttributeMapping: 0x105996a0 note => note>",
    "<RKAttributeMapping: 0x105996b0 private => private>",
    "<RKAttributeMapping: 0x105996c0 send_email => send_email>",
    "<RKAttributeMapping: 0x105996d0 status => status>",
    "<RKAttributeMapping: 0x105996e0 title => title>",
    "<RKAttributeMapping: 0x105996f0 type => type>",
    "<RKAttributeMapping: 0x10599700 updated_at => updated_at>",
    "<RKAttributeMapping: 0x10599710 user_id => user_id>",
    "<RKAttributeMapping: 0x10599720 parent_id => parent_id>",
    "<RKAttributeMapping: 0x10599730 total_attachments => total_attachments>",
    "<RKAttributeMapping: 0x10599740 url => url>",
    "<RKAttributeMapping: 0x10599750 from => from>",
    "<RKAttributeMapping: 0x10599760 total_comments => total_comments>",
    "<RKAttributeMapping: 0x10599870 deleted => shouldBeDeleted>",
    "<RKRelationshipMapping: 0x1059b1c0 attachments => attachments>",
    "<RKRelationshipMapping: 0x1059c5f0 owner => owner>",
    "<RKRelationshipMapping: 0x1059dca0 additional_owners => additional_owners>",
    "<RKRelationshipMapping: 0x105a1e00 tags => tags>"
)>
2014-05-05 16:18:30.695 MyApp[8195:f03] D restkit.object_mapping:RKMappingOperation.m:952 Starting mapping operation...
2014-05-05 16:18:30.696 MyApp[8195:340b] D restkit.object_mapping:RKPropertyInspector.m:130 Cached property inspection for Class 'DBTasks': {
    "additional_owners" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSSet;
        name = "additional_owners";
    };
    attachments =     {
        isPrimitive = 0;
        keyValueCodingClass = NSSet;
        name = attachments;
    };
    "completed_at" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSDate;
        name = "completed_at";
    };
    "created_at" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSDate;
        name = "created_at";
    };
    "due_date" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSDate;
        name = "due_date";
    };
    from =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = from;
    };
    id =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = id;
    };
    note =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = note;
    };
    owner =     {
        isPrimitive = 0;
        keyValueCodingClass = DBContacts;
        name = owner;
    };
    "parent_id" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = "parent_id";
    };
    private =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = private;
    };
    "send_email" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = "send_email";
    };
    shouldBeDeleted =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = shouldBeDeleted;
    };
    status =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = status;
    };
    tags =     {
        isPrimitive = 0;
        keyValueCodingClass = NSSet;
        name = tags;
    };
    title =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = title;
    };
    topic =     {
        isPrimitive = 0;
        keyValueCodingClass = DBTopics;
        name = topic;
    };
    "total_attachments" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = "total_attachments";
    };
    "total_comments" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = "total_comments";
    };
    type =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = type;
    };
    "updated_at" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSDate;
        name = "updated_at";
    };
    url =     {
        isPrimitive = 0;
        keyValueCodingClass = NSString;
        name = url;
    };
    "user_id" =     {
        isPrimitive = 0;
        keyValueCodingClass = NSNumber;
        name = "user_id";
    };
}
2014-05-05 16:18:30.698 MyApp[8195:340b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** setObjectForKey: key cannot be nil'
*** First throw call stack:
(
    0   CoreFoundation                      0x034721e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x02be48e5 objc_exception_throw + 44
    2   CoreFoundation                      0x034fbeb8 -[__NSDictionaryM setObject:forKey:] + 888
    3   MyApp                         0x002dad3a __61-[RKPropertyInspector(CoreData) propertyInspectionForEntity:]_block_invoke61 + 154
    4   libdispatch.dylib                   0x04ce17b8 _dispatch_call_block_and_release + 15
    5   libdispatch.dylib                   0x04cf64d0 _dispatch_client_callout + 14
    6   libdispatch.dylib                   0x04ce4047 _dispatch_queue_drain + 452
    7   libdispatch.dylib                   0x04ce3e42 _dispatch_queue_invoke + 128
    8   libdispatch.dylib                   0x04ce4de2 _dispatch_root_queue_drain + 78
    9   libdispatch.dylib                   0x04ce5127 _dispatch_worker_thread2 + 39
    10  libsystem_pthread.dylib             0x05025dab _pthread_wqthread + 336
    11  libsystem_pthread.dylib             0x05029cce start_wqthread + 30
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)

任何建议我做错了什么?

【问题讨论】:

  • 不,你没有。显示您的模型结构、所需的 JSON 示例以及您尝试使用对象数组的代码(并描述它做错了什么)。
  • @Wain,感谢您的回复。其实我还没有实现它,因为我不知道该怎么做。 postObject 接受单个对象,并且没有可用的 postObjects 方法。我尝试创建一个对象数组并将其提供给postObject。服务器还没有写来处理这个请求,所以我正在等待服务器人员来处理这个请求。但是我仍然想知道发布这样的数组是否会完全反转映射我的本地对象并更新它们。我错了吗?
  • 发布数组是正确的。映射控制从数组中的每个对象到 JSON 的内容。
  • @Wain,我正在尝试将一组对象发布到服务器,但服务器端的人说他没有收到任何参数。当我发布一个对象时,它完美无缺。知道有什么问题吗?我已经更新了问题。

标签: ios objective-c core-data restkit-0.20


【解决方案1】:

问题与发送数组无关,这是由于您的核心数据堆栈设置所致。你的两种情况有很大的不同。一个有效的使用魔法记录中的默认上下文。不使用对象管理器的对象管理器及其关联的对象存储 - 似乎没有配置。

从代码和错误来看,发布没有附件的单个任务也应该失败。

检查您的核心数据堆栈创建以及与对象管理器的关联。

【讨论】:

  • 感谢您的回复。我已经按照 Blake github.com/blakewatters/RKMagicalRecord 的建议设置了核心数据堆栈,并且所有 Web 服务请求和离线 NSPredicate 获取都运行良好。仅发布一组对象是行不通的。因为我之前没有找到任何像这样发布数组的例子,所以只是想确认这真的有效吗?我错过了什么吗?
  • 这部分日志CoreData: error: Failed to call designated initializer on NSManagedObject class 'DBTasks'表示堆栈或您使用它的方式不正确
  • 是的,那会是个问题。考虑在 github 上提出问题。您可以使用managedObjectRequestOperationWithRequest:managedObjectContext:success:failure: 来解决它。
  • 那么服务器收到一个请求,却是空的?日志看起来不错。使用 Charles 检查它是否已发送/。
  • Charles 说数据发送正确,现在我的 Web 服务开发人员感受到了我过去 2 天的感受 :)
猜你喜欢
  • 2014-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 2013-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多