2013-08-27 3 views
1

핵심 데이터를 웹 서비스와 동기화하는 방법 - 1 부 : RayWenderlinch.com 응용 프로그램에 맞게 사용자 지정하고 실제 다운로드하는 데 문제가 있습니다. AFNetworking을 사용하여 구문 분석 한 데이터데이터를 구문 분석하고 데이터를 다운로드하지 않음

응용 프로그램은 구문 분석을 위해 두 클래스 "Club"및 "IronSet"검사를보고 새 레코드가 있는지 확인하거나 초기 실행시 모든 항목을 가져 와서 새로 추가 된 것들.

그런 다음 해당 레코드를 코어 데이터에 저장 한 다음 Cache/JSONRecords/Club (또는 IronSet)에서 파일을 삭제합니다. 그것은 성공적으로 연결하고 있지만 실제로는 파스에서 데이터를 잡는 적이 결코없는 것 같아요 캐시에서 파일을 삭제할 때까지 오류를 throw하지 않습니다.

내가 "완료 모든 작업을"점점 오전 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; 
}  
의 다운로드를 완료한다

MLVAFPars eAPIClient.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

012,351,
#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 
+0

으로 업데이트합니다.붙여 넣기 사이트가 나쁜 형태라는 것을 알고 있지만 http://codetidy.com/6540/처럼 보입니다 – thebusiness11

+0

그냥 궁금 해서요, 왜 iOS 프레임 워크를 사용하지 않고 PFObjects에 쿼리를 발행합니까? – Justin

+0

saveJSONtoDisk 메소드를 실행하기 전에 parse.com HTTP 응답의 NSLog() 결과가 표시됩니까? saveJSONResponse 메소드에 중단 점을 설정하면 어떻게됩니까? 그들이 만든 적이 없다면 왜 디렉토리가 존재하지 않을까요? – Justin

답변

2

솔직히, 당신은 아마 오프 훨씬 더 좋을 것이다 아니 핵심 데이터를 사용하여.

필요에 따라 데이터를로드하여 간단하게 유지하십시오. NSCoding을 사용하여 캐시를 저장하면 처음에 자리 표시 자로로드되고 앱은 새로운 정보가 다운로드 될 때까지 대기합니다.

+0

이것은 데이터 모델의 시작점 일뿐입니다. 사용자가 핵심 데이터에서 항목을 선택하면 캐시됩니다. 그리고 Parse를 사용하면 필요에 따라 새 항목을로드 할 수있었습니다. PS. 나는 NSCoding을 봤는데, 네 이름을보고, 방금 헬리오스에서 팟 캐스트를 들었다는 것을 깨달았다. 좋은 물건! – thebusiness11

1

대답은 매우 간단합니다.이 튜토리얼에서는 이전 버전의 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

나는 MLVDataController.h와 CoreData 모델 파일을 제거한
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]; 
관련 문제