2013-02-08 11 views
7

Pracuję nad moim projektem dyplomowym, który obejmuje klienta iOS z bazą danych Core Data i serwerem Ruby on Rails. Używam RestKit do komunikacji między nimi. Obecnie mam duży problem coraz cały system pracy: jak próbuję mapować do obiektów odpowiedź z serwera, otrzymuję następujący wyjątek:RestKit zawiesza się, ponieważ NSManagedObjectContext jest zerowy w RKResponseMapperOperation

2013-02-08 22:40:43.947 App[66735:5903] *** Assertion failure in -[RKManagedObjectResponseMapperOperation performMappingWithObject:error:], ~/Repositories/App/RestKit/Code/Network/RKResponseMapperOperation.m:358 
2013-02-08 23:04:30.562 App[66735:5903] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unable to perform mapping: No `managedObjectContext` assigned. (Mapping response.URL = http://localhost:3000/contacts?auth_token=s78UFMq8mCQrr12GZcyx)' 
*** First throw call stack: 
(0x1de9012 0x1c0ee7e 0x1de8e78 0x16a4f35 0x8f56e 0x8d520 0x1647d23 0x1647a34 0x16d4301 0x23a253f 0x23b4014 0x23a52e8 0x23a5450 0x90ac6e12 0x90aaecca) 
libc++abi.dylib: terminate called throwing an exception 

Próbuję załadować lista (tablica) kontaktów z serwera, które powinny zostać zapisane jako "Użytkownicy" w danych podstawowych.

Mam uporządkowany cały mój kod Dane podstawowe w Modelu danych klasy, jak widziałem w tym filmie: http://nsscreencast.com/episodes/11-core-data-basics. Oto ona:

plik nagłówka:

#import <Foundation/Foundation.h> 
#import <CoreData/CoreData.h> 

@interface AppDataModel : NSObject 

+ (id)sharedDataModel; 

@property (nonatomic, readonly) NSManagedObjectContext *mainContext; 
@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel; 
@property (nonatomic, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator; 

- (NSString *)modelName; 
- (NSString *)pathToModel; 
- (NSString *)storeFilename; 
- (NSString *)pathToLocalStore; 

@end 

plik Realizacja:

#import "AppDataModel.h" 

@interface AppDataModel() 

- (NSString *)documentsDirectory; 

@end 

@implementation AppDataModel 

@synthesize managedObjectModel = _managedObjectModel; 
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator; 
@synthesize mainContext = _mainContext; 

+ (id)sharedDataModel { 
    static AppDataModel *__instance = nil; 
    if (__instance == nil) { 
     __instance = [[AppDataModel alloc] init]; 
    } 

    return __instance; 
} 

- (NSString *)modelName { 
    return @"AppModels"; 
} 

- (NSString *)pathToModel { 
    return [[NSBundle mainBundle] pathForResource:[self modelName] 
              ofType:@"momd"]; 
} 

- (NSString *)storeFilename { 
    return [[self modelName] stringByAppendingPathExtension:@"sqlite"]; 
} 

- (NSString *)pathToLocalStore { 
    return [[self documentsDirectory] stringByAppendingPathComponent:[self storeFilename]]; 
} 

- (NSString *)documentsDirectory { 
    NSString *documentsDirectory = nil; 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    documentsDirectory = [paths objectAtIndex:0]; 
    return documentsDirectory; 
} 

- (NSManagedObjectContext *)mainContext { 
    if (_mainContext == nil) { 
     _mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
     _mainContext.persistentStoreCoordinator = [self persistentStoreCoordinator]; 
    } 

    return _mainContext; 
} 

- (NSManagedObjectModel *)managedObjectModel { 
    if (_managedObjectModel == nil) { 
     NSURL *storeURL = [NSURL fileURLWithPath:[self pathToModel]]; 
     _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:storeURL]; 
    } 

    return _managedObjectModel; 
} 

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { 
    if (_persistentStoreCoordinator == nil) { 
     NSLog(@"SQLITE STORE PATH: %@", [self pathToLocalStore]); 
     NSURL *storeURL = [NSURL fileURLWithPath:[self pathToLocalStore]]; 
     NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] 
              initWithManagedObjectModel:[self managedObjectModel]]; 
     NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
           [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, 
           [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 
     NSError *e = nil; 
     if (![psc addPersistentStoreWithType:NSSQLiteStoreType 
           configuration:nil 
             URL:storeURL 
            options:options 
             error:&e]) { 
      NSDictionary *userInfo = [NSDictionary dictionaryWithObject:e forKey:NSUnderlyingErrorKey]; 
      NSString *reason = @"Could not create persistent store."; 
      NSException *exc = [NSException exceptionWithName:NSInternalInconsistencyException 
                 reason:reason 
                userInfo:userInfo]; 
      @throw exc; 
     } 

     _persistentStoreCoordinator = psc; 
    } 

    return _persistentStoreCoordinator; 
} 
@end 

Klasa użytkownika jest dość proste, generowane automatycznie z Xcode.

plik nagłówka: plik

#import <Foundation/Foundation.h> 
#import <CoreData/CoreData.h> 


@interface User : NSManagedObject 

@property (nonatomic, retain) NSString * email; 
@property (nonatomic, retain) NSString * firstName; 
@property (nonatomic, retain) NSString * lastName; 
@property (nonatomic, retain) NSNumber * userID; 

@end 

Realizacja:

#import "User.h" 

@implementation User 

@dynamic email; 
@dynamic firstName; 
@dynamic lastName; 
@dynamic userID; 

@end 

Podobnie jak w klasie modelu danych, mam serwera klasy menedżera które używać do komunikacji:

Header file:

#import <Foundation/Foundation.h> 
#import <RestKit/RestKit.h> 
#import "AppServerProtocol.h" 
#import "AppDataModel.h" 


@interface AppServer : NSObject <AppServerDelegate> 

+ (id)sharedInstance; 

@property (strong, nonatomic) RKObjectManager *objectManager; 
@property (strong, nonatomic) RKEntityMapping *userMapping; 

@end 

i wdrażania pliku:

#import "AppServer.h" 
#import "User.h" 
#import "Device.h" 
#import "Ping.h" 
#import "AppAppDelegate.h" 

@interface AppServer() 

@property BOOL initialized; 

@end 

@implementation AppServer 

+ (id)sharedInstance { 
    static AppServer *__instance = nil; 
    if (__instance == nil) { 
     __instance = [[AppServer alloc] init]; 
     __instance.initialized = NO; 
    } 

    if (![__instance initialized]) { 
     [__instance initServer]; 
    } 

    return __instance; 
} 

- (void)initServer { 
    // initialize RestKit 
    NSURL *baseURL = [NSURL URLWithString:@"http://localhost:3000"]; 
    _objectManager = [RKObjectManager managerWithBaseURL:baseURL]; 

    // enable activity indicator spinner 
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES; 

    // initialize managed object store 
    _objectManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[[AppDataModel sharedDataModel] managedObjectModel]]; 

    _userMapping = [RKEntityMapping mappingForEntityForName:@"User" inManagedObjectStore:_objectManager.managedObjectStore]; 
    [_userMapping addAttributeMappingsFromDictionary:@{ 
    @"email" : @"email", 
    @"firstName" : @"first_name", 
    @"lastName" : @"last_name" 
    }]; 
    [_userMapping setIdentificationAttributes: @[@"userID"]]; 

    RKResponseDescriptor *contactsResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:_userMapping pathPattern:@"/contacts" keyPath:nil statusCodes:nil]; 
    [_objectManager addResponseDescriptor:contactsResponseDescriptor]; 

    _initialized = YES; 
} 

// contacts 
- (void)getContactsForCurrentUser { 
    NSString *authToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppAuthenticationToken"]; 

    [_objectManager getObjectsAtPath:@"/contacts" parameters:@{@"auth_token": authToken} success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) { 
     RKLogInfo(@"Load collection of contacts: %@", mappingResult.array); 
    } failure:^(RKObjectRequestOperation *operation, NSError *error) { 
     RKLogError(@"Operation failed with error: %@", error); 
    }]; 

} 

@end 

Więc kiedy otworzyć Kontakt Table View, który jest prawidłowo skonfigurowany do korzystania z idącą kontroler Wyniki (z powodzeniem pociągnięcie podmiotów spośród DB), mam niebezpieczną odświeżanie przycisk, który wywołuje metodę właśnie przeczytać powyżej:

- (void)downloadContacts { 
    [[AppServer sharedInstance] getContactsForCurrentUser]; 
} 

Oto format odpowiedź:

[ 
    { 
     "created_at":"2013-01-11T14:03:57Z", 
     "email":"[email protected]", 
     "first_name":"John", 
     "id":2, 
     "last_name":"Doe", 
     "updated_at":"2013-02-07T10:57:16Z" 
    }, 
    { 
     "created_at":"2013-01-11T14:03:57Z", 
     "email":"[email protected]", 
     "first_name":"Jane", 
     "id":3, 
     "last_name":"Doe", 
     "updated_at":"2013-02-07T10:57:16Z" 
} 
] 

A przed wyłączeniem konsoli stany następujące:

2013-02-08 22:40:36.892 App[66735:c07] I restkit:RKLog.m:34 RestKit logging initialized... 
2013-02-08 22:40:36.994 App[66735:c07] SQLITE STORE PATH: ~/Library/Application Support/iPhone Simulator/6.0/Applications/D735548F-DF42-4E13-A7EF-53DF0C5D8F3B/Documents/AppModels.sqlite 
2013-02-08 22:40:37.001 App[66735:c07] Context is ready! 
2013-02-08 22:40:43.920 App[66735:c07] I restkit.network:RKHTTPRequestOperation.m:154 GET 'http://localhost:3000/contacts?auth_token=s78UFMq8mCQrr12GZcyx' 
2013-02-08 22:40:43.945 App[66735:c07] I restkit.network:RKHTTPRequestOperation.m:181 

Linia biblioteki RestKit, że nie przed całą jest wyjątek jest:

NSAssert(self.managedObjectContext, @"Unable to perform mapping: No `managedObjectContext` assigned. (Mapping response.URL = %@)", self.response.URL); 

Podążyłem za to powrotem do metody initServer w AppServer.m plik, w którym, przed powrotem metod, właściwości klasy RKObjectManager są tak: http://imgur.com/LM5ZU9m

Jak już błędów, mam prześledzić, że problem nie jest ze strony serwera lub przekazywania aplikacja - widzę, że JSON został odebrany i zserializowany w macierzy, ale w momencie, gdy zostanie przekazana do następnej metody, która ma zapisać ją w Core Data, cała aplikacja przechodzi kaboom z powodu NSAssert kontekstu managed obiektu.

Każda pomoc jest bardzo doceniana!

Odpowiedz

4

Po kilku dniach debugowania, w końcu dowiedziałem się, co poszło nie tak: wygląda na to, że musiałem również ustawić ścieżkę do mojego lokalnego magazynu trwałości i samodzielnie wygenerować konteksty zarządzane obiektów dla zarządzanego magazynu obiektów.

Oto gdzie znalazłem rozwiązanie: https://github.com/RestKit/RestKit/issues/1221#issuecomment-13327693

Właśnie dodałem kilka wierszy w moim metody init, Serwer:

NSError *error = nil; 
NSString *pathToPSC = [[AppDataModel sharedDataModel] pathToLocalStore]; 

_objectManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[[AppDataModel sharedDataModel] managedObjectModel]]; 
[_objectManager.managedObjectStore addSQLitePersistentStoreAtPath:pathToPSC fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error]; 

if (error != nil) { 
    NSLog(@"\nSerious object store error!\n"); 
    return; 
} else { 
    [_objectManager.managedObjectStore createManagedObjectContexts]; 
} 
2

udało mi się to zrobić za pomocą tej funkcji RKApplicationDataDirectory() uzyskać aplikację katalog i ustaw moją ścieżkę do bazy danych.

// Initialize HTTPClient 
NSURL *baseURL = [NSURL URLWithString:@"http://myapiaddress.com"]; 
AFHTTPClient* client = [[AFHTTPClient alloc] initWithBaseURL:baseURL]; 
//we want to work with JSON-Data 
[client setDefaultHeader:@"Accept" value:RKMIMETypeJSON]; 

// Initialize RestKit 
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client]; 
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"NameOfMyCoreDataModel" ofType:@"momd"]]; 

//Iniitalize CoreData with RestKit 
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy]; 
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel]; 
NSError *error = nil; 

NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"nameOfDB.sqlite"]; 

objectManager.managedObjectStore = managedObjectStore; 

[objectManager.managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error]; 

[objectManager.managedObjectStore createManagedObjectContexts]; 
Powiązane problemy