2011-11-30 21:42:40 +00:00
|
|
|
//
|
|
|
|
// OPAppDelegate.m
|
|
|
|
// OnePassword
|
|
|
|
//
|
|
|
|
// Created by Maarten Billemont on 24/11/11.
|
|
|
|
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "OPAppDelegate.h"
|
|
|
|
|
|
|
|
#import "OPMainViewController.h"
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
@interface OPAppDelegate ()
|
|
|
|
|
|
|
|
+ (NSDictionary *)keyPhraseQuery;
|
|
|
|
+ (NSDictionary *)keyPhraseHashQuery;
|
|
|
|
|
2012-01-29 11:41:48 +00:00
|
|
|
- (void)forgetKeyPhrase;
|
|
|
|
- (void)loadStoredKeyPhrase;
|
|
|
|
- (void)askKeyPhrase;
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
@end
|
|
|
|
|
2011-11-30 21:42:40 +00:00
|
|
|
@implementation OPAppDelegate
|
|
|
|
|
|
|
|
@synthesize managedObjectModel = __managedObjectModel;
|
2012-01-29 11:41:48 +00:00
|
|
|
@synthesize managedObjectContext = __managedObjectContext;
|
2011-11-30 21:42:40 +00:00
|
|
|
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
|
2012-01-29 11:41:48 +00:00
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
@synthesize keyPhrase = _keyPhrase;
|
2012-01-29 11:41:48 +00:00
|
|
|
@synthesize keyPhraseHash = _keyPhraseHash;
|
|
|
|
@synthesize keyPhraseHashHex = _keyPhraseHashHex;
|
2011-11-30 21:42:40 +00:00
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
+ (void)initialize {
|
2012-01-29 11:41:48 +00:00
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
#ifdef DEBUG
|
2012-01-29 11:41:48 +00:00
|
|
|
[Logger get].autoprintLevel = LogLevelTrace;
|
2012-01-24 23:30:43 +00:00
|
|
|
[NSClassFromString(@"WebView") performSelector:@selector(_enableRemoteInspector)];
|
|
|
|
#endif
|
2012-01-05 00:44:15 +00:00
|
|
|
}
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
+ (NSDictionary *)keyPhraseQuery {
|
2012-01-05 00:44:15 +00:00
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
static NSDictionary *OPKeyPhraseQuery = nil;
|
|
|
|
if (!OPKeyPhraseQuery)
|
|
|
|
OPKeyPhraseQuery = [KeyChain createQueryForClass:kSecClassGenericPassword
|
2012-01-29 11:41:48 +00:00
|
|
|
attributes:[NSDictionary dictionaryWithObject:@"MasterPassword"
|
2012-01-19 16:40:39 +00:00
|
|
|
forKey:(__bridge id)kSecAttrService]
|
|
|
|
matches:nil];
|
|
|
|
|
|
|
|
return OPKeyPhraseQuery;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSDictionary *)keyPhraseHashQuery {
|
|
|
|
|
|
|
|
static NSDictionary *OPKeyPhraseHashQuery = nil;
|
|
|
|
if (!OPKeyPhraseHashQuery)
|
|
|
|
OPKeyPhraseHashQuery = [KeyChain createQueryForClass:kSecClassGenericPassword
|
2012-01-29 11:41:48 +00:00
|
|
|
attributes:[NSDictionary dictionaryWithObject:@"MasterPasswordHash"
|
2012-01-19 16:40:39 +00:00
|
|
|
forKey:(__bridge id)kSecAttrService]
|
|
|
|
matches:nil];
|
|
|
|
|
|
|
|
return OPKeyPhraseHashQuery;
|
2012-01-16 08:51:08 +00:00
|
|
|
}
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
|
|
|
|
|
|
UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
|
|
|
|
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsDefault];
|
|
|
|
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsLandscapePhone];
|
|
|
|
[[UINavigationBar appearance] setTitleTextAttributes:
|
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0], UITextAttributeTextColor,
|
|
|
|
[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8], UITextAttributeTextShadowColor,
|
|
|
|
[NSValue valueWithUIOffset:UIOffsetMake(0, -1)], UITextAttributeTextShadowOffset,
|
|
|
|
[UIFont fontWithName:@"Helvetica-Neue" size:0.0], UITextAttributeFont,
|
|
|
|
nil]];
|
|
|
|
|
|
|
|
UIImage *navBarButton = [[UIImage imageNamed:@"ui_navbar_button"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
|
|
|
|
UIImage *navBarBack = [[UIImage imageNamed:@"ui_navbar_back"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
|
|
|
|
[[UIBarButtonItem appearance] setBackgroundImage:navBarButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
|
|
|
[[UIBarButtonItem appearance] setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
|
|
|
|
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:navBarBack forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
|
|
|
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
|
|
|
|
[[UIBarButtonItem appearance] setTitleTextAttributes:
|
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0], UITextAttributeTextColor,
|
|
|
|
[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5], UITextAttributeTextShadowColor,
|
|
|
|
[NSValue valueWithUIOffset:UIOffsetMake(0, 1)], UITextAttributeTextShadowOffset,
|
|
|
|
[UIFont fontWithName:@"Helvetica-Neue" size:0.0], UITextAttributeFont,
|
|
|
|
nil]
|
|
|
|
forState:UIControlStateNormal];
|
2012-01-29 11:41:48 +00:00
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
UIImage *toolBarImage = [[UIImage imageNamed:@"ui_toolbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(25, 5, 5, 5)];
|
|
|
|
[[UISearchBar appearance] setBackgroundImage:toolBarImage];
|
|
|
|
[[UIToolbar appearance] setBackgroundImage:toolBarImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
|
|
|
|
|
|
|
|
/*
|
2012-01-29 11:41:48 +00:00
|
|
|
UIImage *minImage = [[UIImage imageNamed:@"slider-minimum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
|
|
|
|
UIImage *maxImage = [[UIImage imageNamed:@"slider-maximum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
|
|
|
|
UIImage *thumbImage = [UIImage imageNamed:@"slider-handle.png"];
|
|
|
|
|
|
|
|
[[UISlider appearance] setMaximumTrackImage:maxImage forState:UIControlStateNormal];
|
|
|
|
[[UISlider appearance] setMinimumTrackImage:minImage forState:UIControlStateNormal];
|
|
|
|
[[UISlider appearance] setThumbImage:thumbImage forState:UIControlStateNormal];
|
|
|
|
|
|
|
|
UIImage *segmentSelected = [[UIImage imageNamed:@"segcontrol_sel.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
|
|
|
|
UIImage *segmentUnselected = [[UIImage imageNamed:@"segcontrol_uns.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
|
|
|
|
UIImage *segmentSelectedUnselected = [UIImage imageNamed:@"segcontrol_sel-uns.png"];
|
|
|
|
UIImage *segUnselectedSelected = [UIImage imageNamed:@"segcontrol_uns-sel.png"];
|
|
|
|
UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.png"];
|
|
|
|
|
|
|
|
[[UISegmentedControl appearance] setBackgroundImage:segmentUnselected forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
|
|
|
[[UISegmentedControl appearance] setBackgroundImage:segmentSelected forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
|
|
|
|
|
|
|
|
[[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
|
|
|
[[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
|
|
|
[[UISegmentedControl appearance] setDividerImage:segUnselectedSelected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault];*/
|
2012-01-24 23:30:43 +00:00
|
|
|
|
2012-01-29 11:41:48 +00:00
|
|
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
|
|
|
}
|
|
|
|
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
2012-01-24 23:30:43 +00:00
|
|
|
|
2012-02-02 23:10:58 +00:00
|
|
|
if ([[OPConfig get].showQuickstart boolValue])
|
2012-02-02 13:56:09 +00:00
|
|
|
[self showGuide];
|
|
|
|
else
|
|
|
|
[self loadKeyPhrase];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)showGuide {
|
|
|
|
|
|
|
|
[self.navigationController performSegueWithIdentifier:@"OP_Guide" sender:self];
|
2012-01-29 11:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)loadKeyPhrase {
|
2012-01-24 23:30:43 +00:00
|
|
|
|
2012-01-29 11:41:48 +00:00
|
|
|
if ([[OPConfig get].forgetKeyPhrase boolValue]) {
|
|
|
|
[self forgetKeyPhrase];
|
|
|
|
return;
|
|
|
|
}
|
2012-01-24 23:30:43 +00:00
|
|
|
|
2012-01-29 11:41:48 +00:00
|
|
|
[self loadStoredKeyPhrase];
|
|
|
|
if (!self.keyPhrase) {
|
|
|
|
// Key phrase is not known. Ask user to set/specify it.
|
|
|
|
dbg(@"Key phrase not known. Will ask user.");
|
|
|
|
[self askKeyPhrase];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)forgetKeyPhrase {
|
2012-01-24 23:30:43 +00:00
|
|
|
|
2012-01-29 11:41:48 +00:00
|
|
|
dbg(@"Forgetting key phrase.");
|
|
|
|
[AlertViewController showAlertWithTitle:@"Changing Master Password"
|
|
|
|
message:
|
|
|
|
@"You've requested to change your master password.\n\n"
|
|
|
|
@"If you continue, your current sites and passwords will become unavailable.\n\n"
|
|
|
|
@"You can always change back to the old master password later.\n"
|
|
|
|
@"Your old sites and passwords will then become available again."
|
|
|
|
viewStyle:UIAlertViewStyleDefault
|
|
|
|
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
|
|
|
if (buttonIndex == [alert firstOtherButtonIndex]) {
|
|
|
|
// Key phrase reset. Delete it.
|
|
|
|
dbg(@"Deleting master key phrase and hash from key chain.");
|
|
|
|
[KeyChain deleteItemForQuery:[OPAppDelegate keyPhraseQuery]];
|
|
|
|
[KeyChain deleteItemForQuery:[OPAppDelegate keyPhraseHashQuery]];
|
|
|
|
}
|
|
|
|
|
|
|
|
[self loadKeyPhrase];
|
|
|
|
}
|
|
|
|
cancelTitle:[PearlStrings get].commonButtonAbort
|
|
|
|
otherTitles:[PearlStrings get].commonButtonContinue, nil];
|
|
|
|
[OPConfig get].forgetKeyPhrase = [NSNumber numberWithBool:NO];
|
2012-01-24 23:30:43 +00:00
|
|
|
}
|
2012-01-29 11:41:48 +00:00
|
|
|
|
|
|
|
- (void)loadStoredKeyPhrase {
|
2012-01-16 08:51:08 +00:00
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
if ([[OPConfig get].storeKeyPhrase boolValue]) {
|
|
|
|
// Key phrase is stored in keychain. Load it.
|
|
|
|
dbg(@"Loading master key phrase from key chain.");
|
|
|
|
NSData *keyPhraseData = [KeyChain dataOfItemForQuery:[OPAppDelegate keyPhraseQuery]];
|
|
|
|
dbg(@" -> Master key phrase %@.", keyPhraseData? @"found": @"NOT found");
|
|
|
|
|
|
|
|
self.keyPhrase = keyPhraseData? [[NSString alloc] initWithBytes:keyPhraseData.bytes length:keyPhraseData.length
|
|
|
|
encoding:NSUTF8StringEncoding]: nil;
|
|
|
|
} else {
|
|
|
|
// Key phrase should not be stored in keychain. Delete it.
|
|
|
|
dbg(@"Deleting master key phrase from key chain.");
|
|
|
|
[KeyChain deleteItemForQuery:[OPAppDelegate keyPhraseQuery]];
|
|
|
|
}
|
2012-01-29 11:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)askKeyPhrase {
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2012-01-29 11:41:48 +00:00
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
NSData *keyPhraseHash = [KeyChain dataOfItemForQuery:[OPAppDelegate keyPhraseHashQuery]];
|
|
|
|
dbg(@"Key phrase hash %@.", keyPhraseHash? @"known": @"NOT known");
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2012-01-29 11:41:48 +00:00
|
|
|
[AlertViewController showAlertWithTitle:@"One Password"
|
|
|
|
message:keyPhraseHash? @"Unlock with your master password:": @"Choose your master password:"
|
|
|
|
viewStyle:UIAlertViewStyleSecureTextInput
|
|
|
|
tappedButtonBlock:
|
|
|
|
^(UIAlertView *alert, NSInteger buttonIndex) {
|
|
|
|
if (buttonIndex == [alert cancelButtonIndex])
|
|
|
|
exit(0);
|
|
|
|
|
|
|
|
NSString *answer = [alert textFieldAtIndex:0].text;
|
|
|
|
if (![answer length]) {
|
|
|
|
// User didn't enter a key phrase.
|
|
|
|
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
|
|
|
|
message:@"No master password entered."
|
|
|
|
viewStyle:UIAlertViewStyleDefault
|
|
|
|
tappedButtonBlock:
|
|
|
|
^(UIAlertView *alert, NSInteger buttonIndex) {
|
|
|
|
exit(0);
|
|
|
|
} cancelTitle:@"Quit" otherTitles:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
NSData *answerHash = [answer hashWith:PearlDigestSHA512];
|
|
|
|
if (keyPhraseHash)
|
|
|
|
// A key phrase hash is known -> a key phrase is set.
|
|
|
|
// Make sure the user's entered key phrase matches it.
|
|
|
|
if (![keyPhraseHash isEqual:answerHash]) {
|
|
|
|
dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, answerHash);
|
|
|
|
|
|
|
|
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
|
2012-01-30 00:02:45 +00:00
|
|
|
message:
|
|
|
|
@"Incorrect master password.\n\n"
|
|
|
|
@"If you are trying to use the app with a different master password, "
|
|
|
|
@"flip the 'Change my password' option in Settings."
|
2012-01-29 11:41:48 +00:00
|
|
|
viewStyle:UIAlertViewStyleDefault
|
|
|
|
tappedButtonBlock:
|
|
|
|
^(UIAlertView *alert, NSInteger buttonIndex) {
|
|
|
|
exit(0);
|
|
|
|
} cancelTitle:@"Quit" otherTitles:nil];
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.keyPhrase = answer;
|
|
|
|
} cancelTitle:@"Quit" otherTitles:@"Unlock", nil];
|
|
|
|
});
|
2012-01-19 16:40:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationWillResignActive:(UIApplication *)application {
|
|
|
|
|
|
|
|
if (![[OPConfig get].rememberKeyPhrase boolValue])
|
|
|
|
self.keyPhrase = nil;
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationWillTerminate:(UIApplication *)application
|
|
|
|
{
|
|
|
|
// Saves changes in the application's managed object context before the application terminates.
|
|
|
|
[self saveContext];
|
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
+ (OPAppDelegate *)get {
|
|
|
|
|
|
|
|
return (OPAppDelegate *)[super get];
|
|
|
|
}
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
+ (NSManagedObjectContext *)managedObjectContext {
|
2012-01-19 16:40:39 +00:00
|
|
|
|
|
|
|
return [(OPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext];
|
|
|
|
}
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
+ (NSManagedObjectModel *)managedObjectModel {
|
|
|
|
|
|
|
|
return [(OPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectModel];
|
|
|
|
}
|
|
|
|
|
2011-11-30 21:42:40 +00:00
|
|
|
- (void)saveContext
|
|
|
|
{
|
|
|
|
NSError *error = nil;
|
|
|
|
if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error]) {
|
|
|
|
/*
|
|
|
|
Replace this implementation with code to handle the error appropriately.
|
|
|
|
|
|
|
|
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
|
|
|
*/
|
|
|
|
err(@"Unresolved error %@, %@", error, [error userInfo]);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
- (void)setKeyPhrase:(NSString *)keyPhrase {
|
|
|
|
|
|
|
|
_keyPhrase = keyPhrase;
|
|
|
|
|
|
|
|
if (keyPhrase) {
|
2012-01-29 11:41:48 +00:00
|
|
|
self.keyPhraseHash = [keyPhrase hashWith:PearlDigestSHA512];
|
|
|
|
self.keyPhraseHashHex = [self.keyPhraseHash encodeHex];
|
|
|
|
|
|
|
|
dbg(@"Updating master key phrase hash to: %@.", self.keyPhraseHashHex);
|
2012-01-19 16:40:39 +00:00
|
|
|
[KeyChain addOrUpdateItemForQuery:[OPAppDelegate keyPhraseHashQuery]
|
2012-01-29 11:41:48 +00:00
|
|
|
withAttributes:[NSDictionary dictionaryWithObject:self.keyPhraseHash
|
2012-01-19 16:40:39 +00:00
|
|
|
forKey:(__bridge id)kSecValueData]];
|
|
|
|
if ([[OPConfig get].storeKeyPhrase boolValue]) {
|
|
|
|
dbg(@"Storing master key phrase in key chain.");
|
|
|
|
[KeyChain addOrUpdateItemForQuery:[OPAppDelegate keyPhraseQuery]
|
|
|
|
withAttributes:[NSDictionary dictionaryWithObject:[keyPhrase dataUsingEncoding:NSUTF8StringEncoding]
|
|
|
|
forKey:(__bridge id)kSecValueData]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-30 21:42:40 +00:00
|
|
|
#pragma mark - Core Data stack
|
|
|
|
|
|
|
|
/**
|
|
|
|
Returns the managed object context for the application.
|
|
|
|
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
|
|
|
|
*/
|
|
|
|
- (NSManagedObjectContext *)managedObjectContext
|
|
|
|
{
|
|
|
|
if (__managedObjectContext != nil)
|
|
|
|
{
|
|
|
|
return __managedObjectContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
|
|
|
|
if (coordinator != nil)
|
|
|
|
{
|
|
|
|
__managedObjectContext = [[NSManagedObjectContext alloc] init];
|
|
|
|
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
|
|
|
|
}
|
|
|
|
return __managedObjectContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
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:@"OnePassword" withExtension:@"momd"];
|
|
|
|
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
|
2012-01-05 00:44:15 +00:00
|
|
|
|
2011-11-30 21:42:40 +00:00
|
|
|
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:@"OnePassword.sqlite"];
|
|
|
|
|
|
|
|
NSError *error = nil;
|
|
|
|
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
|
2012-01-05 00:44:15 +00:00
|
|
|
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL
|
|
|
|
options:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
(id)kCFBooleanTrue, NSMigratePersistentStoresAutomaticallyOption,
|
|
|
|
(id)kCFBooleanTrue, NSInferMappingModelAutomaticallyOption,
|
|
|
|
nil]
|
|
|
|
error:&error])
|
2011-11-30 21:42:40 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Replace this implementation with code to handle the error appropriately.
|
|
|
|
|
|
|
|
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
|
|
|
|
|
|
|
Typical reasons for an error here include:
|
|
|
|
* The persistent store is not accessible;
|
|
|
|
* The schema for the persistent store is incompatible with current managed object model.
|
|
|
|
Check the error message to determine what the actual problem was.
|
|
|
|
|
|
|
|
|
|
|
|
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
|
|
|
|
|
|
|
|
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
|
|
|
|
* Simply deleting the existing store:
|
|
|
|
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
|
|
|
|
|
|
|
|
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
|
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
|
|
|
|
|
|
|
|
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
|
|
|
|
|
|
|
|
*/
|
2012-01-05 00:44:15 +00:00
|
|
|
err(@"Unresolved error %@, %@", error, [error userInfo]);
|
|
|
|
#if DEBUG
|
|
|
|
wrn(@"Deleted datastore: %@", storeURL);
|
|
|
|
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
|
|
|
|
#endif
|
|
|
|
@throw [NSException exceptionWithName:error.domain reason:error.localizedDescription
|
|
|
|
userInfo:[NSDictionary dictionaryWithObject:error forKey:@"cause"]];
|
|
|
|
}
|
2011-11-30 21:42:40 +00:00
|
|
|
|
|
|
|
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
|