2
0

TestFlight update + search improvements.

[UPDATED]   TestFlight SDK updated to 0.8.3
[ADDED]     Log >=info messages to TestFlight
[ADDED]     Hide status bar while locked.
[ADDED]     Show all sites when search results icon is tapped.
[IMPROVED]  Search table cells.
This commit is contained in:
Maarten Billemont 2012-02-26 23:01:17 +01:00
parent 039ec9b082
commit f3196841f3
433 changed files with 1926 additions and 1505 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit 7263659901f7c5e8135f553e835fdabc7784ef2f Subproject commit f1b86c1c9768e7f4f5ec4dd4196410c9d94d214d

View File

@ -1,4 +1,4 @@
Thanks for downloading the TestFlight SDK 0.8.2! Thanks for downloading the TestFlight SDK 0.8.3!
This document is also available on the web at https://www.testflightapp.com/sdk/doc This document is also available on the web at https://www.testflightapp.com/sdk/doc
@ -12,6 +12,7 @@ This document is also available on the web at https://www.testflightapp.com/sdk/
8. View your results 8. View your results
9. Advanced Exception Handling 9. Advanced Exception Handling
10. Remote Logging 10. Remote Logging
11. iOS 3
START START
@ -45,7 +46,7 @@ This SDK can be run from both the iPhone Simulator and Device and has been teste
3. Set Folders to "Create groups for any added folders" 3. Set Folders to "Create groups for any added folders"
4. Select all targets that you want to add the SDK to 4. Select all targets that you want to add the SDK to
2. Verify that libTestFlight.a has been added to the Link Binary With Libraries Build Phase for the targets you want to use the SDK with 2. Verify that libTestFlight.a and has been added to the Link Binary With Libraries Build Phase for the targets you want to use the SDK with
1. Select your Project in the Project Navigator 1. Select your Project in the Project Navigator
2. Select the target you want to enable the SDK for 2. Select the target you want to enable the SDK for
@ -54,15 +55,26 @@ This SDK can be run from both the iPhone Simulator and Device and has been teste
5. If libTestFlight.a is not listed, drag and drop the library from your Project Navigator to the Link Binary With Libraries area 5. If libTestFlight.a is not listed, drag and drop the library from your Project Navigator to the Link Binary With Libraries area
6. Repeat Steps 2 - 5 until all targets you want to use the SDK with have the SDK linked 6. Repeat Steps 2 - 5 until all targets you want to use the SDK with have the SDK linked
3. In your Application Delegate: 3. Add libz to your Link Binary With Libraries Build Phase
1. Select your Project in the Project Navigator
2. Select the target you want to enable the SDK for
3. Select the Build Phases tab
4. Open the Link Binary With Libraries Phase
5. Click the + to add a new library
6. Find libz.dylib in the list and add it
7. Repeat Steps 2 - 6 until all targets you want to use the SDK with have libz.dylib
4. In your Application Delegate:
1. Import TestFlight: `#import "TestFlight.h"` 1. Import TestFlight: `#import "TestFlight.h"`
NOTE: If you do not want to import TestFlight.h in every file you may add the above line into you pre-compiled header (`<projectname>_Prefix.pch`) file inside of the NOTE: If you do not want to import TestFlight.h in every file you may add the above line into you pre-compiled header (`<projectname>_Prefix.pch`) file inside of the
#ifdef __OBJC__ section. #ifdef __OBJC__
section.
This will give you access to the SDK across all files. This will give you access to the SDK across all files.
2. Get your Team Token which you can find at [http://testflightapp.com/dashboard/team/](http://testflightapp.com/dashboard/team/) select the team you are using from the team selection drop down list on the top of the page and then select edit. 2. Get your Team Token which you can find at [http://testflightapp.com/dashboard/team/](http://testflightapp.com/dashboard/team/) select the team you are using from the team selection drop down list on the top of the page and then select Team Info.
3. Launch TestFlight with your Team Token 3. Launch TestFlight with your Team Token
@ -78,9 +90,9 @@ This SDK can be run from both the iPhone Simulator and Device and has been teste
4. To report crashes to you we install our own uncaught exception handler. If you are not currently using an exception handler of your own then all you need to do is go to the next step. If you currently use an Exception Handler, or you use another framework that does please go to the section on advanced exception handling. 4. To report crashes to you we install our own uncaught exception handler. If you are not currently using an exception handler of your own then all you need to do is go to the next step. If you currently use an Exception Handler, or you use another framework that does please go to the section on advanced exception handling.
4. To enable the best crash reporting possible we recommend setting the following project build settings in Xcode to NO for all targets that you want to have live crash reporting for. You can find build settings by opening the Project Navigator (default command+1 or command+shift+j) then clicking on the project you are configuring (usually the first selection in the list). From there you can choose to either change the global project settings or settings on an individual project basis. All settings below are in the Deployment Section. 5. To enable the best crash reporting possible we recommend setting the following project build settings in Xcode to NO for all targets that you want to have live crash reporting for. You can find build settings by opening the Project Navigator (default command+1 or command+shift+j) then clicking on the project you are configuring (usually the first selection in the list). From there you can choose to either change the global project settings or settings on an individual project basis. All settings below are in the Deployment Section.
1. Deployment Post Processing 1. Deployment Postrocessing
2. Strip Debug Symbols During Copy 2. Strip Debug Symbols During Copy
3. Strip Linked Product 3. Strip Linked Product
@ -99,6 +111,15 @@ To launch unguided feedback call the `openFeedbackView` method. We recommend tha
[TestFlight openFeedbackView]; [TestFlight openFeedbackView];
} }
If you want to create your own feedback form you can use the `submitCustomFeedback` method to submit the feedback that the user has entered.
-(IBAction)submitFeedbackPressed:(id)sender {
NSString *feedback = [self getUserFeedback];
[TestFlight submitCustomFeedback:feedback];
}
The above sample assumes that [self getUserFeedback] is implemented such that it obtains the users feedback from the GUI element you have created and that submitFeedbackPressed is the action for your submit button.
Once users have submitted feedback from inside of the application you can view it in the feedback area of your build page. Once users have submitted feedback from inside of the application you can view it in the feedback area of your build page.
6. Upload your build. 6. Upload your build.
@ -183,11 +204,19 @@ We have implemented three different loggers.
Each of the loggers log asynchronously and all TFLog calls are non blocking. The TestFlight logger writes its data to a file which is then sent to our servers on Session End events. The Apple System Logger sends its messages to the Apple System Log and are viewable using the Organizer in Xcode when the device is attached to your computer. The ASL logger can be disabled by turning it off in your TestFlight options Each of the loggers log asynchronously and all TFLog calls are non blocking. The TestFlight logger writes its data to a file which is then sent to our servers on Session End events. The Apple System Logger sends its messages to the Apple System Log and are viewable using the Organizer in Xcode when the device is attached to your computer. The ASL logger can be disabled by turning it off in your TestFlight options
[TestFlight setOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"logsToConsole"]]; [TestFlight setOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"logToConsole"]];
The default option is YES. The default option is YES.
The STDERR logger sends log messages to STDERR so that you can see your log statements while debugging. The STDERR logger is only active when a debugger is attached to your application. The STDERR logger sends log messages to STDERR so that you can see your log statements while debugging. The STDERR logger is only active when a debugger is attached to your application. If you do not wish to use the STDERR logger you can disable it by turning it off in your TestFlight options
[TestFlight setOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"logToSTDERR"]];
The default option is YES.
11. iOS3
We now require that anyone who is writing an application that supports iOS3 add the System.framework as an optional link. In order to provide a better shutdown experience we send any large log files to our servers in the background. To add System.framework as an optional link you can follow
END END

View File

@ -6,7 +6,7 @@
// Copyright 2011 TestFlight. All rights reserved. // Copyright 2011 TestFlight. All rights reserved.
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#define TESTFLIGHT_SDK_VERSION @"0.8.2" #define TESTFLIGHT_SDK_VERSION @"0.8.3"
#undef TFLog #undef TFLog
#if __cplusplus #if __cplusplus
@ -51,7 +51,9 @@ extern "C" {
* library installs crash handlers overtop of the TestFlight Crash Handlers * library installs crash handlers overtop of the TestFlight Crash Handlers
* logToConsole [ NSNumber numberWithBool:YES ] YES - default, sends log statements to Apple System Log and TestFlight log * logToConsole [ NSNumber numberWithBool:YES ] YES - default, sends log statements to Apple System Log and TestFlight log
* NO - sends log statements to TestFlight log only * NO - sends log statements to TestFlight log only
* sendLogOnlyOnCrash [ NSNumber numberWithBool:YES ] NO - default, sends logs to TestFlight at the end of every session * logToSTDERR [ NSNumber numberWithBool:YES ] YES - default, sends log statements to STDERR when debugger is attached
* NO - sends log statements to TestFlight log only
* sendLogOnlyOnCrash [ NSNumber numberWithBool:YES ] NO - default, sends logs to TestFlight at the end of every session
* YES - sends logs statements to TestFlight only if there was a crash * YES - sends logs statements to TestFlight only if there was a crash
*/ */
+ (void)setOptions:(NSDictionary*)options; + (void)setOptions:(NSDictionary*)options;
@ -68,4 +70,12 @@ extern "C" {
*/ */
+ (void)openFeedbackView; + (void)openFeedbackView;
/**
* Submits custom feedback to the site. Sends the data in feedback to the site. This is to be used as the method to submit
* feedback from custom feedback forms.
*
* @param feedback Your users feedback, method does nothing if feedback is nil
*/
+ (void)submitFeedback:(NSString*)feedback;
@end @end

Binary file not shown.

View File

@ -1,3 +1,41 @@
0.8.3 - February 14, 2012
Rolled previous beta code into release builds
No longer allow in application updates to occur in applications that were obtained from the app store.
Tested compiled library with:
Xcode 4.3
Xcode 4.2
Xcode 4.1
Xcode 3.2.6
0.8.3 BETA 5 - February 10, 2012
Changed logging from asynchronous to synchronous.
Resolved crash when looking for a log path failed.
Added submitFeedback to the TestFlight class to allow for custom feedback forms.
0.8.3 BETA 4 - January 20, 2012
Resolved an issue that occured when an application was upgraded from 0.8.3 BETA 1 to 0.8.3 BETA 3+ with unsent data from 0.8.3 BETA 1
0.8.3 BETA 3 - January 19, 2012
On crash log files over 64k will not be sent until next launch.
Known Issues:
Logging massive amounts of data at the end of a session may prevent the application from launching in time on next launch
0.8.3 BETA 2 - January 13, 2012
libz.dylib is now required to be added to your "Link Binary with Libraries" build phase
Log file compression, The compression is done on an as needed basis rather than before sending
Changed all outgoing data from JSON to MessagePack
Added option logToSTDERR to disable the STDERR logger
0.8.3 BETA 1 - December 29, 2011
In rare occurrences old session data that had not been sent to our server may have been discarded or attached to the wrong build. It is now no longer discarded
Made sending of Session End events more robust
Network queuing system does better bursting of unsent data
Log files that are larger than 64K are now sent sometime after the next launch
Log files that are larger than 16MB are no longer supported and will be replaced with a message indicating the log file was too large
Fixed crashes while resuming from background
0.8.2 - December 20, 2011 0.8.2 - December 20, 2011
Promoted 0.8.2 BETA 4 to stable Promoted 0.8.2 BETA 4 to stable
@ -5,7 +43,7 @@ Known Issues:
Under some circumstances Session End events may not be sent until the next launch. Under some circumstances Session End events may not be sent until the next launch.
With large log files Session End events may take a long time to show up. With large log files Session End events may take a long time to show up.
Tested compiled library with Tested compiled library with:
Xcode 4.3 Xcode 4.3
Xcode 4.2 Xcode 4.2
Xcode 4.1 Xcode 4.1
@ -13,12 +51,11 @@ Xcode 3.2.6
0.8.2 BETA 4 - December 12, 2011 0.8.2 BETA 4 - December 12, 2011
Prevented "The string argument is NULL" from occuring during finishedHandshake in rare cases Prevented "The string argument is NULL" from occuring during finishedHandshake in rare cases
Resolved issue where background data may not be sent Resolved issue where data recorded while offline may not be sent
0.8.2 BETA 3 - December 8, 2011 0.8.2 BETA 3 - December 8, 2011
Added auto-release pools to background setup and tear down Added auto-release pools to background setup and tear down
Added C++ specific code to TFLog declaration
0.8.2 BETA 2 - December 5, 2011 0.8.2 BETA 2 - December 5, 2011
Fixed the "pointer being freed was not allocated" bug Fixed the "pointer being freed was not allocated" bug
@ -28,7 +65,7 @@ Fixed an issue where Session End events may not be sent until next launch
Fixed an issue where duplicate events could be sent Fixed an issue where duplicate events could be sent
Fixed an issue with Session End events not being sent from some iPod touch models Fixed an issue with Session End events not being sent from some iPod touch models
Tested compiled library with Tested compiled library with:
Xcode 4.2 Xcode 4.2
Xcode 4.1 Xcode 4.1
Xcode 3.2.6 Xcode 3.2.6
@ -46,7 +83,7 @@ Fixed compability issues with iOS 3
Added calling into the rootViewController shouldAutorotateToInterfaceOrientation if a rootViewController is set Added calling into the rootViewController shouldAutorotateToInterfaceOrientation if a rootViewController is set
Made the comments in TestFlight.h compatible with Appledoc Made the comments in TestFlight.h compatible with Appledoc
Tested compiled library with Tested compiled library with:
Xcode 4.2 Xcode 4.2
Xcode 4.1 Xcode 4.1
Xcode 3.2 Xcode 3.2

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,11 @@
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<AdditionalOptions> <AdditionalOptions>
<AdditionalOption
key = "OBJC_PRINT_EXCEPTIONS"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption <AdditionalOption
key = "NSZombieEnabled" key = "NSZombieEnabled"
value = "YES" value = "YES"
@ -69,7 +74,7 @@
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = "" savedToolIdentifier = ""
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
buildConfiguration = "Release" buildConfiguration = "Debug"
debugDocumentVersioning = "YES"> debugDocumentVersioning = "YES">
<BuildableProductRunnable> <BuildableProductRunnable>
<BuildableReference <BuildableReference

View File

@ -73,9 +73,16 @@
@try { @try {
[TestFlight takeOff:@"bd44885deee7adce0645ce8e5498d80a_NDQ5NDQyMDExLTEyLTAyIDExOjM1OjQ4LjQ2NjM4NA"]; [TestFlight takeOff:@"bd44885deee7adce0645ce8e5498d80a_NDQ5NDQyMDExLTEyLTAyIDExOjM1OjQ4LjQ2NjM4NA"];
[TestFlight setOptions:[NSDictionary dictionaryWithObjectsAndKeys: [TestFlight setOptions:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], @"logToConsole", [NSNumber numberWithBool:NO], @"logToConsole",
[NSNumber numberWithBool:NO], @"logToSTDERR",
nil]]; nil]];
[TestFlight passCheckpoint:MPTestFlightCheckpointLaunched]; [TestFlight passCheckpoint:MPTestFlightCheckpointLaunched];
[[Logger get] registerListener:^BOOL(LogMessage *message) {
if (message.level >= LogLevelInfo)
TFLog(@"%@", message);
return YES;
}];
} }
@catch (NSException *exception) { @catch (NSException *exception) {
err(@"TestFlight: %@", exception); err(@"TestFlight: %@", exception);
@ -146,6 +153,8 @@
[self loadKeyPhrase]; [self loadKeyPhrase];
}]; }];
[self loadKeyPhrase];
return [super application:application didFinishLaunchingWithOptions:launchOptions]; return [super application:application didFinishLaunchingWithOptions:launchOptions];
} }
- (void)applicationDidBecomeActive:(UIApplication *)application { - (void)applicationDidBecomeActive:(UIApplication *)application {
@ -171,13 +180,19 @@
- (void)loadKeyPhrase { - (void)loadKeyPhrase {
if (self.keyPhrase)
return;
if ([[MPConfig get].forgetKeyPhrase boolValue]) { if ([[MPConfig get].forgetKeyPhrase boolValue]) {
[self forgetKeyPhrase]; [self forgetKeyPhrase];
return; return;
} }
[self loadStoredKeyPhrase]; [self loadStoredKeyPhrase];
if (!self.keyPhrase) { if (self.keyPhrase)
[[UIApplication sharedApplication] setStatusBarHidden:NO
withAnimation:UIStatusBarAnimationSlide];
else {
// Key phrase is not known. Ask user to set/specify it. // Key phrase is not known. Ask user to set/specify it.
dbg(@"Key phrase not known. Will ask user."); dbg(@"Key phrase not known. Will ask user.");
[self askKeyPhrase]; [self askKeyPhrase];

View File

@ -11,7 +11,7 @@
#import "MPSearchDelegate.h" #import "MPSearchDelegate.h"
#import "IASKAppSettingsViewController.h" #import "IASKAppSettingsViewController.h"
@interface MPMainViewController : UIViewController <MPTypeDelegate, UITextFieldDelegate, UISearchBarDelegate, MPSearchResultsDelegate, UIWebViewDelegate, IASKSettingsDelegate> @interface MPMainViewController : UIViewController <MPTypeDelegate, UITextFieldDelegate, MPSearchResultsDelegate, UIWebViewDelegate, IASKSettingsDelegate>
@property (strong, nonatomic) MPElementEntity *activeElement; @property (strong, nonatomic) MPElementEntity *activeElement;
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchResultsController; @property (strong, nonatomic) IBOutlet MPSearchDelegate *searchResultsController;

View File

@ -74,7 +74,7 @@
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
self.searchTipContainer.hidden = YES; self.searchTipContainer.hidden = YES;
@ -147,11 +147,11 @@
[[MPAppDelegate get] saveContext]; [[MPAppDelegate get] saveContext];
if (animated) if (animated)
[UIView animateWithDuration:0.2 animations:^{ [UIView animateWithDuration:0.3f animations:^{
[self updateWasAnimated:YES]; [self updateWasAnimated:animated];
}]; }];
else else
[self updateWasAnimated:NO]; [self updateWasAnimated:animated];
} }
- (void)updateWasAnimated:(BOOL)animated { - (void)updateWasAnimated:(BOOL)animated {
@ -219,7 +219,7 @@
[[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]]]]; [[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]]]];
NSString *error = [self.helpView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setClass('%@');", NSString *error = [self.helpView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setClass('%@');",
ClassNameFromMPElementType(self.activeElement.type)]]; ClassNameFromMPElementType(self.activeElement.type)]];
if (error.length) if (error.length)
err(@"setClass: %@", error); err(@"setClass: %@", error);
} }
@ -312,7 +312,7 @@
self.contentField.enabled = YES; self.contentField.enabled = YES;
[self.contentField becomeFirstResponder]; [self.contentField becomeFirstResponder];
} }
#ifndef PRODUCTION #ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointEditPassword]; [TestFlight passCheckpoint:MPTestFlightCheckpointEditPassword];
#endif #endif
@ -325,7 +325,7 @@
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
self.alertBody.text = nil; self.alertBody.text = nil;
}]; }];
#ifndef PRODUCTION #ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointCloseAlert]; [TestFlight passCheckpoint:MPTestFlightCheckpointCloseAlert];
#endif #endif
@ -354,7 +354,7 @@
settingsVC.delegate = self; settingsVC.delegate = self;
[self.navigationController pushViewController:settingsVC animated:YES]; [self.navigationController pushViewController:settingsVC animated:YES];
break; break;
} }
#ifndef PRODUCTION #ifndef PRODUCTION
case 4: case 4:
[TestFlight openFeedbackView]; [TestFlight openFeedbackView];
@ -387,7 +387,7 @@
// Type requires a different class of element. Recreate the element. // Type requires a different class of element. Recreate the element.
[[MPAppDelegate managedObjectContext] performBlockAndWait:^{ [[MPAppDelegate managedObjectContext] performBlockAndWait:^{
MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromMPElementType(type) MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromMPElementType(type)
inManagedObjectContext:[MPAppDelegate managedObjectContext]]; inManagedObjectContext:[MPAppDelegate managedObjectContext]];
newElement.name = self.activeElement.name; newElement.name = self.activeElement.name;
newElement.mpHashHex = self.activeElement.mpHashHex; newElement.mpHashHex = self.activeElement.mpHashHex;
newElement.uses = self.activeElement.uses; newElement.uses = self.activeElement.uses;
@ -410,24 +410,17 @@
- (void)didSelectElement:(MPElementEntity *)element { - (void)didSelectElement:(MPElementEntity *)element {
self.activeElement = element; if (element) {
[self.activeElement use]; self.activeElement = element;
[self.activeElement use];
[self.searchDisplayController setActive:NO animated:YES];
self.searchDisplayController.searchBar.text = self.activeElement.name; [self.searchDisplayController setActive:NO animated:YES];
self.searchDisplayController.searchBar.text = self.activeElement.name;
#ifndef PRODUCTION #ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointSelectElement]; [TestFlight passCheckpoint:MPTestFlightCheckpointSelectElement];
#endif
[self updateAnimated:YES];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointCancelSearch];
#endif #endif
}
[self updateAnimated:YES]; [self updateAnimated:YES];
} }

View File

@ -15,9 +15,11 @@
@end @end
@interface MPSearchDelegate : NSObject <UITableViewDelegate, UITableViewDataSource, UISearchDisplayDelegate, NSFetchedResultsControllerDelegate> @interface MPSearchDelegate : NSObject <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate, NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) NSDateFormatter *dateFormatter;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; @property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (strong, nonatomic) NSString *query;
@property (weak, nonatomic) IBOutlet id<MPSearchResultsDelegate> delegate; @property (weak, nonatomic) IBOutlet id<MPSearchResultsDelegate> delegate;
@property (weak, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController; @property (weak, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController;

View File

@ -18,40 +18,85 @@
@end @end
@implementation MPSearchDelegate @implementation MPSearchDelegate
@synthesize query;
@synthesize dateFormatter;
@synthesize fetchedResultsController; @synthesize fetchedResultsController;
@synthesize delegate; @synthesize delegate;
@synthesize searchDisplayController; @synthesize searchDisplayController;
@synthesize searchTipContainer; @synthesize searchTipContainer;
- (id)init {
if (!([super init]))
return nil;
self.dateFormatter = [NSDateFormatter new];
self.dateFormatter.timeStyle = NSDateFormatterNoStyle;
self.dateFormatter.dateStyle = NSDateFormatterShortStyle;
self.query = @"";
return self;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
UITableView *tableView = self.searchDisplayController.searchResultsTableView;
for (NSUInteger section = 0; section < [self numberOfSectionsInTableView:tableView]; ++section) {
NSUInteger rowCount = [self tableView:tableView numberOfRowsInSection:section];
if (!rowCount)
continue;
if (rowCount == 1)
[self tableView:tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
break;
}
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointCancelSearch];
#endif
[self.delegate didSelectElement:nil];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchBar.searchResultsButtonSelected && !searchText.length)
searchBar.text = @" ";
self.query = [searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (!self.query)
self.query = @"";
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller { - (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
self.searchDisplayController.searchBar.text = @""; controller.searchBar.prompt = @"Enter the site's name (eg. apple.com):";
self.searchDisplayController.searchBar.prompt = @"Enter the site's domain name (eg. apple.com):";
[UIView animateWithDuration:0.2f animations:^{ [UIView animateWithDuration:0.2f animations:^{
self.searchTipContainer.alpha = 0; self.searchTipContainer.alpha = 0;
}]; }];
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
[self update]; controller.searchBar.text = controller.searchBar.searchResultsButtonSelected? @" ": @"";
self.query = @"";
} }
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller { - (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
self.searchDisplayController.searchBar.prompt = nil; controller.searchBar.prompt = nil;
controller.searchBar.searchResultsButtonSelected = NO;
} }
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView { - (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
[[NSNotificationCenter defaultCenter] addObserverForName:UIScreenModeDidChangeNotification
object:nil queue:nil usingBlock:^(NSNotification *note) {
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
}];
tableView.backgroundColor = [UIColor blackColor]; tableView.backgroundColor = [UIColor blackColor];
tableView.rowHeight = 34.0f;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone; tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.rowHeight = 48.0f;
} }
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
@ -63,19 +108,18 @@
- (void)update { - (void)update {
NSString *query = self.searchDisplayController.searchBar.text; assert(self.query);
if (!query) assert([MPAppDelegate get].keyPhraseHashHex);
query = @"";
NSFetchRequest *fetchRequest = [[MPAppDelegate get].managedObjectModel NSFetchRequest *fetchRequest = [[MPAppDelegate get].managedObjectModel
fetchRequestFromTemplateWithName:@"MPSearchElement" fetchRequestFromTemplateWithName:@"MPElements"
substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys: substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
query, @"query", self.query, @"query",
[MPAppDelegate get].keyPhraseHashHex, @"mpHashHex", [MPAppDelegate get].keyPhraseHashHex, @"mpHashHex",
nil]]; nil]];
[fetchRequest setSortDescriptors: [fetchRequest setSortDescriptors:
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]]; [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
self.fetchedResultsController.delegate = nil;
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[MPAppDelegate managedObjectContext] managedObjectContext:[MPAppDelegate managedObjectContext]
sectionNameKeyPath:nil cacheName:nil]; sectionNameKeyPath:nil cacheName:nil];
@ -107,8 +151,7 @@
case NSFetchedResultsChangeUpdate: case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] [self configureCell:[tableView cellForRowAtIndexPath:indexPath]
inTableView:tableView inTableView:tableView atIndexPath:indexPath];
atIndexPath:indexPath];
break; break;
case NSFetchedResultsChangeMove: case NSFetchedResultsChangeMove:
@ -121,7 +164,7 @@
} }
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
UITableView *tableView = self.searchDisplayController.searchResultsTableView; UITableView *tableView = self.searchDisplayController.searchResultsTableView;
@ -146,32 +189,38 @@
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count] + 1; return [[self.fetchedResultsController sections] count] + ([self.query length]? 1: 0);
} }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == [self numberOfSectionsInTableView:tableView] - 1) if (section < [[self.fetchedResultsController sections] count])
return 1; return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects]; return 1;
} }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementSearch"]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementSearch"];
if (!cell) { if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"MPElementSearch"]; cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MPElementSearch"];
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"ui_list_middle"] resizableImageWithCapInsets:UIEdgeInsetsMake(5, 5, 5, 5)]];
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ui_list_middle"]];
backgroundImageView.frame = CGRectMake(-5, 0, 330, 34); backgroundImageView.frame = CGRectMake(-5, 0, 330, 34);
UIView *backgroundView = [[UIView alloc] initWithFrame:cell.frame]; backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
backgroundImageView.contentStretch = CGRectMake(0.2f, 0.2f, 0.6f, 0.6f);
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 34)];
[backgroundView addSubview:backgroundImageView]; [backgroundView addSubview:backgroundImageView];
backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
cell.backgroundView = backgroundView; cell.backgroundView = backgroundView;
cell.textLabel.backgroundColor = [UIColor clearColor]; cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor whiteColor]; cell.textLabel.textColor = [UIColor whiteColor];
cell.detailTextLabel.backgroundColor = [UIColor clearColor]; cell.detailTextLabel.backgroundColor = [UIColor clearColor];
cell.detailTextLabel.textColor = [UIColor lightGrayColor]; cell.detailTextLabel.textColor = [UIColor lightGrayColor];
cell.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
cell.clipsToBounds = YES;
} }
[self configureCell:cell inTableView:tableView atIndexPath:indexPath]; [self configureCell:cell inTableView:tableView atIndexPath:indexPath];
@ -185,11 +234,12 @@
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath]; MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = element.name; cell.textLabel.text = element.name;
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", element.uses]; cell.detailTextLabel.text = [NSString stringWithFormat:@"Used %d times, last on %@",
element.uses, [self.dateFormatter stringFromDate:[NSDate dateWithTimeIntervalSinceReferenceDate:element.lastUsed]]];
} else { } else {
// "New" section // "New" section
cell.textLabel.text = self.searchDisplayController.searchBar.text; cell.textLabel.text = self.query;
cell.detailTextLabel.text = @"New"; cell.detailTextLabel.text = @"Create a new site.";
} }
} }
@ -200,7 +250,7 @@
else { else {
// "New" section. // "New" section.
NSString *siteName = self.searchDisplayController.searchBar.text; NSString *siteName = self.query;
[AlertViewController showAlertWithTitle:@"New Site" [AlertViewController showAlertWithTitle:@"New Site"
message:l(@"Do you want to create a new site named:\n%@", siteName) message:l(@"Do you want to create a new site named:\n%@", siteName)
viewStyle:UIAlertViewStyleDefault viewStyle:UIAlertViewStyleDefault
@ -228,10 +278,10 @@
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (--section == -1) if (section < [[self.fetchedResultsController sections] count])
return @""; return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
return [[[self.fetchedResultsController sections] objectAtIndex:section] name]; return @"";
} }
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
@ -246,19 +296,17 @@
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == [[self.fetchedResultsController sections] count]) if (indexPath.section < [[self.fetchedResultsController sections] count]) {
// "New" section. if (editingStyle == UITableViewCellEditingStyleDelete)
return; [self.fetchedResultsController.managedObjectContext performBlock:^{
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (editingStyle == UITableViewCellEditingStyleDelete) [self.fetchedResultsController.managedObjectContext deleteObject:element];
[self.fetchedResultsController.managedObjectContext performBlock:^{
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.fetchedResultsController.managedObjectContext deleteObject:element];
#ifndef PRODUCTION #ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointDeleteElement]; [TestFlight passCheckpoint:MPTestFlightCheckpointDeleteElement];
#endif #endif
}]; }];
}
} }

View File

@ -130,6 +130,13 @@ typedef enum {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
} }
- (void)viewDidAppear:(BOOL)animated {
[self.field becomeFirstResponder];
[super viewDidAppear:animated];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField { - (BOOL)textFieldShouldReturn:(UITextField *)textField {
if ([textField.text length]) { if ([textField.text length]) {

View File

@ -334,7 +334,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<rect key="frame" x="0.0" y="64" width="320" height="416"/> <rect key="frame" x="0.0" y="64" width="320" height="416"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="top" image="Content-Backdrop.png" id="0hY-LL-ITu"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="bottom" image="background.png" id="0hY-LL-ITu">
<rect key="frame" x="0.0" y="44" width="320" height="372"/> <rect key="frame" x="0.0" y="44" width="320" height="372"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView> </imageView>
@ -342,10 +342,6 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<rect key="frame" x="0.0" y="43" width="320" height="175"/> <rect key="frame" x="0.0" y="43" width="320" height="175"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="bottom" image="background.png" id="enb-OH-DVZ">
<rect key="frame" x="0.0" y="0.0" width="320" height="175"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" alpha="0.80000000000000004" contentMode="scaleToFill" image="ui_panel_container.png" id="0Yh-Py-lB6"> <imageView userInteractionEnabled="NO" alpha="0.80000000000000004" contentMode="scaleToFill" image="ui_panel_container.png" id="0Yh-Py-lB6">
<rect key="frame" x="11" y="20" width="298" height="87"/> <rect key="frame" x="11" y="20" width="298" height="87"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
@ -382,7 +378,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/> <size key="shadowOffset" width="0.0" height="1"/>
</label> </label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="S3cre7^Pa$swcrD" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" id="fiX-10-fif"> <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="S3cre7^Pa$swcrD" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" id="fiX-10-fif">
<rect key="frame" x="20" y="46" width="280" height="60"/> <rect key="frame" x="20" y="33" width="280" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/> <color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="26"/> <fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="26"/>
@ -401,8 +397,8 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
<size key="shadowOffset" width="0.0" height="1"/> <size key="shadowOffset" width="0.0" height="1"/>
</label> </label>
<button opaque="NO" alpha="0.50000000000000011" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="jec-mu-nPt"> <button opaque="NO" alpha="0.50000000000000011" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="jec-mu-nPt">
<rect key="frame" x="274.5" y="18.5" width="37" height="37"/> <rect key="frame" x="272.5" y="18.5" width="36.5" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="contentEdgeInsets" minX="5" minY="5" maxX="5" maxY="5"/> <inset key="contentEdgeInsets" minX="5" minY="5" maxX="5" maxY="5"/>
<state key="normal" image="icon_plus.png"> <state key="normal" image="icon_plus.png">
@ -417,7 +413,7 @@ The passwords aren't saved anywhere. This is a major advantage: if you loose yo
</connections> </connections>
</button> </button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="9FS-fS-xH6"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="9FS-fS-xH6">
<rect key="frame" x="274.5" y="18.5" width="37.5" height="37"/> <rect key="frame" x="273" y="18.5" width="36" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="contentEdgeInsets" minX="5" minY="5" maxX="5" maxY="5"/> <inset key="contentEdgeInsets" minX="5" minY="5" maxX="5" maxY="5"/>
@ -538,13 +534,13 @@ L4m3P4sSw0rD</string>
</subviews> </subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view> </view>
<searchBar contentMode="redraw" barStyle="blackOpaque" placeholder="Site" id="qeo-n2-WVh"> <searchBar contentMode="redraw" barStyle="blackOpaque" placeholder="Site" showsSearchResultsButton="YES" id="qeo-n2-WVh">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<gestureRecognizers/> <gestureRecognizers/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL"/> <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL"/>
<connections> <connections>
<outlet property="delegate" destination="PQa-Xl-A3x" id="oXp-0r-Gkl"/> <outlet property="delegate" destination="0QO-2P-OhD" id="Maj-Iz-6Hb"/>
</connections> </connections>
</searchBar> </searchBar>
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="zOR-Du-qRL"> <view userInteractionEnabled="NO" contentMode="scaleToFill" id="zOR-Du-qRL">
@ -800,7 +796,6 @@ L4m3P4sSw0rD</string>
</scene> </scene>
</scenes> </scenes>
<resources> <resources>
<image name="Content-Backdrop.png" width="16" height="16"/>
<image name="Square-bottom.png" width="551" height="58"/> <image name="Square-bottom.png" width="551" height="58"/>
<image name="background.png" width="480" height="480"/> <image name="background.png" width="480" height="480"/>
<image name="guide_page_0.png" width="320" height="480"/> <image name="guide_page_0.png" width="320" height="480"/>
@ -826,6 +821,64 @@ L4m3P4sSw0rD</string>
<image name="ui_spinner.png" width="75" height="75"/> <image name="ui_spinner.png" width="75" height="75"/>
<image name="ui_textfield.png" width="158" height="34"/> <image name="ui_textfield.png" width="158" height="34"/>
</resources> </resources>
<classes>
<class className="MPGuideViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPGuideViewController.h"/>
<relationships>
<relationship kind="action" name="close"/>
<relationship kind="outlet" name="scrollView" candidateClass="UIScrollView"/>
</relationships>
</class>
<class className="MPMainViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPMainViewController.h"/>
<relationships>
<relationship kind="action" name="action:" candidateClass="UIBarButtonItem"/>
<relationship kind="action" name="closeAlert"/>
<relationship kind="action" name="copyContent"/>
<relationship kind="action" name="editPassword"/>
<relationship kind="action" name="incrementPasswordCounter"/>
<relationship kind="outlet" name="alertBody" candidateClass="UITextView"/>
<relationship kind="outlet" name="alertContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="alertTitle" candidateClass="UILabel"/>
<relationship kind="outlet" name="contentContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="contentField" candidateClass="UITextField"/>
<relationship kind="outlet" name="contentTipBody" candidateClass="UILabel"/>
<relationship kind="outlet" name="contentTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="contentTipEditIcon" candidateClass="UIImageView"/>
<relationship kind="outlet" name="helpContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="helpView" candidateClass="UIWebView"/>
<relationship kind="outlet" name="passwordCounter" candidateClass="UILabel"/>
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
<relationship kind="outlet" name="searchResultsController" candidateClass="MPSearchDelegate"/>
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
</relationships>
</class>
<class className="MPSearchDelegate" superclassName="NSObject">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPSearchDelegate.h"/>
<relationships>
<relationship kind="outlet" name="delegate"/>
<relationship kind="outlet" name="searchDisplayController" candidateClass="UISearchDisplayController"/>
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
</relationships>
</class>
<class className="MPTypeViewController" superclassName="UITableViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPTypeViewController.h"/>
</class>
<class className="MPUnlockViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPUnlockViewController.h"/>
<relationships>
<relationship kind="action" name="changeMP"/>
<relationship kind="outlet" name="changeMPView" candidateClass="UIView"/>
<relationship kind="outlet" name="field" candidateClass="UITextField"/>
<relationship kind="outlet" name="lock" candidateClass="UIImageView"/>
<relationship kind="outlet" name="messageLabel" candidateClass="UILabel"/>
<relationship kind="outlet" name="spinner" candidateClass="UIImageView"/>
</relationships>
</class>
</classes>
<simulatedMetricsContainer key="defaultSimulatedMetrics"> <simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar" statusBarStyle="blackTranslucent"/> <simulatedStatusBarMetrics key="statusBar" statusBarStyle="blackTranslucent"/>
<simulatedOrientationMetrics key="orientation"/> <simulatedOrientationMetrics key="orientation"/>

View File

@ -56,6 +56,8 @@
<array> <array>
<string>armv7</string> <string>armv7</string>
</array> </array>
<key>UIStatusBarHidden</key>
<true/>
<key>UIStatusBarStyle</key> <key>UIStatusBarStyle</key>
<string>UIStatusBarStyleBlackOpaque</string> <string>UIStatusBarStyleBlackOpaque</string>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="872" systemVersion="11C74" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic"> <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1170" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES"> <entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
<attribute name="lastUsed" attributeType="Date" syncable="YES"/> <attribute name="lastUsed" attributeType="Date" syncable="YES"/>
<attribute name="mpHashHex" optional="YES" attributeType="String" syncable="YES"/> <attribute name="mpHashHex" optional="YES" attributeType="String" syncable="YES"/>
@ -13,10 +13,10 @@
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES"> <entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/> <attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
</entity> </entity>
<fetchRequest name="MPSearchElement" entity="MPElementEntity" predicateString="name BEGINSWITH[cd] $query AND mpHashHex == $mpHashHex"/> <fetchRequest name="MPElements" entity="MPElementEntity" predicateString="($query == '' OR name BEGINSWITH[cd] $query) AND mpHashHex == $mpHashHex"/>
<elements> <elements>
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/> <element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/>
<element name="MPElementGeneratedEntity" positionX="160" positionY="192" width="128" height="60"/> <element name="MPElementGeneratedEntity" positionX="160" positionY="192" width="128" height="60"/>
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/> <element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/>
</elements> </elements>
</model> </model>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 883 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 806 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 686 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 851 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Some files were not shown because too many files have changed in this diff Show More