对于从Web服务获取Web的应用程序,我已经包含了一个要在第一次运行时解析到CoreData中的plist,因为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];只有plist数据存在于CoreData中。第一次或第n次运行,我只得到plist数据。如果我评论该行,我会获取我的网络获取数据。
为什么Web获取的数据不会替换plist解析数据?
答案 0 :(得分:1)
所以parsePlistIntoCD
的逻辑基本上是
startSync
上调用[SDSyncEngine sharedEngine]
,startSync
处理网络下载和同步。我认为你的 BOOL alreadyStartedOnVersion = [[NSUserDefaults standardUserDefaults] boolForKey:appFirstStartOfVersionKey];
实际上会被调用。所以我会在那里看看这个bug。您可以添加日志语句或设置断点,以验证是否实际遵循了该代码路径。
plist解析和Web数据提取可能需要一些时间。这表明您应该在后台执行这些操作,可能使用GCD队列。你事先并不知道他们中的任何一个是否会成功。因此,在完成之前不要设置首选项。
附注:您可以查询首选项数据库中的BOOL,使您的代码更短,因此更易于阅读。
[prefs setBool:YES forKey:appFirstStartOfVersionKey];
和
numberWithBool:
您也可以仅使用@(YES)
和@(NO)
替换-applicationDidFinishLaunchingWithOptions:
。
对于你的程序逻辑,我建议像这样:
shouldLoadPlistData
中,检查是否已加载起始plist数据。忘掉它是否是第一次运行。只需查看是否需要加载plist数据。也许称之为latestPlistVersionLoaded
。或者您可能需要将其与正在运行的版本联系起来,在这种情况下,您将存储字符串shouldLoadPlistData
。NO
设置为parseListIntoCD
,注意不再需要加载plist数据。如果由于某种原因,plist加载失败(可能是手机电量耗尽或者你的应用程序被用户或系统杀死),那么在下次启动时你就会回到你开始的地方。如果数据很大,您可能需要检查此工作:
如果您的应用程序在下载过程中退出,此阶段检查点还可让您向系统询问额外时间。
shouldLoadPlist
对我来说有点臃肿。它的名字不仅仅是它的意思。也许你可以将它重构为一个支票(importPlist:intoContext:
),一个执行导入的方法(NSManagedObjectContext
),以及一个触发同步的方法。
我强烈建议您将工作[SDCoreDataController sharedInstance]
作为参数传递,而不是让一些全局对象分配MOC(如NSError **
所示。)它为您提供了更多控制,并允许你可以更容易地编写单元测试。如果你也传递了plist的路径,那么你现在拥有干净的代码,每次调用它时都应该采用相同的方式。
您对if (![someObject doTaskWithObject:foo error:&error]) {
// handle the error
}
参数的使用始终不正确。成功时,NSError的值未定义。您必须测试操作的结果,而不是错误的值,以确定您是否成功。这个成语总是
countForFetchRequest:error
另请参阅{{1}}。通过执行获取和计数结果,它将为您提供当前正在提取的相同信息,但无需实例化NSManagedObjects。