使用 Parse 同步数据,而不是下载数据
Posted
技术标签:
【中文标题】使用 Parse 同步数据,而不是下载数据【英文标题】:Syncing Data with Parse, not downloading data 【发布时间】:2013-08-27 02:50:54 【问题描述】:我一直在关注 RayWenderlinch.com 的 How To Synchronize Core Data with a Web Service – Part 1 我正在尝试将其自定义为我的应用程序,但在使用 AFNetworking 从 Parse 实际下载数据时遇到问题
应用程序应该连接解析,查看两个类“Club”和“IronSet”检查是否有新记录(或在初始运行时,抓取所有内容)并仅下载新添加的内容。
然后它将这些记录保存到核心数据中,然后从 Cache/JSONRecords/Club(或 IronSet)中删除文件。似乎我从来没有真正从 Parse 中获取数据,尽管它连接成功,并且在从缓存中删除文件之前不会引发错误。
我收到“所有操作已完成”,表明 SyncEngine 应该已完成,并在 downloadDataForRegisteredObjects
中下载
错误
2013-08-26 19:39:03.981 WGT Golf Calculator[3287:c07] All operations completed
2013-08-26 19:39:03.991 WGT Golf Calculator[3287:c07] Unable to delete JSON records at Club -- file://localhost/Users/**/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/4B72F57E-264D-44F7-981D-3D921B0CC2A4/Library/Caches/JSONRecords/, reason Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed. (Cocoa error 4.)" UserInfo=0x75610f0 NSUnderlyingError=0x75615e0 "The operation couldn’t be completed. No such file or directory", NSFilePath=/Users/**/Library/Application Support/iPhone Simulator/6.1/Applications/4B72F57E-264D-44F7-981D-3D921B0CC2A4/Library/Caches/JSONRecords/Club, NSUserStringVariant=(
Remove
)
MLVAppDelegate.m
#import "MLVAppDelegate.h"
#import "MLVSyncEngine.h"
#import "Club.h"
#import "IronSet.h"
@implementation MLVAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
[[MLVSyncEngine sharedEngine] registerNSManagedObjectClassToSync:[Club class]];
[[MLVSyncEngine sharedEngine] registerNSManagedObjectClassToSync:[IronSet class]];
return YES;
MLVAFParseAPIClient.h
#import "AFHTTPClient.h"
@interface MLVAFParseAPIClient : AFHTTPClient
+ (MLVAFParseAPIClient *)sharedClient;
- (NSMutableURLRequest *)GETRequestForClass:(NSMutableString *)className parameters:(NSDictionary *)parameters;
- (NSMutableURLRequest *)GETRequestForAllRecordsOfClass:(NSString *)className updatedAfterDate:(NSDate *)updatedDate;
@end
MLVAFParseAPIClient.m
#import "MLVAFParseAPIClient.h"
#import "AFJSONRequestOperation.h"
static NSString * const kSDFParseAPIBaseURLString = @"https://api.parse.com/1/";
static NSString * const kSDFParseAPIApplicationId = @"APP ID REMOVED";
static NSString * const kSDFParseAPIKey = @"API KEY REMOVED";
@implementation MLVAFParseAPIClient
+ (MLVAFParseAPIClient *)sharedClient
static MLVAFParseAPIClient *sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^sharedClient = [[MLVAFParseAPIClient alloc] initWithBaseURL:[NSURL URLWithString:kSDFParseAPIBaseURLString]];
);
return sharedClient;
- (id)initWithBaseURL:(NSURL *)url
self = [super initWithBaseURL:url];
if (self)
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setParameterEncoding:AFJSONParameterEncoding];
[self setDefaultHeader:@"X-Parse-Application-Id" value:kSDFParseAPIApplicationId];
[self setDefaultHeader:@"X-Parse-REST-API-Key" value:kSDFParseAPIKey];
return self;
- (NSMutableURLRequest *)GETRequestForClass:(NSMutableString *)className parameters:(NSDictionary *)parameters
NSMutableURLRequest *request = nil;
request = [self requestWithMethod:@"GET" path:[NSString stringWithFormat:@"classes/%@", className] parameters:parameters ];
return request;
- (NSMutableURLRequest *)GETRequestForAllRecordsOfClass:(NSString *)className updatedAfterDate:(NSDate *)updatedDate
NSMutableURLRequest *request = nil;
NSDictionary *parameters = nil;
if (updatedDate)
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.'999Z'"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
NSString *jsonString = [NSString stringWithFormat:@"\"updatedAt\":\"$gte\":\"__type\":\"Date\",\"iso\":\"%@\"", [dateFormatter stringFromDate:updatedDate]];
parameters = [NSDictionary dictionaryWithObject:jsonString forKey:@"where"];
request = [self GETRequestForClass:className parameters:parameters];
return request;
@end
MLVCoreDataController.m
#import "MLVCoreDataController.h"
@interface MLVCoreDataController ()
@property (strong, nonatomic) NSManagedObjectContext *masterManagedObjectContext;
@property (strong, nonatomic) NSManagedObjectContext *backgroundManagedObjectContext;
@property (strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@end
@implementation MLVCoreDataController
@synthesize masterManagedObjectContext = _masterManagedObjectContext;
@synthesize backgroundManagedObjectContext = _backgroundManagedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
+ (id)sharedInstance
static dispatch_once_t once;
static MLVCoreDataController *sharedInstance;
dispatch_once(&once, ^
sharedInstance = [[self alloc] init];
);
return sharedInstance;
#pragma mark - Core Data stack
// Used to propegate saves to the persistent store (disk) without blocking the UI
- (NSManagedObjectContext *)masterManagedObjectContext
if (_masterManagedObjectContext != nil)
return _masterManagedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
_masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_masterManagedObjectContext performBlockAndWait:^
[_masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
];
return _masterManagedObjectContext;
// Return the NSManagedObjectContext to be used in the background during sync
- (NSManagedObjectContext *)backgroundManagedObjectContext
if (_backgroundManagedObjectContext != nil)
return _backgroundManagedObjectContext;
NSManagedObjectContext *masterContext = [self masterManagedObjectContext];
if (masterContext != nil)
_backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_backgroundManagedObjectContext performBlockAndWait:^
[_backgroundManagedObjectContext setParentContext:masterContext];
];
return _backgroundManagedObjectContext;
// Return the NSManagedObjectContext to be used in the background during sync
- (NSManagedObjectContext *)newManagedObjectContext
NSManagedObjectContext *newContext = nil;
NSManagedObjectContext *masterContext = [self masterManagedObjectContext];
if (masterContext != nil)
newContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[newContext performBlockAndWait:^
[newContext setParentContext:masterContext];
];
return newContext;
- (void)saveMasterContext
[self.masterManagedObjectContext performBlockAndWait:^
NSError *error = nil;
BOOL saved = [self.masterManagedObjectContext save:&error];
if (!saved)
// do some real error handling
NSLog(@"Could not save master context due to %@", error);
];
- (void)saveBackgroundContext
[self.backgroundManagedObjectContext performBlockAndWait:^
NSError *error = nil;
BOOL saved = [self.backgroundManagedObjectContext save:&error];
if (!saved)
// do some real error handling
NSLog(@"Could not save background context due to %@", error);
];
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
if (_managedObjectModel != nil)
return _managedObjectModel;
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"WGTCalculator" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
if (_persistentStoreCoordinator != nil)
return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"WGTCalcul.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
return _persistentStoreCoordinator;
#pragma mark - Application's Documents directory
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
@end
MLVSyncEngine.h
#import <Foundation/Foundation.h>
typedef enum
MLVObjectSynced = 0,
MLVObjectCreated,
MLVObjectDeleted,
MLVObjectSyncStatus;
@interface MLVSyncEngine : NSObject
@property (atomic, readonly) BOOL syncInProgress;
+ (MLVSyncEngine *)sharedEngine;
- (void)registerNSManagedObjectClassToSync:(Class)aClass;
- (void)startSync;
@end
MLVSyncEngine.m
#import "MLVSyncEngine.h"
#import "MLVCoreDataController.h"
#import "MLVAFParseAPIClient.h"
#import "AFHTTPRequestOperation.h"
#import "AFJSONRequestOperation.h"
NSString * const kMLVSyncEngineInitialCompleteKey = @"MLVSyncEngineInitialSyncCompleted";
NSString * const kMLVSyncEngineSyncCompletedNotificationName = @"MLVSyncEngineSyncCompleted";
@interface MLVSyncEngine ()
@property (nonatomic, strong) NSMutableArray *registeredClassesToSync;
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@end
@implementation MLVSyncEngine
@synthesize registeredClassesToSync = _registeredClassesToSync;
@synthesize syncInProgress = _syncInProgress;
@synthesize dateFormatter = _dateFormatter;
+ (MLVSyncEngine *)sharedEngine
static MLVSyncEngine *sharedEngine = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
sharedEngine = [[MLVSyncEngine alloc] init];
);
return sharedEngine;
- (void)registerNSManagedObjectClassToSync:(Class)aClass
if (!self.registeredClassesToSync)
self.registeredClassesToSync = [NSMutableArray array];
if ([aClass isSubclassOfClass:[NSManagedObject class]])
if (![self.registeredClassesToSync containsObject:NSStringFromClass(aClass)])
[self.registeredClassesToSync addObject:NSStringFromClass(aClass)];
else
NSLog(@"Unable to register %@ as it is already registered", NSStringFromClass(aClass));
else
NSLog(@"Unable to reguster %@ as it is not a subclass of NSManagedObject", NSStringFromClass(aClass));
- (BOOL)initialSyncComplete
return [[[NSUserDefaults standardUserDefaults] valueForKey:kMLVSyncEngineInitialCompleteKey] boolValue];
- (void)setInitialSyncCompleted
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] forKey:kMLVSyncEngineInitialCompleteKey];
[[NSUserDefaults standardUserDefaults] synchronize];
- (void)executeSyncCompletedOperations
dispatch_async(dispatch_get_main_queue(), ^
[self setInitialSyncCompleted];
NSError *error = nil;
[[MLVCoreDataController sharedInstance] saveBackgroundContext];
if (error)
NSLog(@"Error saving background context after creating objects on server: %@", error);
[[MLVCoreDataController sharedInstance] saveMasterContext];
[[NSNotificationCenter defaultCenter]
postNotificationName:kMLVSyncEngineSyncCompletedNotificationName
object:nil];
[self willChangeValueForKey:@"syncInProgress"];
_syncInProgress = NO;
[self didChangeValueForKey:@"syncInProgress"];
);
- (void)startSync
if (!self.syncInProgress)
[self willChangeValueForKey:@"syncInProgress"];
_syncInProgress = YES;
[self didChangeValueForKey:@"syncInProgress"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^[self downloadDataForRegisteredObjects:YES];
);
- (NSDate *)mostRecentUpdatedAtDateForEntityWithName:(NSString *)entityName
__block NSDate *date = nil;
//
// Create a new fetch request for the specified entity
//
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
//
// Set the sort descriptors on the request to sort by updatedAt in descending order
//
[request setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:@"updatedAt" ascending:NO]]];
//
// You are only interested in 1 result so limit the request to 1
//
[request setFetchLimit:1];
[[[MLVCoreDataController sharedInstance] backgroundManagedObjectContext] performBlockAndWait:^
NSError *error = nil;
NSArray *results = [[[MLVCoreDataController sharedInstance] backgroundManagedObjectContext] executeFetchRequest:request error:&error];
if ([results lastObject])
//
// Set date to the fetched result
//
date = [[results lastObject] valueForKey:@"updatedAt"];
];
return date;
- (void)newManagedObjectWithClassName:(NSString *)className forRecord:(NSDictionary *)record
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:className inManagedObjectContext:[[MLVCoreDataController sharedInstance] backgroundManagedObjectContext]];
[record enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
[self setValue:obj forKey:key forManagedObject:newManagedObject];
];
[record setValue:[NSNumber numberWithInt:MLVObjectSynced] forKey:@"syncStatus"];
- (void)updateManagedObject:(NSManagedObject *)managedObject withRecord:(NSDictionary *)record
[record enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
[self setValue:obj forKey:key forManagedObject:managedObject];
];
- (void)setValue:(id)value forKey:(NSString *)key forManagedObject:(NSManagedObject *)managedObject
if ([key isEqualToString:@"createdAt"] || [key isEqualToString:@"updatedAt"])
NSDate *date = [self dateUsingStringFromAPI:value];
[managedObject setValue:date forKey:key];
else if ([value isKindOfClass:[NSDictionary class]])
if ([value objectForKey:@"__type"])
NSString *dataType = [value objectForKey:@"__type"];
if ([dataType isEqualToString:@"Date"])
NSString *dateString = [value objectForKey:@"iso"];
NSDate *date = [self dateUsingStringFromAPI:dateString];
[managedObject setValue:date forKey:key];
else if ([dataType isEqualToString:@"File"])
NSString *urlString = [value objectForKey:@"url"];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *dataResponse = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[managedObject setValue:dataResponse forKey:key];
else
NSLog(@"Unknown Data Type Received");
[managedObject setValue:nil forKey:key];
else
[managedObject setValue:value forKey:key];
- (NSArray *)managedObjectsForClass:(NSString *)className withSyncStatus:(MLVObjectSyncStatus)syncStatus
__block NSArray *results = nil;
NSManagedObjectContext *managedObjectContext = [[MLVCoreDataController sharedInstance] backgroundManagedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:className];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"syncStatus = %d", syncStatus];
[fetchRequest setPredicate:predicate];
[managedObjectContext performBlockAndWait:^
NSError *error = nil;
results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
];
return results;
- (NSArray *)managedObjectsForClass:(NSString *)className sortedByKey:(NSString *)key usingArrayOfIds:(NSArray *)idArray inArrayOfIds:(BOOL)inIds
__block NSArray *results = nil;
NSManagedObjectContext *managedObjectContext = [[MLVCoreDataController sharedInstance] backgroundManagedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:className];
NSPredicate *predicate;
if (inIds)
predicate = [NSPredicate predicateWithFormat:@"objectId IN %@", idArray];
else
predicate = [NSPredicate predicateWithFormat:@"NOT (objectId IN %@)", idArray];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:@"objectId" ascending:YES]]];
[managedObjectContext performBlockAndWait:^
NSError *error = nil;
results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
];
return results;
- (void)downloadDataForRegisteredObjects:(BOOL)useUpdatedAtDate
NSMutableArray *operations = [NSMutableArray array];
for (NSString *className in self.registeredClassesToSync)
NSDate *mostRecentUpdatedDate = nil;
if (useUpdatedAtDate)
mostRecentUpdatedDate = [self mostRecentUpdatedAtDateForEntityWithName:className];
NSMutableURLRequest *request = [[MLVAFParseAPIClient sharedClient]
GETRequestForAllRecordsOfClass:className
updatedAfterDate:mostRecentUpdatedDate];
AFHTTPRequestOperation *operation = [[MLVAFParseAPIClient sharedClient] HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject)
if ([responseObject isKindOfClass:[NSDictionary class]])
[self writeJSONResponse:responseObject toDiskForClassWithName:className];
NSLog(@"Response for %@: %@", className, responseObject);
failure:^(AFHTTPRequestOperation *operation, NSError *error)
NSLog(@"Request for class %@ failed with error: %@", className, error);
];
[operations addObject:operation];
[[MLVAFParseAPIClient sharedClient] enqueueBatchOfHTTPRequestOperations:operations progressBlock:^(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations)
completionBlock:^(NSArray *operations)
NSLog(@"All operations completed");
[self processJSONDataRecordsIntoCoreData];
];
- (void)processJSONDataRecordsIntoCoreData
NSManagedObjectContext *managedObjectContext = [[MLVCoreDataController sharedInstance] backgroundManagedObjectContext];
//
// Iterate over all registered classes to sync
//
for (NSString *className in self.registeredClassesToSync)
if (![self initialSyncComplete])
NSDictionary *JSONDictionary = [self JSONDictionaryForClassWithName:className];
NSArray *records = [JSONDictionary objectForKey:@"results"];
for (NSDictionary *record in records)
[self newManagedObjectWithClassName:className forRecord:record];
else
NSArray *downloadedRecords = [self JSONDataRecordsForClass:className sortedByKey:@"objectId"];
if ([downloadedRecords lastObject])
NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:@"objectId" usingArrayOfIds:[downloadedRecords valueForKey:@"objectId"] inArrayOfIds:YES];
int currentIndex = 0;
for (NSDictionary *record in downloadedRecords)
NSManagedObject *storedManagedObject = nil;
if ([storedRecords count] > currentIndex)
storedManagedObject = [storedRecords objectAtIndex:currentIndex];
if ([[storedManagedObject valueForKey:@"objectId"] isEqualToString:[record valueForKey:@"objectId"]])
[self updateManagedObject:[storedRecords objectAtIndex:currentIndex] withRecord:record];
else
[self newManagedObjectWithClassName:className forRecord:record];
currentIndex++;
[managedObjectContext performBlockAndWait:^
NSError *error = nil;
if (![managedObjectContext save:&error])
NSLog(@"Unable to save context for class %@", className);
];
[self deleteJSONDataRecordsForClassWithName:className];
[self executeSyncCompletedOperations];
- (void)initializeDateFormatter
if (!self.dateFormatter)
self.dateFormatter = [[NSDateFormatter alloc] init];
[self.dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
[self.dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
- (NSDate *)dateUsingStringFromAPI:(NSString *)dateString
[self initializeDateFormatter];
dateString = [dateString substringWithRange:NSMakeRange(0, [dateString length]-5)];
return [self.dateFormatter dateFromString:dateString];
- (NSString *)dateStringForAPIUsingDate:(NSDate *)date
[self initializeDateFormatter];
NSString *dateString = [self.dateFormatter stringFromDate:date];
dateString = [dateString substringWithRange:NSMakeRange(0, [dateString length]-1)];
dateString = [dateString stringByAppendingFormat:@".000Z"];
return dateString;
#pragma mark - File Management
- (NSURL *)applicationCacheDirectory
return [[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
- (NSURL *)JSONDataRecordsDirectory
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *url = [NSURL URLWithString:@"JSONRecords/" relativeToURL:[self applicationCacheDirectory]];
NSError *error = nil;
if (![fileManager fileExistsAtPath:[url path]])
[fileManager createDirectoryAtPath:[url path] withIntermediateDirectories:YES attributes:nil error:&error];
return url;
-(void)writeJSONResponse:(id)response toDiskForClassWithName:(NSString *)className
NSURL *fileURL = [NSURL URLWithString:className relativeToURL:[self JSONDataRecordsDirectory]] ;
if (![(NSDictionary *)response writeToFile:[fileURL path] atomically:YES])
NSLog(@"Error saving response to disk, will attempt to remove NSNull values and try again.");
//remove NSNulls and try again...
NSArray *records = [response objectForKey:@"results"];
NSMutableArray *nullFreeRecords = [NSMutableArray array];
for (NSDictionary *record in records)
NSMutableDictionary *nullFreeRecord = [NSMutableDictionary dictionaryWithDictionary:record];
[record enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop)
if ([obj isKindOfClass:[NSNull class]])
[nullFreeRecord setValue:nil forKey:key];
];
[nullFreeRecords addObject:nullFreeRecord];
NSDictionary *nullFreeDictionary = [NSDictionary dictionaryWithObject:nullFreeRecords forKey:@"results"];
if (![nullFreeDictionary writeToFile:[fileURL path] atomically:YES])
NSLog(@"Failed all attempts to save response to disk: %@", response);
- (void)deleteJSONDataRecordsForClassWithName:(NSString *)className
NSURL *url = [NSURL URLWithString:className relativeToURL:[self JSONDataRecordsDirectory]];
NSError *error = nil;
BOOL deleted = [[NSFileManager defaultManager] removeItemAtURL:url error:&error];
if (!deleted)
NSLog(@"Unable to delete JSON records at %@, reason %@", url, error);
- (NSDictionary *)JSONDictionaryForClassWithName:(NSString *)className
NSURL *fileURL = [NSURL URLWithString:className relativeToURL:[self JSONDataRecordsDirectory]];
return [NSDictionary dictionaryWithContentsOfURL:fileURL];
- (NSArray *)JSONDataRecordsForClass:(NSString *)className sortedByKey:(NSString *)key
NSDictionary *JSONDictionary = [self JSONDictionaryForClassWithName:className];
NSArray *records = [JSONDictionary objectForKey:@"results"];
return [records sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:key ascending:YES]]];
@end
【问题讨论】:
我已经删除了 MLVDataController.h 和 CoreData 模型文件,因为它超过了字符数限制。我知道粘贴网站的格式不好,但它们看起来像 codetidy.com/6540 只是好奇,为什么不直接使用 ios 框架,并为 PFObjects 发出查询? 在执行 saveJSONtoDisk 方法之前,您是否看到 parse.com HTTP 响应的 NSLog() 结果?如果在 saveJSONResponse 方法中设置断点会发生什么?为什么目录不存在,除非它们从未被创建? 【参考方案1】:老实说,如果您的数据模型如此简单,并且您在每次加载时都删除本地持久存储/缓存,那么不使用 Core Data 可能会好得多。
通过根据需要加载数据来保持简单。使用NSCoding
保存一个缓存,最初作为占位符加载,同时应用等待下载新信息。
【讨论】:
这只是数据模型的起点。一旦用户从 Core Data 中选择了项目,它们就会被缓存。使用 Parse 是为了能够根据需要加载新项目。 PS。我刚刚在 NSCoding 上搜索了 NSCoding,看到了你的名字并意识到我刚刚在 Helios 上收听了你的播客。好东西!【参考方案2】:事实证明答案非常简单,本教程使用的是旧版本的 AFNetworking,其中 AFHTTPRequestOperation 可以下载数据但不将其视为 JSON。
AFHTTPRequestOperation *operation = [[MLVAFParseAPIClient sharedClient] HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject)
if ([responseObject isKindOfClass:[NSDictionary class]])
[self writeJSONResponse:responseObject toDiskForClassWithName:className];
NSLog(@"Response for %@: %@", className, responseObject);
需要用 AFJSONRequestOperation 更新
AFJSONRequestOperation *operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest: request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
NSDictionary * responseObject = (NSDictionary * )JSON;
if ([responseObject isKindOfClass:[NSDictionary class]])
NSLog(@"Response for %@: %@", className, responseObject);
[self writeJSONResponse:responseObject toDiskForClassWithName:className];
【讨论】:
以上是关于使用 Parse 同步数据,而不是下载数据的主要内容,如果未能解决你的问题,请参考以下文章
Android:如何使用 Parse.com 的 Bolts 同步查询?