2
0

New passwords display in collection view.

This commit is contained in:
Maarten Billemont 2014-03-20 07:15:37 -04:00
parent d036b43d6f
commit 4c8bed2826
17 changed files with 727 additions and 98 deletions

View File

@ -16,6 +16,7 @@
#endif
@property(strong, nonatomic) MPKey *key;
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
+ (instancetype)get;

View File

@ -10,9 +10,7 @@
#import "MPAppDelegate_Store.h"
#import "MPAppDelegate_Key.h"
@implementation MPAppDelegate_Shared {
NSManagedObjectID *_activeUserOID;
}
@implementation MPAppDelegate_Shared
+ (MPAppDelegate_Shared *)get {
@ -32,11 +30,11 @@
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc {
if (!_activeUserOID || !moc)
if (!self.activeUserOID || !moc)
return nil;
NSError *error;
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:_activeUserOID error:&error];
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:self.activeUserOID error:&error];
if (!activeUser) {
[self signOutAnimated:YES];
err(@"Failed to retrieve active user: %@", error);
@ -51,7 +49,7 @@
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
err(@"Failed to obtain a permanent object ID after setting active user: %@", error);
_activeUserOID = activeUser.objectID;
self.activeUserOID = activeUser.objectID;
}
@end

View File

@ -21,6 +21,8 @@ typedef enum {
@interface MPAppDelegate_Shared(Store)<UbiquityStoreManagerDelegate>
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock;
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;

View File

@ -48,6 +48,32 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
return mainManagedObjectContext;
}
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock {
NSManagedObjectContext *mainManagedObjectContext = [self managedObjectContextForMainThreadIfReady];
if (!mainManagedObjectContext)
return NO;
[mainManagedObjectContext performBlock:^{
mocBlock( mainManagedObjectContext );
}];
return YES;
}
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void (^)(NSManagedObjectContext *mainContext))mocBlock {
NSManagedObjectContext *mainManagedObjectContext = [self managedObjectContextForMainThreadIfReady];
if (!mainManagedObjectContext)
return NO;
[mainManagedObjectContext performBlockAndWait:^{
mocBlock( mainManagedObjectContext );
}];
return YES;
}
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock {
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];

View File

@ -35,6 +35,8 @@
- (NSUInteger)use;
- (BOOL)migrateExplicitly:(BOOL)explicit;
- (NSString *)resolveContentUsingKey:(MPKey *)key;
- (void)resolveContentUsingKey:(MPKey *)key result:(void (^)(NSString *))result;
@end

View File

@ -145,6 +145,16 @@
return YES;
}
- (NSString *)resolveContentUsingKey:(MPKey *)key {
return [self.algorithm resolveContentForElement:self usingKey:key];
}
- (void)resolveContentUsingKey:(MPKey *)key result:(void (^)(NSString *))result {
[self.algorithm resolveContentForElement:self usingKey:key result:result];
}
@end
@implementation MPElementGeneratedEntity(MP)

View File

@ -37,7 +37,7 @@ const long MPAvatarAdd = 10000;
+ (NSString *)reuseIdentifier {
return @"MPAvatarCell";
return NSStringFromClass( self );
}
#pragma mark - Life cycle

View File

@ -0,0 +1,36 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPAvatarCell.h
// MPAvatarCell
//
// Created by lhunath on 2014-03-11.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPEntities.h"
@interface MPPasswordCell : UICollectionViewCell <UITextFieldDelegate>
@property(nonatomic, copy) NSString *transientSite;
@property(strong, nonatomic) IBOutlet UITextField *contentField;
+ (NSString *)reuseIdentifier;
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context;
+ (NSString *)reuseIdentifierForElement:(MPElementEntity *)entity;
- (void)setElement:(MPElementEntity *)element;
- (void)updateAnimated:(BOOL)animated;
- (void)populateWithElement:(MPElementEntity *)element;
@end

View File

@ -0,0 +1,186 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPAvatarCell.h
// MPAvatarCell
//
// Created by lhunath on 2014-03-11.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPPasswordCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPPasswordGeneratedCell.h"
#import "MPPasswordStoredCell.h"
@interface MPPasswordCell()
@property(strong, nonatomic) IBOutlet UILabel *nameLabel;
@property(strong, nonatomic) IBOutlet UIButton *loginButton;
@property(nonatomic, strong) NSManagedObjectID *elementOID;
@end
@implementation MPPasswordCell
+ (NSString *)reuseIdentifier {
return NSStringFromClass( self );
}
+ (NSString *)reuseIdentifierForElement:(MPElementEntity *)element {
if ([element isKindOfClass:[MPElementGeneratedEntity class]])
return [MPPasswordGeneratedCell reuseIdentifier];
if ([element isKindOfClass:[MPElementStoredEntity class]])
return [MPPasswordStoredCell reuseIdentifier];
return [self reuseIdentifier];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.contentField && [self.contentField.text length]) {
[self.contentField resignFirstResponder];
return YES;
}
return NO;
}
#pragma mark - Life cycle
- (void)awakeFromNib {
[super awakeFromNib];
self.layer.cornerRadius = 5;
}
- (void)dealloc {
[self removeKeyPathObservers];
}
#pragma mark - Actions
- (IBAction)doUser:(id)sender {
}
#pragma mark - Properties
- (void)setTransientSite:(NSString *)name {
_transientSite = name;
_elementOID = nil;
[self updateAnimated:YES];
}
- (void)setElement:(MPElementEntity *)element {
self.elementOID = [element objectID];
}
- (void)setElementOID:(NSManagedObjectID *)elementOID {
_transientSite = nil;
_elementOID = elementOID;
[self updateAnimated:YES];
}
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context {
NSError *error = nil;
MPElementEntity *element = _elementOID? (MPElementEntity *)[context existingObjectWithID:_elementOID error:&error]: nil;
if (_elementOID && !element)
err(@"Failed to load element: %@", error);
return element;
}
#pragma mark - Private
- (void)updateAnimated:(BOOL)animated {
Weakify(self);
if (self.transientSite) {
self.alpha = 1;
self.nameLabel.text = self.transientSite;
self.contentField.text = nil;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPKey *key = [MPiOSAppDelegate get].key;
if (!key) {
self.alpha = 0;
return;
}
MPElementType type = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType;
if (!type)
type = MPElementTypeGeneratedLong;
NSString *newContent = [MPAlgorithmDefault generateContentNamed:self.transientSite ofType:type withCounter:1 usingKey:key];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.contentField.text = newContent;
}];
}];
}
else if (self.elementOID) {
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
[mainContext performBlock:^{
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
Strongify(self);
NSError *error = nil;
MPKey *key = [MPiOSAppDelegate get].key;
if (!key) {
self.alpha = 0;
return;
}
MPElementEntity *element = (MPElementEntity *)[mainContext existingObjectWithID:_elementOID error:&error];
if (!element) {
err(@"Failed to load element: %@", error);
self.alpha = 0;
return;
}
self.alpha = 1;
[self populateWithElement:element];
[element resolveContentUsingKey:key result:^(NSString *result) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
Strongify(self);
self.contentField.text = result;
}];
}];
}];
}];
}
else {
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
self.alpha = 0;
}];
}
}
- (void)populateWithElement:(MPElementEntity *)element {
self.nameLabel.text = element.name;
self.contentField.text = nil;
}
@end

View File

@ -0,0 +1,26 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordGeneratedCell.h
// MPPasswordGeneratedCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPPasswordCell.h"
@interface MPPasswordGeneratedCell : MPPasswordCell
- (MPElementGeneratedEntity *)elementInContext:(NSManagedObjectContext *)context;
@end

View File

@ -0,0 +1,87 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordGeneratedCell.h
// MPPasswordGeneratedCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPPasswordGeneratedCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@interface MPPasswordGeneratedCell()
@property(strong, nonatomic) IBOutlet UILabel *counterLabel;
@property(strong, nonatomic) IBOutlet UIButton *counterButton;
@property(strong, nonatomic) IBOutlet UIButton *upgradeButton;
@end
@implementation MPPasswordGeneratedCell
- (void)populateWithElement:(MPElementEntity *)element {
[super populateWithElement:element];
MPElementGeneratedEntity *generatedElement = [self generatedElement:element];
self.counterLabel.text = strf(@"%lu", (unsigned long)generatedElement.counter);
if (element.requiresExplicitMigration) {
self.upgradeButton.alpha = 1;
self.counterButton.alpha = 0;
} else {
self.upgradeButton.alpha = 0;
self.counterButton.alpha = 1;
}
}
#pragma mark - Actions
- (IBAction)doUpgrade:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
[[self elementInContext:mainContext] migrateExplicitly:YES];
[mainContext saveToStore];
[self updateAnimated:YES];
}];
}
- (IBAction)doIncrementCounter:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
++[self elementInContext:mainContext].counter;
[mainContext saveToStore];
[self updateAnimated:YES];
}];
}
#pragma mark - Properties
- (MPElementGeneratedEntity *)elementInContext:(NSManagedObjectContext *)context {
return [self generatedElement:[super elementInContext:context]];
}
- (MPElementGeneratedEntity *)generatedElement:(MPElementEntity *)element {
NSAssert([element isKindOfClass:[MPElementGeneratedEntity class]], @"Element is not of generated type: %@", element.name);
if (![element isKindOfClass:[MPElementGeneratedEntity class]])
return nil;
return (MPElementGeneratedEntity *)element;
}
@end

View File

@ -0,0 +1,26 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordStoredCell.h
// MPPasswordStoredCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPPasswordCell.h"
@interface MPPasswordStoredCell : MPPasswordCell
- (MPElementStoredEntity *)elementInContext:(NSManagedObjectContext *)context;
@end

View File

@ -0,0 +1,73 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordGeneratedCell.h
// MPPasswordGeneratedCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPPasswordStoredCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@interface MPPasswordStoredCell()
@property(strong, nonatomic) IBOutlet UIButton *editButton;
@end
@implementation MPPasswordStoredCell
#pragma mark - Actions
- (IBAction)doEditContent:(UIButton *)sender {
self.contentField.enabled = YES;
[self.contentField becomeFirstResponder];
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
[super textFieldDidEndEditing:textField];
if (textField == self.contentField) {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
if (mainContext) {
[[self elementInContext:mainContext] setContentObject:self.contentField.text];
[mainContext saveToStore];
}
[self updateAnimated:YES];
}];
}
}
#pragma mark - Properties
- (MPElementStoredEntity *)elementInContext:(NSManagedObjectContext *)context {
return [self storedElement:[super elementInContext:context]];
}
- (MPElementStoredEntity *)storedElement:(MPElementEntity *)element {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], @"Element is not of generated type: %@", element.name);
if (![element isKindOfClass:[MPElementStoredEntity class]])
return nil;
return (MPElementStoredEntity *)element;
}
@end

View File

@ -18,10 +18,11 @@
#import "LLGitTip.h"
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UISearchDisplayDelegate, UITableViewDataSource, UITableViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UITextFieldDelegate>
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UITextFieldDelegate>
@property(strong, nonatomic) IBOutlet UIView *passwordSelectionContainer;
@property(strong, nonatomic) IBOutlet UICollectionView *passwordCollectionView;
@property (strong, nonatomic) IBOutlet UISearchBar *passwordsSearchBar;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *passwordsToBottomConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *navigationBarToTopConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *navigationBarToPasswordsConstraint;

View File

@ -19,10 +19,14 @@
#import "MPPasswordsViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPPasswordCell.h"
@interface MPPasswordsViewController()
@property (strong, nonatomic) IBOutlet UINavigationBar *navigationBar;
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
@property(strong, nonatomic) IBOutlet UINavigationBar *navigationBar;
@property(nonatomic, readonly) NSString *query;
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@end
@implementation MPPasswordsViewController {
@ -54,28 +58,6 @@
[self stopObservingStore];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchDisplayController.searchResultsTableView)
return 0;
NSAssert(NO, @"Unexpected table view: %@", tableView);
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.searchDisplayController.searchResultsTableView) {
}
NSAssert(NO, @"Unexpected table view: %@", tableView);
return nil;
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
@ -88,43 +70,99 @@
// This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification.
- (void)textFieldDidChange:(UITextField *)textField {
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return [self.fetchedResultsController.sections count] + 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (collectionView == self.passwordCollectionView)
return 0;
if (collectionView == self.passwordCollectionView) {
if (section < [self.fetchedResultsController.sections count])
return ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects;
// New Site.
return [self.query length]? 1: 0;
}
Throw(@"unexpected collection view: %@", collectionView);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.passwordCollectionView)
return nil;
if (collectionView == self.passwordCollectionView) {
MPPasswordCell *cell;
if (indexPath.section < [self.fetchedResultsController.sections count]) {
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (indexPath.item < 2)
cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPPasswordCell reuseIdentifierForElement:element] forIndexPath:indexPath];
else
cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPPasswordCell reuseIdentifierForElement:element] forIndexPath:indexPath];
[cell setElement:element];
} else {
// New Site.
cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPPasswordCell reuseIdentifier] forIndexPath:indexPath];
cell.transientSite = self.query;
}
return cell;
}
Throw(@"unexpected collection view: %@", collectionView);
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
}
#pragma mark - UILongPressGestureRecognizer
- (void)didLongPress:(UILongPressGestureRecognizer *)recognizer {
}
#pragma mark - UIScrollViewDelegate
#pragma mark - UISearchBarDelegate
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
if (searchBar == self.passwordsSearchBar) {
self.passwordsSearchBar.showsCancelButton = YES;
[UIView animateWithDuration:0.3f animations:^{
self.passwordCollectionView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3f];
}];
}
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
if (searchBar == self.passwordsSearchBar) {
self.passwordsSearchBar.showsCancelButton = NO;
[UIView animateWithDuration:0.3f animations:^{
self.passwordCollectionView.backgroundColor = [UIColor clearColor];
}];
}
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchBar == self.passwordsSearchBar)
[self updatePasswords];
}
#pragma mark - Private
@ -164,23 +202,24 @@
- (void)observeStore {
// Weakify(self);
Weakify(self);
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
if (!_mocObserver && mainContext)
_mocObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
if (!_mocObserver && mainContext)
_mocObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Strongify(self);
// [self updateMode]; TODO: reload passwords list
}];
if (!_storeObserver)
_storeObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Strongify(self);
// [self updateMode]; TODO: reload passwords list
}];
}];
if (!_storeObserver)
_storeObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
self.fetchedResultsController = nil;
[self updatePasswords];
}];
}
- (void)stopObservingStore {
@ -191,8 +230,49 @@
[[NSNotificationCenter defaultCenter] removeObserver:_storeObserver];
}
- (void)updatePasswords {
[self.fetchedResultsController.managedObjectContext performBlock:^{
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
if (!activeUserOID)
return;
NSError *error = nil;
self.fetchedResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:
@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, self.query];
if (![self.fetchedResultsController performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.passwordCollectionView reloadData];
}];
}];
}
#pragma mark - Properties
- (NSString *)query {
return [self.passwordsSearchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
- (NSFetchedResultsController *)fetchedResultsController {
if (!_fetchedResultsController)
[MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = @[
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO]
];
fetchRequest.fetchBatchSize = 10;
_fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
}];
return _fetchedResultsController;
}
- (void)setActive:(BOOL)active {
[self setActive:active animated:YES];

View File

@ -13,6 +13,7 @@
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; };
93D393543ACC701C018C74DA /* PearlUIView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393676C32D23A47E27957 /* PearlUIView.m */; };
93D394F6D3F6E2553AA0D684 /* MPPasswordStoredCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3947F6BB69CA9A9124A5D /* MPPasswordStoredCell.m */; };
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; };
93D3959643EACF286D0152BA /* PearlUINavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DDDAC305E8ABB4220C7 /* PearlUINavigationBar.m */; };
@ -27,9 +28,11 @@
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C8AD8EAB747856B3A8C /* LLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3923B42DA2DA18F287092 /* LLModel.m */; };
93D39CB5E2EC1078E898F46A /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937863061C3916AF7AD2 /* MPPasswordCell.m */; };
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A28369954D147E239BA /* MPSetupViewController.m */; };
93D39FA97F4C3F69A75D5A03 /* MPPasswordGeneratedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3993422E207BF0B21D089 /* MPPasswordGeneratedCell.m */; };
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */; };
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */; };
DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; };
@ -510,11 +513,14 @@
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; };
93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; };
93D393676C32D23A47E27957 /* PearlUIView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUIView.m; sourceTree = "<group>"; };
93D3937863061C3916AF7AD2 /* MPPasswordCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordCell.m; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; };
93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = "<group>"; };
93D3947F6BB69CA9A9124A5D /* MPPasswordStoredCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordStoredCell.m; sourceTree = "<group>"; };
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; };
93D395BA6B2CFF5F49A4D25F /* MPPasswordStoredCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordStoredCell.h; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; };
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSetupViewController.h; sourceTree = "<group>"; };
@ -522,6 +528,7 @@
93D3983278751A530262F64E /* LLConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLConfig.h; sourceTree = "<group>"; };
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
93D3993422E207BF0B21D089 /* MPPasswordGeneratedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordGeneratedCell.m; sourceTree = "<group>"; };
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; };
93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = "<group>"; };
93D39A3CC4D8330831FC8CB4 /* LLToggleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLToggleViewController.h; sourceTree = "<group>"; };
@ -530,9 +537,11 @@
93D39BA6C5CB452973918B7D /* LLButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLButtonView.h; sourceTree = "<group>"; };
93D39BF6BCBDFFE844E7D34C /* LLButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLButtonView.m; sourceTree = "<group>"; };
93D39C8E26B06F01566785B7 /* LLToggleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLToggleViewController.m; sourceTree = "<group>"; };
93D39CE1138FDA4D3D1B847A /* MPPasswordGeneratedCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordGeneratedCell.h; sourceTree = "<group>"; };
93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = "<group>"; };
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = "<group>"; };
93D39DDDAC305E8ABB4220C7 /* PearlUINavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUINavigationBar.m; sourceTree = "<group>"; };
93D39E02F69CACAB61C056F8 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
@ -1682,6 +1691,10 @@
93D3971FE104BB4052484151 /* MPUsersViewController.h */,
93D393676C32D23A47E27957 /* PearlUIView.m */,
93D39083C93D90C4B94541AD /* PearlUIView.h */,
93D39E02F69CACAB61C056F8 /* MPPasswordCell.h */,
93D3937863061C3916AF7AD2 /* MPPasswordCell.m */,
93D395BA6B2CFF5F49A4D25F /* MPPasswordStoredCell.h */,
93D3947F6BB69CA9A9124A5D /* MPPasswordStoredCell.m */,
);
sourceTree = "<group>";
};
@ -2539,6 +2552,8 @@
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */,
93D39DDDAC305E8ABB4220C7 /* PearlUINavigationBar.m */,
93D390EEC85E94D9C888643F /* PearlUINavigationBar.h */,
93D3993422E207BF0B21D089 /* MPPasswordGeneratedCell.m */,
93D39CE1138FDA4D3D1B847A /* MPPasswordGeneratedCell.h */,
);
path = iOS;
sourceTree = "<group>";
@ -3843,6 +3858,9 @@
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
93D3959643EACF286D0152BA /* PearlUINavigationBar.m in Sources */,
93D393543ACC701C018C74DA /* PearlUIView.m in Sources */,
93D39CB5E2EC1078E898F46A /* MPPasswordCell.m in Sources */,
93D39FA97F4C3F69A75D5A03 /* MPPasswordGeneratedCell.m in Sources */,
93D394F6D3F6E2553AA0D684 /* MPPasswordStoredCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -19,7 +19,7 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rWM-08-aab" userLabel="User Selection">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rWM-08-aab" userLabel="Users Root">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
@ -391,15 +391,10 @@
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<searchBar contentMode="redraw" translatesAutoresizingMaskIntoConstraints="NO" id="oAG-Ea-TOI">
<searchBar contentMode="redraw" barStyle="black" searchBarStyle="minimal" placeholder="eg. apple.com" showsBookmarkButton="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oAG-Ea-TOI">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="barTintColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="calibratedRGB"/>
<textInputTraits key="textInputTraits"/>
<scopeButtonTitles>
<string>Title</string>
<string>Title</string>
</scopeButtonTitles>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL"/>
<connections>
<outlet property="delegate" destination="nkY-z6-8jd" id="Mfn-aT-vK7"/>
</connections>
@ -409,28 +404,31 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Mv1-29-TWx">
<size key="itemSize" width="320" height="100"/>
<size key="itemSize" width="304" height="100"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="302-fI-maQ">
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPPasswordGeneratedCell" id="302-fI-maQ" customClass="MPPasswordGeneratedCell">
<rect key="frame" x="8" y="0.0" width="304" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
<rect key="frame" x="0.0" y="0.0" width="304" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="SadwGafy7^Sidu" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="q75-Uz-86O">
<rect key="frame" x="8" y="20" width="304" height="37"/>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="SadwGafy7^Sidu" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="q75-Uz-86O">
<rect key="frame" x="8" y="20" width="288" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="302-fI-maQ" id="vji-9t-frp"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="qek-2l-YQf" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="280" height="14"/>
<rect key="frame" x="20" y="71" width="264" height="14"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="">
<accessibilityTraits key="traits" none="YES" staticText="YES" summaryElement="YES"/>
@ -442,7 +440,7 @@
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="I2J-B6-5rE" userLabel="Counter">
<rect key="frame" x="236" y="69" width="11" height="19"/>
<rect key="frame" x="220" y="69" width="11" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="Site's counter."/>
<fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="17"/>
@ -452,7 +450,7 @@
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iLD-rv-uZZ" userLabel="Upgrade">
<rect key="frame" x="241" y="56" width="44" height="44"/>
<rect key="frame" x="225" y="56" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
<constraints>
@ -468,9 +466,12 @@
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doUpgrade:" destination="302-fI-maQ" eventType="touchUpInside" id="iaP-5Y-5re"/>
</connections>
</button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fQc-Fn-JDq" userLabel="Incrementer">
<rect key="frame" x="241" y="56" width="44" height="44"/>
<rect key="frame" x="225" y="56" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="Increments the site counter."/>
<gestureRecognizers/>
@ -487,9 +488,12 @@
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doIncrementCounter:" destination="302-fI-maQ" eventType="touchUpInside" id="Aa1-tk-whD"/>
</connections>
</button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cOv-2G-EAP" userLabel="User">
<rect key="frame" x="276" y="56" width="44" height="44"/>
<rect key="frame" x="260" y="56" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
<constraints>
@ -505,6 +509,9 @@
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doUser:" destination="302-fI-maQ" eventType="touchUpInside" id="Fp9-L4-hlU"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
@ -526,23 +533,34 @@
<constraint firstItem="fQc-Fn-JDq" firstAttribute="centerY" secondItem="I2J-B6-5rE" secondAttribute="centerY" id="uR7-lg-A9q"/>
<constraint firstItem="qek-2l-YQf" firstAttribute="leading" secondItem="302-fI-maQ" secondAttribute="leading" constant="20" id="wUJ-7N-Z1z"/>
</constraints>
<connections>
<outlet property="contentField" destination="q75-Uz-86O" id="so3-qq-0Lv"/>
<outlet property="counterButton" destination="fQc-Fn-JDq" id="1QL-JK-tHn"/>
<outlet property="counterLabel" destination="I2J-B6-5rE" id="C4b-gE-XHW"/>
<outlet property="loginButton" destination="cOv-2G-EAP" id="WoR-eP-Ztq"/>
<outlet property="nameLabel" destination="qek-2l-YQf" id="CcC-PM-kMx"/>
<outlet property="upgradeButton" destination="iLD-rv-uZZ" id="OKi-9X-R6F"/>
</connections>
</collectionViewCell>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="GAB-fT-EFv">
<rect key="frame" x="0.0" y="110" width="320" height="100"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPPasswordStoredCell" id="GAB-fT-EFv" customClass="MPPasswordStoredCell">
<rect key="frame" x="8" y="110" width="304" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
<rect key="frame" x="0.0" y="0.0" width="304" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="45O-9d-keG">
<rect key="frame" x="8" y="20" width="304" height="37"/>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="45O-9d-keG">
<rect key="frame" x="8" y="20" width="288" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="GAB-fT-EFv" id="tBW-n5-SOj"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="VNc-fL-Vfa" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="280" height="14"/>
<rect key="frame" x="20" y="71" width="264" height="14"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="">
<accessibilityTraits key="traits" none="YES" staticText="YES" summaryElement="YES"/>
@ -554,7 +572,7 @@
<size key="shadowOffset" width="0.0" height="1"/>
</label>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UAg-Fn-Iph" userLabel="Edit">
<rect key="frame" x="241" y="56" width="44" height="44"/>
<rect key="frame" x="225" y="56" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="Edits the password."/>
<constraints>
@ -570,9 +588,12 @@
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doEditContent:" destination="GAB-fT-EFv" eventType="touchUpInside" id="XnF-cN-Z2j"/>
</connections>
</button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="T1f-JH-Ppw" userLabel="User">
<rect key="frame" x="276" y="56" width="44" height="44"/>
<rect key="frame" x="260" y="56" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
<constraints>
@ -588,6 +609,9 @@
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doUser:" destination="GAB-fT-EFv" eventType="touchUpInside" id="xCm-vG-Hvp"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
@ -605,6 +629,12 @@
<constraint firstAttribute="trailing" secondItem="VNc-fL-Vfa" secondAttribute="trailing" constant="20" id="k3u-1Q-cKf"/>
<constraint firstItem="45O-9d-keG" firstAttribute="top" secondItem="GAB-fT-EFv" secondAttribute="top" constant="20" id="qIK-xa-HCc"/>
</constraints>
<connections>
<outlet property="contentField" destination="45O-9d-keG" id="b56-gA-eQD"/>
<outlet property="editButton" destination="UAg-Fn-Iph" id="VLe-Hk-a9T"/>
<outlet property="loginButton" destination="T1f-JH-Ppw" id="KJN-iM-QFu"/>
<outlet property="nameLabel" destination="VNc-fL-Vfa" id="kMh-xS-U1W"/>
</connections>
</collectionViewCell>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="0BZ-lH-jYe">
<rect key="frame" x="0.0" y="220" width="155" height="100"/>
@ -613,12 +643,15 @@
<rect key="frame" x="0.0" y="0.0" width="155" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="SadwGafy7^Sidu" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="GQM-CJ-dQd">
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="SadwGafy7^Sidu" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="GQM-CJ-dQd">
<rect key="frame" x="8" y="20" width="139" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="0BZ-lH-jYe" id="oQO-GK-fja"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="QSz-Qk-OvW" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="115" height="14"/>
@ -686,19 +719,22 @@
</constraints>
<size key="customSize" width="155" height="100"/>
</collectionViewCell>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="vMF-fk-FYX">
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPPasswordCell" id="vMF-fk-FYX" customClass="MPPasswordCell">
<rect key="frame" x="165" y="220" width="155" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="155" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="fmG-ML-oLT">
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="fmG-ML-oLT">
<rect key="frame" x="8" y="20" width="139" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="vMF-fk-FYX" id="m0K-yd-GFJ"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="IgW-NX-sao" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="115" height="14"/>
@ -729,6 +765,9 @@
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doUser:" destination="vMF-fk-FYX" eventType="touchUpInside" id="bFe-ar-oI7"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
@ -745,6 +784,11 @@
<constraint firstAttribute="trailing" secondItem="GLO-mQ-h0v" secondAttribute="trailing" id="yXU-bt-rwt"/>
</constraints>
<size key="customSize" width="155" height="100"/>
<connections>
<outlet property="contentField" destination="fmG-ML-oLT" id="G9g-Nm-5O4"/>
<outlet property="loginButton" destination="GLO-mQ-h0v" id="s2n-qw-Pu6"/>
<outlet property="nameLabel" destination="IgW-NX-sao" id="zUR-nh-FUZ"/>
</connections>
</collectionViewCell>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="yRD-Rk-pNt">
<rect key="frame" x="0.0" y="330" width="155" height="100"/>
@ -753,12 +797,15 @@
<rect key="frame" x="0.0" y="0.0" width="155" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="2iC-wr-tcf">
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="2iC-wr-tcf">
<rect key="frame" x="8" y="20" width="139" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="yRD-Rk-pNt" id="Gcn-G5-0Je"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="9DA-ac-KNn" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="115" height="14"/>
@ -813,12 +860,15 @@
<rect key="frame" x="0.0" y="0.0" width="155" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="vmd-6A-sMl">
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="vmd-6A-sMl">
<rect key="frame" x="8" y="20" width="139" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="0w1-b0-Hlj" id="3ia-Ph-fjc"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="fmF-pH-pLd" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="115" height="14"/>
@ -873,12 +923,15 @@
<rect key="frame" x="0.0" y="0.0" width="155" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="Ksb-AB-je4">
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="Ksb-AB-je4">
<rect key="frame" x="8" y="20" width="139" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="3uT-QC-zue" id="XFD-25-rYU"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="stt-hA-Evt" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="115" height="14"/>
@ -933,12 +986,15 @@
<rect key="frame" x="0.0" y="0.0" width="155" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="8X9-yV-BkP">
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Pa$sW0rD" textAlignment="center" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="8X9-yV-BkP">
<rect key="frame" x="8" y="20" width="139" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="Hfe-CZ-2BC" id="hP6-WH-1Gy"/>
</connections>
</textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="RRk-AC-RlV" userLabel="Site Name">
<rect key="frame" x="20" y="71" width="115" height="14"/>
@ -987,6 +1043,16 @@
<size key="customSize" width="155" height="100"/>
</collectionViewCell>
</cells>
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="ef2-bN-Zyo">
<autoresizingMask key="autoresizingMask"/>
</collectionReusableView>
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="JAE-Ae-oH9">
<autoresizingMask key="autoresizingMask"/>
</collectionReusableView>
<connections>
<outlet property="dataSource" destination="nkY-z6-8jd" id="5DW-tc-rAp"/>
<outlet property="delegate" destination="nkY-z6-8jd" id="xhr-Qn-SBe"/>
</connections>
</collectionView>
</subviews>
<color key="backgroundColor" cocoaTouchSystemColor="viewFlipsideBackgroundColor"/>
@ -1036,20 +1102,11 @@
<outlet property="navigationBarToTopConstraint" destination="AkN-iE-w6G" id="kw0-4E-iQG"/>
<outlet property="passwordCollectionView" destination="aXw-tn-8Sj" id="fNR-qS-qNP"/>
<outlet property="passwordSelectionContainer" destination="tI8-OT-LrO" id="J91-sd-kq3"/>
<outlet property="passwordsSearchBar" destination="oAG-Ea-TOI" id="sqL-H5-LCV"/>
<outlet property="passwordsToBottomConstraint" destination="dNt-uf-8BC" id="Ta6-eL-z7w"/>
<outlet property="searchDisplayController" destination="y1f-AK-Hd5" id="chz-MM-BSf"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="APh-u5-vFI" userLabel="First Responder" sceneMemberID="firstResponder"/>
<searchDisplayController id="y1f-AK-Hd5">
<connections>
<outlet property="delegate" destination="nkY-z6-8jd" id="Cqs-L6-YSJ"/>
<outlet property="searchBar" destination="oAG-Ea-TOI" id="FFP-s6-tPm"/>
<outlet property="searchContentsController" destination="nkY-z6-8jd" id="SXm-1E-vs9"/>
<outlet property="searchResultsDataSource" destination="nkY-z6-8jd" id="el3-A9-EfV"/>
<outlet property="searchResultsDelegate" destination="nkY-z6-8jd" id="cuI-bo-AX8"/>
</connections>
</searchDisplayController>
</objects>
<point key="canvasLocation" x="1350" y="1200"/>
</scene>