Web services 是否将IOS核心数据与web服务同步?

Web services 是否将IOS核心数据与web服务同步?,web-services,ios5,core-data,Web Services,Ios5,Core Data,这是我的问题: 我想使用核心数据速度和连接问题来构建我的IOS应用程序。存储在核心数据中的数据来自SQLServer数据库,我可以通过一个尚未定义的web服务访问该数据库 对核心数据中存储的数据的任何更改都需要通过web服务与SQLServer同步。此外,我需要缓冲由于连接问题而无法同步的更改 我还需要用服务器上发生的任何更改更新核心数据。这可能发生在用户首选项中设置的计划上 我探索过的解决方案: 使用NSIncrementalStore类(IOS 5中新增)。我很困惑这到底能做什么,但听

这是我的问题:

  • 我想使用核心数据速度和连接问题来构建我的IOS应用程序。存储在核心数据中的数据来自SQLServer数据库,我可以通过一个尚未定义的web服务访问该数据库
  • 对核心数据中存储的数据的任何更改都需要通过web服务与SQLServer同步。此外,我需要缓冲由于连接问题而无法同步的更改
  • 我还需要用服务器上发生的任何更改更新核心数据。这可能发生在用户首选项中设置的计划上
我探索过的解决方案:

  • 使用
    NSIncrementalStore
    类(IOS 5中新增)。我很困惑这到底能做什么,但听起来很有希望。据我所知,您可以创建子类
    NSIncrementalStore
    ,它允许您拦截常规的核心数据API调用。然后,我可以将信息传递到核心数据,并通过web服务将其与外部数据库同步。我可能完全错了。但假设我是对的,如果与互联网的连接中断,我将如何同步增量
  • AFIncrementalStore
    -这是
    NSIncrementalStore
    的一个子类,使用
    AFNetworking
    执行web服务部分
  • RestKit
    -我有点担心这个API有多活跃,它似乎正在向块功能过渡。有人广泛使用过这个吗
我倾向于
AFIncrementalStore
,因为这是一种(似乎是)更标准的方法。问题是,我可能完全偏离了
NSIncrementalStore
的真正含义


链接到一些示例代码或教程将是伟大的

我的解决方案是将数据集的两个副本存储在CoreData数据库中。一个表示最后一个已知的服务器状态,是不可变的。另一个由用户编辑

当需要同步更改时,应用程序会在数据的已编辑副本和不可变副本之间创建差异。应用程序将差异发送到web服务,web服务将差异应用到自己的数据副本。它会回复数据集的完整副本,应用程序会将其覆盖到数据集的两个副本上

优点是:

  • 如果没有网络连接,则不会丢失任何更改:每次需要发送数据集时都会计算差异,并且只有在成功同步时才会更改不可变副本
  • 只传输需要发送的最小信息量
  • 多人可以同时编辑相同的数据,而无需使用锁定策略,通过覆盖将数据丢失的机会降至最低
缺点是:

  • 编写差分代码很复杂
  • 编写合并服务很复杂
  • 除非您是元编程大师,否则您会发现您的diff/merge代码是脆弱的,并且无论何时更改对象模型都必须更改
以下是我在制定战略时考虑的一些因素:

  • 如果允许脱机进行更改,签入/签出锁定将不起作用(如何在没有连接的情况下建立锁定?)
  • 如果两个人同时编辑相同的数据会发生什么
  • 如果一个人在没有连接的情况下在一台iOS设备上编辑数据,将其关闭,在另一台设备上编辑,然后重新打开原始设备,会发生什么情况
  • 使用CoreData进行多线程处理本身就是一个完整的问题类
我所听说的最接近于开箱即用的支持远程执行类似操作的是iOS6中新的iCloud/CoreData同步系统,当实体发生变化时,它会自动将实体从CoreData数据库传输到iCloud。然而,这意味着你必须使用iCloud

编辑:我知道这已经很晚了,但是这里有一个类能够在两个NSManagedObject实例之间产生差异

// SZManagedObjectDiff.h
@interface SZManagedObjectDiff

- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject

@end

// SZManagedObjectDiff.m
#import "SZManagedObjectDiff.h"

@implementation SZManagedObjectDiff

- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSDictionary *attributeDiff = [self diffAttributesOfNewObject:newObject withOldObject:oldObject];

    NSDictionary *relationshipsDiff = [self diffRelationshipsOfNewObject:newObject withOldObject:oldObject];

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    if (attributeDiff.count > 0) {
        diff[@"attributes"] = attributeDiff;
    }

    if (relationshipsDiff.count > 0) {
        diff[@"relationships"] = relationshipsDiff;
    }

    if (diff.count > 0) {
        diff[@"entityName"] = newObject ? newObject.entity.name : oldObject.entity.name;

        NSString *idAttributeName = newObject ? newObject.entity.userInfo[@"id"] : oldObject.entity.userInfo[@"id"];

        if (idAttributeName) {
            id itemId = newObject ? [newObject valueForKey:idAttributeName] : [oldObject valueForKey:idAttributeName];

            if (itemId) {
                diff[idAttributeName] = itemId;
            }
        }
    }

    return diff;
}

- (NSDictionary *)diffRelationshipsOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSDictionary *relationships = newObject == nil ? [[oldObject entity] relationshipsByName] : [[newObject entity] relationshipsByName];

    for (NSString *name in relationships) {

        NSRelationshipDescription *relationship = relationships[name];

        if (relationship.deleteRule != NSCascadeDeleteRule) continue;

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        if (relationship.isToMany) {

            NSArray *changes = [self diffNewSet:newValue withOldSet:oldValue];

            if (changes.count > 0) {
                diff[name] = changes;
            }

        } else {

            NSDictionary *relationshipDiff = [self diffNewObject:newValue withOldObject:oldValue];

            if (relationshipDiff.count > 0) {
                diff[name] = relationshipDiff;
            }
        }
    }

    return diff;
}

- (NSDictionary *)diffAttributesOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys];

    for (NSString *name in attributeNames) {

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        newValue = newValue ? newValue : [NSNull null];
        oldValue = oldValue ? oldValue : [NSNull null];

        if (![newValue isEqual:oldValue]) {
            diff[name] = @{ @"new": newValue, @"old": oldValue };
        }
    }

    return diff;
}

- (NSArray *)diffNewSet:(NSSet *)newSet withOldSet:(NSSet *)oldSet {

    NSMutableArray *changes = [NSMutableArray array];

    // Find all items that have been newly created or updated.
    for (NSManagedObject *newItem in newSet) {

        NSString *idAttributeName = newItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id newItemId = [newItem valueForKey:idAttributeName];

        NSManagedObject *oldItem = nil;

        for (NSManagedObject *setItem in oldSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:newItemId]) {
                oldItem = setItem;
                break;
            }
        }

        NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

        if (diff.count > 0) {
            [changes addObject:diff];
        }
    }

    // Find all items that have been deleted.
    for (NSManagedObject *oldItem in oldSet) {

        NSString *idAttributeName = oldItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id oldItemId = [oldItem valueForKey:idAttributeName];

        NSManagedObject *newItem = nil;

        for (NSManagedObject *setItem in newSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:oldItemId]) {
                newItem = setItem;
                break;
            }
        }

        if (!newItem) {
            NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

            if (diff.count > 0) {
                [changes addObject:diff];
            }
        }
    }

    return changes;
}

@end
这里有关于它的作用、作用方式及其限制/假设的更多信息:


使用解析平台及其IOS SDK来构造和存储信息。它可以在本地缓存数据,以便在没有连接的情况下快速检索数据

我认为你所说的同步功能在IOS5中是可用的,但最重要的是它与iCloud同步,而不是我必须与之交互的系统(SQLServer)。我很喜欢你的想法,但我关心你指出的问题:找出三角洲。你有过NSIncrementalStore的经验吗?我仍然不清楚它到底是什么。NSIncrementalStore是实现任何类型的存储系统作为核心数据存储的基类。要将XML文件与CoreData API一起使用吗?从NSIncrementalStore继承并编写执行此操作的方法。我认为这对你的处境没有帮助。它允许我同时做这两件事吗?我可以用它来更新核心数据和我的外部web服务吗?你可以,就像AFIncrementalStore一样,但是你最好使用这个类,而不是使用你自己的类。与CoreData和web API交互是最简单的部分。您面临的真正挑战是如何处理连接状态与断开状态以及多个用户同时访问同一数据,这两者都需要您创建自己的自定义解决方案。我浏览了为AFIncrementalStore编写的一些示例代码。如果我理解的话,它基本上允许程序员使用核心数据api从核心数据以外的其他来源获取数据。它通过子类化AFIncrementalStore覆盖一些调用来实现这一点。这看起来像是一个全有或全无的冒险。它似乎没有在核心数据中实际存储任何内容。有没有一种简单的方法可以让它同时更新这两个版本?