0

对于从 web 服务获取 web 的应用程序,我已经包含了一个 plist,如果它第一次运行,它会被解析为 CoreData,因为数据在 Docs 目录中不容易获得或者可能需要很长时间才能从 web 获取。当网络获取/同步成功时,我确实有 NSNotifications 信号。

目前在 AppDelegate applicationDidFinishLaunchingWithOptions 我调用:

[self checkIfFirstRun];

这是:

-(void)checkIfFirstRun{
    NSString *bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
    NSString *appFirstStartOfVersionKey = [NSString stringWithFormat:@"first_start_%@", bundleVersion];
    NSNumber *alreadyStartedOnVersion = [[NSUserDefaults standardUserDefaults] objectForKey:appFirstStartOfVersionKey];

    if(!alreadyStartedOnVersion || [alreadyStartedOnVersion boolValue] == NO) {
        // IF FIRST TIME -> Preload plist data
        UIAlertView *firstRun = [[UIAlertView alloc] initWithTitle:@"1st RUN USE LOCAL DB"
                                                                message:@"FIRST"
                                                               delegate:self
                                                      cancelButtonTitle:@"Cancel"
                                                      otherButtonTitles:@"Ok", nil];
        [firstRun show];
        NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
        [prefs setObject:[NSNumber numberWithBool:YES] forKey:appFirstStartOfVersionKey];
        [prefs synchronize];

        //Use plist
        [self parsePlistIntoCD];

    } else {
        UIAlertView *secondRun = [[UIAlertView alloc] initWithTitle:@"nTH RUN WEB FETCH"
                                                           message:@"nTH"
                                                          delegate:self
                                                 cancelButtonTitle:@"Cancel"
                                                 otherButtonTitles:@"Ok", nil];
        [secondRun show];


    }
}

好吧,我将我的 plist 完美地解析到我的 CoreData 数据库中。

这是 parsePlistIntoCD:

-(void)parsePlistIntoCD{
    self.managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
    // 3: Now put the plistDictionary into CD...create get ManagedObjectContext
    NSManagedObjectContext *context = self.managedObjectContext;
    NSError *error;

    //Create Request & set Entity for request
    NSFetchRequest *holidayRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *topicEntityDescription = [NSEntityDescription entityForName:@"Holiday" inManagedObjectContext:context];
    [holidayRequest setEntity:topicEntityDescription];

    //Create new NSManagedObject
    //Holiday *holidayObjectToSeed = nil;
    Holiday *newHoliday = nil;
    //Execute fetch just to make sure?
    NSArray *holidayFetchedArray = [context executeFetchRequest:holidayRequest error:&error];
    if (error) NSLog(@"Error encountered in executing topic fetch request: %@", error);

    // No holidays in database so we proceed to populate the database
    if ([holidayFetchedArray count] == 0) {
        //Get path to plist file
        NSString *holidaysPath = [[NSBundle mainBundle] pathForResource:@"PreloadedFarsiman" ofType:@"plist"];
        //Put data into an array (with dictionaries in it)
        NSArray *holidayDataArray = [[NSArray alloc] initWithContentsOfFile:holidaysPath];
        NSLog(@"holidayDataArray is %@", holidayDataArray);
        //Get number of items in that array
        int numberOfTopics = [holidayDataArray count];
        //Loop thru array items...
        for (int i = 0; i<numberOfTopics; i++) {
            //get each dict at each node
            NSDictionary *holidayDataDictionary = [holidayDataArray objectAtIndex:i];
            //Insert new object
            newHoliday = [NSEntityDescription insertNewObjectForEntityForName:@"Holiday" inManagedObjectContext:context];
            //Parse all keys in each dict object
            [newHoliday setValuesForKeysWithDictionary:holidayDataDictionary];
            //Save and or log error
            [context save:&error];
            if (error) NSLog(@"Error encountered in saving topic entity, %d, %@, Hint: check that the structure of the pList matches Core Data: %@",i, newHoliday, error);
        };
    }
    [[SDSyncEngine sharedEngine] startSync];

}

问题是,我还需要确保如果有可用的互联网,我的 CoreData 数据库会重新填充获取的网络数据。

但是如果我把电话留给 [self parsePlistIntoCD]; CoreData 中只有 plist 数据。第一次或第 n 次运行,我只得到 plist 数据。如果我注释掉那条线,我会得到我的网络获取数据。

为什么 web 获取的数据不替换 plist 解析的数据?

4

1 回答 1

1

所以逻辑parsePlistIntoCD本质上是

  • 如果存储中没有对象,则从 plist 加载它们
  • 总是调用startSyncon [SDSyncEngine sharedEngine],它处理网络下载和同步。

在我看来,您的startSync遗嘱实际上已被援引。所以我会在那里寻找错误。您可以添加一条日志语句或设置断点,以验证该代码路径是否实际被遵循。

plist 解析和网络数据获取都可能需要一些时间。这表明您应该在后台执行这些操作,可能使用 GCD 队列。你事先不知道他们中的任何一个是否会成功。因此,在完成之前不要设置首选项。

旁注:您可以在首选项数据库中查询 BOOL,从而使您的代码更短,因此更易于阅读。

    BOOL alreadyStartedOnVersion = [[NSUserDefaults standardUserDefaults] boolForKey:appFirstStartOfVersionKey];

    [prefs setBool:YES forKey:appFirstStartOfVersionKey];            

您也可以numberWithBool:简单地替换为@(YES)@(NO)

对于您的程序逻辑,我建议如下:

  • -applicationDidFinishLaunchingWithOptions:中,检查是否已经加载了起始 plist 数据。忘记这是不是第一次运行。就看plist数据是否需要加载。也许叫那个shouldLoadPlistData。或者,也许您需要将其与您正在运行的版本绑定,在这种情况下,您将存储一个 string latestPlistVersionLoaded
  • 如果您还没有加载它,请将一个块排入队列以执行 plist 加载。在 plist 加载结束时,设置shouldLoadPlistDataNO,以注意不再需要加载 plist 数据。如果由于某种原因,plist 加载失败(可能是手机没电了,或者你的应用程序被用户或系统杀死了),那么在下一次启动时,你又回到了开始的地方。
  • 还要检查您是否有网络访问权限。如果这样做,请将一个块排入队列以检索基于 Web 的数据,解析数据,然后在得出结论时更新首选项。

如果数据很大,您可能需要检查此工作:

  • 我有完整的网络更新吗?那我就完了。否则...
  • 下载完成了吗?是的,我有数据,让我们加载它。
  • 如果没有,我开始下载了吗?

如果您的应用在下载过程中退出,这种分阶段的检查点还允许您向系统询问额外的时间。

parseListIntoCD我觉得有点臃肿。它所做的比它的名字所暗示的要多。也许您可以将其重构为检查 ( shouldLoadPlist)、执行导入 ( importPlist:intoContext:) 的方法和触发同步的方法。

我强烈建议您将工作NSManagedObjectContext作为参数传递,而不是使用一些分配 MOC 的全局对象([SDCoreDataController sharedInstance]看起来确实如此。它为您提供了更多控制权,并允许您更轻松地编写单元测试。如果您还通过在 plist 的路径中,您现在拥有干净的代码,每次调用它时都应该以相同的方式运行。

您对该NSError **参数的使用始终不正确。NSError 的值在成功时未定义。您必须测试操作的结果,而不是错误的值,以确定您是否成功。成语总是

if (![someObject doTaskWithObject:foo error:&error]) {
    // handle the error
}

看看也countForFetchRequest:error。它会为您提供与您当前通过执行获取和计数结果提取的信息相同的信息,但无需实例化 NSManagedObjects。

于 2013-04-13T20:42:56.013 回答