2013-02-08 49 views
7

我正在研究我的文凭项目,其中包括iOS客户端与核心数据数据库和Ruby on Rails服务器。我正在使用RestKit进行它们之间的通信。目前,我有一个大问题,让整个系统的工作:当我尝试映射到从服务器对象的响应,我得到以下异常RestKit崩溃,因为NSManagedObjectContext是零在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 

我试图加载来自服务器的联系人列表(一个数组),应该将其保存为核心数据中的“用户”。

我在数据模型类中构建了我所有的核心数据代码,就像我在本视频中看到的:http://nsscreencast.com/episodes/11-core-data-basics。那就是:

头文件

#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 

实现文件:

#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 

User类是非常简单的,自动生成和Xcode。

头文件

#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 

实现文件:

#import "User.h" 

@implementation User 

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

@end 

就像数据模型类,我有一个服务器管理器类我使用的通信:

页眉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 

而且实现文件:

#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 

所以,当我打开联系人表视图,它设置正确使用已取得的成果控制器(成功拉动实体走出DB),我有一个危险的刷新按钮,它调用你刚才上面阅读方法:

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

这里是响应格式:

[ 
    { 
     "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" 
} 
] 

和异常控制台前状态如下:

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 

的RestKit库的线,被抛出整个异常之前失败的:

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

我已经跟着回到AppServer中的initServer方法。米文件,在其中,该方法返回前,RKObjectManager类的的性质是这样的:http://imgur.com/LM5ZU9m

正如我所调试,我已经跟踪的问题是无法与服务器侧或所述通信该应用程序 - 我可以看到JSON接收并反序列化到一个数组中,但是当它传递给下一个应该将其保存到Core Data的方法时,整个应用程序将由于受管对象上下文的NSAssert而进入kaboom。

任何帮助非常感谢!

回答

4

经过几天的调试,我终于发现出了什么问题:它看起来像我也必须设置路径到我的本地持久存储,并自己为托管对象存储生成托管对象上下文。

此处,我找到了解决方法:https://github.com/RestKit/RestKit/issues/1221#issuecomment-13327693

我刚刚加入我的服务器init()方法的几行:使用此功能RKApplicationDataDirectory()得到应用

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

我设法做到这一点目录并设置我的数据库路径。

// 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];