From ea5be8efcb129b86b2ba57510b7d5de548b77388 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Thu, 27 Apr 2017 02:20:49 -0400 Subject: [PATCH] Rewrite handling of collection view and table view reloading for reliability. --- platform-darwin/External/Pearl | 2 +- .../project.pbxproj | 16 +- .../Source/Mac/MPPasswordWindowController.m | 4 +- .../Source/iOS/MPPasswordsViewController.h | 2 +- .../Source/iOS/MPPasswordsViewController.m | 243 ++++++------------ .../Source/iOS/MPStoreViewController.m | 4 +- platform-darwin/Source/iOS/MPiOSAppDelegate.m | 39 +++ .../Source/iOS/Storyboard.storyboard | 12 +- 8 files changed, 144 insertions(+), 178 deletions(-) diff --git a/platform-darwin/External/Pearl b/platform-darwin/External/Pearl index 5e5eaa13..8d390f17 160000 --- a/platform-darwin/External/Pearl +++ b/platform-darwin/External/Pearl @@ -1 +1 @@ -Subproject commit 5e5eaa1344a6d2c7c058e7ca458387b43489bb59 +Subproject commit 8d390f176b2f2997b75a18cfc6446b798667c54a diff --git a/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj b/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj index 134b59ed..6f179af3 100644 --- a/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/platform-darwin/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -47,7 +47,7 @@ 93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; }; 93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; }; 93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; }; - 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; }; + 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */; }; 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; }; 93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; }; 93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */; }; @@ -58,7 +58,7 @@ 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; }; 93D39BFB5F5F9337F6565DE3 /* UIView+Visible.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B7B765546B1F1900CB7 /* UIView+Visible.h */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; - 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */; }; + 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */; }; 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; }; 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; @@ -466,7 +466,7 @@ /* Begin PBXFileReference section */ 93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = ""; }; 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; }; - 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = ""; }; + 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadItems.m"; sourceTree = ""; }; 93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = ""; }; 93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v0.c"; sourceTree = ""; }; 93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = ""; }; @@ -477,7 +477,7 @@ 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = ""; }; 93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = ""; }; 93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = ""; }; - 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadFromArray.h"; sourceTree = ""; }; + 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadItems.h"; sourceTree = ""; }; 93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = ""; }; 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = ""; }; 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = ""; }; @@ -3114,8 +3114,8 @@ DAFE462515039823003ABA7C /* Resources */, DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */, DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */, - 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */, - 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */, + 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */, + 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */, DAFE4A63150399FF003ABA83 /* UIControl+PearlBlocks.h */, DAFE4A63150399FF003ABA81 /* UIControl+PearlBlocks.m */, DAFE4A63150399FF003ABA8B /* UIControl+PearlSelect.h */, @@ -3249,7 +3249,7 @@ 93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */, 93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */, 93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */, - 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */, + 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadItems.h in Headers */, 93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */, 93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */, 93D3959696396A91961C6148 /* UIView+AlphaScale.h in Headers */, @@ -3959,7 +3959,7 @@ DAA141201922FF020032B392 /* PearlTween.m in Sources */, 93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */, 93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */, - 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */, + 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadItems.m in Sources */, 93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */, 93D392A33CCE85431E910C7B /* NSOrderedSetOrArray.m in Sources */, 93D393AA69A1193401160418 /* UIView+AlphaScale.m in Sources */, diff --git a/platform-darwin/Source/Mac/MPPasswordWindowController.m b/platform-darwin/Source/Mac/MPPasswordWindowController.m index 28dedd6e..4f0af45b 100644 --- a/platform-darwin/Source/Mac/MPPasswordWindowController.m +++ b/platform-darwin/Source/Mac/MPPasswordWindowController.m @@ -547,8 +547,8 @@ NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ]; - fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@", - queryPattern, queryPattern, [MPMacAppDelegate get].activeUserOID]; + fetchRequest.predicate = + [NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPMacAppDelegate get].activeUserOID]; prof_rewind( @"fetchRequest" ); NSError *error = nil; diff --git a/platform-darwin/Source/iOS/MPPasswordsViewController.h b/platform-darwin/Source/iOS/MPPasswordsViewController.h index e16e9996..9aa3c4a4 100644 --- a/platform-darwin/Source/iOS/MPPasswordsViewController.h +++ b/platform-darwin/Source/iOS/MPPasswordsViewController.h @@ -34,7 +34,7 @@ @property(assign, nonatomic) BOOL active; - (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void ( ^ )(BOOL finished))completion; -- (void)updatePasswords; +- (void)reloadPasswords; - (IBAction)dismissPopdown:(id)sender; diff --git a/platform-darwin/Source/iOS/MPPasswordsViewController.m b/platform-darwin/Source/iOS/MPPasswordsViewController.m index 86aeeb53..35fde63e 100644 --- a/platform-darwin/Source/iOS/MPPasswordsViewController.m +++ b/platform-darwin/Source/iOS/MPPasswordsViewController.m @@ -25,6 +25,8 @@ #import "MPAnswersViewController.h" #import "MPMessageViewController.h" +static const NSString *MPTransientPasswordItem = @"MPTransientPasswordItem"; + typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { MPPasswordsBadNameTip = 1 << 0, }; @@ -32,7 +34,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { @interface MPPasswordsViewController() @property(nonatomic, strong) IBOutlet UINavigationBar *navigationBar; -@property(nonatomic, readonly) NSString *query; @end @@ -42,11 +43,9 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { UIColor *_backgroundColor; UIColor *_darkenedBackgroundColor; __weak UIViewController *_popdownVC; - BOOL _showTransientItem; - NSUInteger _transientItem; NSCharacterSet *_siteNameAcceptableCharactersSet; NSArray *_fuzzyGroups; - NSMutableArray *_passwordCollectionViewUpdatesBatch; + NSMutableArray *_passwordCollectionSections; } #pragma mark - Life @@ -62,8 +61,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { _backgroundColor = self.passwordCollectionView.backgroundColor; _darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f]; - _transientItem = NSNotFound; - _passwordCollectionViewUpdatesBatch = [NSMutableArray arrayWithCapacity:4]; + _passwordCollectionSections = [NSMutableArray new]; self.view.backgroundColor = [UIColor clearColor]; [self.passwordCollectionView automaticallyAdjustInsetsForKeyboard]; @@ -83,7 +81,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { [self registerObservers]; [self updateConfigKey:nil]; - [self updatePasswords]; + [self reloadPasswords]; } - (void)viewDidAppear:(BOOL)animated { @@ -140,30 +138,42 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { return CGSizeMake( itemWidth, 100 ); } +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout + insetForSectionAtIndex:(NSInteger)section { + + UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout; + UIEdgeInsets occludedInsets = [self.passwordCollectionView occludedInsets]; + UIEdgeInsets insets = layout.sectionInset; + + if (section == 0) + insets.top += occludedInsets.top; + + if (section == collectionView.numberOfSections - 1) + insets.bottom += occludedInsets.bottom; + + return insets; +} + #pragma mark - UICollectionViewDataSource - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - return [self.fetchedResultsController.sections count]; + return [_passwordCollectionSections count]; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - if (![MPiOSAppDelegate get].activeUserOID || !_fetchedResultsController) - return 0; - - NSUInteger objects = ((id)self.fetchedResultsController.sections[section]).numberOfObjects; - _transientItem = _showTransientItem? objects: NSNotFound; - return objects + (_showTransientItem? 1: 0); + return [_passwordCollectionSections[(NSUInteger)section] count]; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath]; [cell setFuzzyGroups:_fuzzyGroups]; - if (indexPath.item < ((id)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) - [cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO]; - else + id item = _passwordCollectionSections[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item]; + if ([item isKindOfClass:[MPSiteEntity class]]) + [cell setSite:item animated:NO]; + else // item == MPTransientPasswordItem [cell setTransientSite:self.query animated:NO]; return cell; @@ -180,61 +190,14 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { #pragma mark - NSFetchedResultsControllerDelegate -- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath - forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { - - Weakify( self ); - - if (controller == _fetchedResultsController) { - @synchronized (_passwordCollectionViewUpdatesBatch) { - [_passwordCollectionViewUpdatesBatch addObject:[^{ - Strongify( self ); - - switch (type) { - case NSFetchedResultsChangeInsert: - [self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]]; - break; - case NSFetchedResultsChangeDelete: - [self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]]; - break; - case NSFetchedResultsChangeMove: - if (![indexPath isEqual:newIndexPath]) - [self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; - break; - case NSFetchedResultsChangeUpdate: - [self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]]; - break; - } - } copy]]; - } - - [controller.managedObjectContext performBlock:^{ - PearlMainQueueOperation( ^{ - @try { - [self.passwordCollectionView performBatchUpdates:^{ - [self updateTransientItem]; - - @synchronized (_passwordCollectionViewUpdatesBatch) { - for (VoidBlock block in _passwordCollectionViewUpdatesBatch) - block(); - [_passwordCollectionViewUpdatesBatch removeAllObjects]; - } - } completion:nil]; - } - @catch (NSException *exception) { - wrn( @"While updating password cells: %@", [exception fullDescription] ); - [self.passwordCollectionView reloadData]; - } - } ); - }]; - } -} - -- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id)sectionInfo - atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { +- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { if (controller == _fetchedResultsController) - [self.passwordCollectionView reloadData]; + PearlMainQueue( ^{ + [self.passwordCollectionView updateDataSource:_passwordCollectionSections + toSections:[self createPasswordCollectionSections] + reloadItems:nil completion:nil]; + } ); } #pragma mark - UISearchBarDelegate @@ -266,7 +229,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { if (_passwordsDismissRecognizer) [self.view removeGestureRecognizer:_passwordsDismissRecognizer]; - [self updatePasswords]; + [self reloadPasswords]; [UIView animateWithDuration:0.3f animations:^{ self.passwordCollectionView.backgroundColor = _backgroundColor; }]; @@ -290,7 +253,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { if ([[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length]) [self showTips:MPPasswordsBadNameTip]; - [self updatePasswords]; + [self reloadPasswords]; } } @@ -311,24 +274,29 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { }]; } -- (void)updateTransientItem { +- (NSMutableArray *)createPasswordCollectionSections { NSString *query = self.query; - _showTransientItem = [query length] > 0 && ![[[self.fetchedResultsController.sections[0] objects] filteredArrayUsingPredicate: - [NSPredicate predicateWithBlock:^BOOL(MPSiteEntity *site, NSDictionary *bindings) { - return [site.name isEqualToString:query]; - }]] count]; - if (!_showTransientItem && _transientItem != NSNotFound) - [self.passwordCollectionView deleteItemsAtIndexPaths: - @[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]]; - else if (_showTransientItem && _transientItem == NSNotFound) { - NSUInteger objects = [self.fetchedResultsController.sections[0] numberOfObjects]; - [self.passwordCollectionView insertItemsAtIndexPaths: - @[ [NSIndexPath indexPathForItem:objects inSection:0] ]]; + BOOL needTransientItem = [query length] > 0; + + NSArray> *sectionInfos = [self.fetchedResultsController sections]; + NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:[sectionInfos count]]; + for (id sectionInfo in sectionInfos) { + NSArray *sites = [sectionInfo.objects copy]; + [sections addObject:sites]; + + if (needTransientItem) + for (MPSiteEntity *site in sites) + if ([site.name isEqualToString:query]) { + needTransientItem = NO; + break; + } } - else if (_transientItem != NSNotFound) - [self.passwordCollectionView reloadItemsAtIndexPaths: - @[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]]; + + if (needTransientItem) + [sections addObject:@[ MPTransientPasswordItem ]]; + + return sections; } - (void)registerObservers { @@ -340,7 +308,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { } ); PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue], ^(MPPasswordsViewController *self, NSNotification *note) { - [self updatePasswords]; + [self reloadPasswords]; } ); PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue], ^(MPPasswordsViewController *self, NSNotification *note) { @@ -372,7 +340,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, nil, nil, ^(MPPasswordsViewController *self, NSNotification *note) { PearlMainQueue( ^{ - [self updatePasswords]; + [self reloadPasswords]; [self registerObservers]; } ); } ); @@ -395,81 +363,40 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { [self.passwordCollectionView reloadData]; } -- (void)updatePasswords { +- (void)reloadPasswords { - NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID; - if (!activeUserOID) { - PearlMainQueue( ^{ - self.passwordsSearchBar.text = nil; - [self.passwordCollectionView reloadData]; - [self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) animated:YES]; - } ); - return; - } - - static NSRegularExpression *fuzzyRE; - static dispatch_once_t once = 0; - dispatch_once( &once, ^{ - fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil]; - } ); - - NSString *queryString = self.query; - NSString *queryPattern; - if ([queryString length] < 13) - queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"]; - else - // If query is too long, a wildcard per character makes the CoreData fetch take excessively long. - queryPattern = strf( @"*%@*", queryString ); - NSMutableArray *fuzzyGroups = [NSMutableArray arrayWithCapacity:[queryString length]]; - [fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length ) - usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { - [fuzzyGroups addObject:[queryString substringWithRange:result.range]]; - }]; - _fuzzyGroups = fuzzyGroups; [self.fetchedResultsController.managedObjectContext performBlock:^{ - NSArray *oldSectionInfos = [self.fetchedResultsController sections]; - NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]]; - for (id sectionInfo in oldSectionInfos) - [oldSections addObject:[sectionInfo.objects copy]]; + static NSRegularExpression *fuzzyRE; + static dispatch_once_t once = 0; + dispatch_once( &once, ^{ + fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil]; + } ); + + prof_new( @"updateSites" ); + NSString *queryString = self.query; + NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"] + stringByAppendingString:@"*"]; + prof_rewind( @"queryPattern" ); + NSMutableArray *fuzzyGroups = [NSMutableArray new]; + [fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length ) + usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + [fuzzyGroups addObject:[queryString substringWithRange:result.range]]; + }]; + _fuzzyGroups = fuzzyGroups; NSError *error = nil; self.fetchedResultsController.fetchRequest.predicate = - [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@", - queryPattern, queryPattern, activeUserOID]; + [NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID]; if (![self.fetchedResultsController performFetch:&error]) err( @"Couldn't fetch sites: %@", [error fullDescription] ); PearlMainQueue( ^{ - @try { - [self.passwordCollectionView performBatchUpdates:^{ - [self updateTransientItem]; - - NSInteger fromSections = self.passwordCollectionView.numberOfSections; - NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView]; - for (NSInteger section = 0; section < MAX( toSections, fromSections ); ++section) { - if (section >= fromSections) - [self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]]; - else if (section >= toSections) - [self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]]; - else if (section < [oldSections count]) - [self.passwordCollectionView reloadItemsFromArray:oldSections[section] - toArray:[[self.fetchedResultsController sections][section] objects] - inSection:section]; - else - [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]]; - } - } completion:^(BOOL finished) { - if (finished) - [self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) - animated:YES]; - for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells) - [cell setFuzzyGroups:_fuzzyGroups]; - }]; - } - @catch (NSException *exception) { - wrn( @"While updating password cells: %@", [exception fullDescription] ); - [self.passwordCollectionView reloadData]; - } + [self.passwordCollectionView updateDataSource:_passwordCollectionSections + toSections:[self createPasswordCollectionSections] + reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) { + for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells) + [cell setFuzzyGroups:_fuzzyGroups]; + }]; } ); }]; } @@ -484,15 +411,15 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) { - (NSFetchedResultsController *)fetchedResultsController { if (!_fetchedResultsController) { - _showTransientItem = NO; [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO] ]; fetchRequest.fetchBatchSize = 10; - _fetchedResultsController = [[NSFetchedResultsController alloc] - initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil]; + _fetchedResultsController = + [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext + sectionNameKeyPath:nil cacheName:nil]; _fetchedResultsController.delegate = self; }]; [self registerObservers]; diff --git a/platform-darwin/Source/iOS/MPStoreViewController.m b/platform-darwin/Source/iOS/MPStoreViewController.m index 42f88650..17021827 100644 --- a/platform-darwin/Source/iOS/MPStoreViewController.m +++ b/platform-darwin/Source/iOS/MPStoreViewController.m @@ -73,7 +73,7 @@ PearlEnum( MPDevelopmentFuelConsumption, self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 ); - [self reloadCellsHiding:self.allCellsBySection[0] showing:@[ self.loadingCell ]]; + [self updateCellsHiding:self.allCellsBySection[0] showing:@[ self.loadingCell ]]; [self.allCellsBySection[0] enumerateObjectsUsingBlock:^(MPStoreProductCell *cell, NSUInteger idx, BOOL *stop) { if ([cell isKindOfClass:[MPStoreProductCell class]]) { cell.purchasedIndicator.visible = NO; @@ -257,7 +257,7 @@ PearlEnum( MPDevelopmentFuelConsumption, } [hideCells removeObjectsInArray:showCells]; - [self reloadCellsHiding:hideCells showing:showCells]; + [self updateCellsHiding:hideCells showing:showCells]; } - (void)updateFuel { diff --git a/platform-darwin/Source/iOS/MPiOSAppDelegate.m b/platform-darwin/Source/iOS/MPiOSAppDelegate.m index 55f856f9..02375c5f 100644 --- a/platform-darwin/Source/iOS/MPiOSAppDelegate.m +++ b/platform-darwin/Source/iOS/MPiOSAppDelegate.m @@ -25,6 +25,7 @@ @property(nonatomic, strong) UIDocumentInteractionController *interactionController; +@property(nonatomic) UIBackgroundTaskIdentifier task; @end @implementation MPiOSAppDelegate @@ -244,6 +245,44 @@ if (![[MPiOSConfig get].rememberLogin boolValue]) [self signOutAnimated:NO]; +// self.task = [application beginBackgroundTaskWithExpirationHandler:^{ +// [application endBackgroundTask:self.task]; +// dbg( @"background expiring" ); +// }]; +// PearlNotMainQueueOperation( ^{ +// NSString *pbstring = [UIPasteboard generalPasteboard].string; +// while (YES) { +// NSString *newString = [UIPasteboard generalPasteboard].string; +// if (![newString isEqualToString:pbstring]) { +// dbg( @"pasteboard changed to: %@", newString ); +// pbstring = newString; +// NSURL *url = [NSURL URLWithString:pbstring]; +// if (url) { +// NSString *siteName = [url host]; +// } +// MPKey *key = [MPiOSAppDelegate get].key; +// if (key) +// [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { +// NSFetchRequest +// *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )]; +// fetchRequest.sortDescriptors = @[ +// [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO] +// ]; +// fetchRequest.fetchBatchSize = 2; +// fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name LIKE[cd] %@) AND user == %@", siteName, +// [[MPiOSAppDelegate get] activeUserOID]]; +// NSError *error = nil; +// NSArray *results = [fetchRequest execute:&error]; +// dbg( @"site search, error: %@, results:\n%@", error, results ); +// if ([results count]) { +// [UIPasteboard generalPasteboard].string = [[results firstObject] resolvePasswordUsingKey:key]; +// } +// }]; +// } +// [NSThread sleepForTimeInterval:5]; +// } +// } ); + [super applicationDidEnterBackground:application]; } diff --git a/platform-darwin/Source/iOS/Storyboard.storyboard b/platform-darwin/Source/iOS/Storyboard.storyboard index c2102e62..f19db4c9 100644 --- a/platform-darwin/Source/iOS/Storyboard.storyboard +++ b/platform-darwin/Source/iOS/Storyboard.storyboard @@ -526,7 +526,7 @@ - + @@ -1155,7 +1155,7 @@ - + @@ -1163,11 +1163,11 @@ - + - + @@ -2022,7 +2022,7 @@ eg. apple.com, rmitchell@twitter.com - + @@ -2273,7 +2273,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien. - +