2
0

Add TestFlight.

[ADDED]     TestFlightSDK implemented.
This commit is contained in:
Maarten Billemont 2012-02-06 00:40:20 +01:00
parent 2b9fcfe030
commit 360f71c4fa
16 changed files with 701 additions and 99 deletions

199
External/TestFlightSDK/README.txt vendored Normal file
View File

@ -0,0 +1,199 @@
Thanks for downloading the TestFlight SDK 0.8.2!
This document is also available on the web at https://www.testflightapp.com/sdk/doc
1. Why use the TestFlight SDK?
2. Considerations
3. How do I integrate the SDK into my project?
4. Using the Checkpoint API
5. Using the Feedback API
6. Upload your build
7. Questions API
8. View your results
9. Advanced Exception Handling
10. Remote Logging
START
1. Why use the TestFlight SDK?
The TestFlight SDK allows you to track how beta testers are testing your application. Out of the box we track simple usage information, such as which tester is using your application, their device model/OS, how long they used the application, logs of their test session, and automatic recording of any crashes they encounter.
To get the most out of the SDK we have provided the Checkpoint API.
The Checkpoint API is used to help you track exactly how your testers are using your application. Curious about which users passed level 5 in your game, or posted their high score to Twitter, or found that obscure feature? With a single line of code you can finally gather all this information. Wondering how many times your app has crashed? Wondering who your power testers are? We've got you covered. See more information on the Checkpoint API in section 4.
Alongside the Checkpoint API is the Questions interface. The Questions interface is managed on a per build basis on the TestFlight website. Find out more about the Questions Interface in section 6.
For more detailed debugging we have a remote logging solution. Find out more about our logging system with TFLog in the Remote Logging section.
2. Considerations
Information gathered by the SDK is sent to the website in real time. When an application is put into the background (iOS 4.x) or terminated (iOS 3.x) we try to send the finalizing information for the session during the time allowed for finalizing the application. Should all of the data not get sent the remaining data will be sent the next time the application is launched. As such, to get the most out of the SDK we recommend your application support iOS 4.0 and higher.
This SDK can be run from both the iPhone Simulator and Device and has been tested using Xcode 4.0.
3. How do I integrate the SDK into my project?
1. Add the files to your project: File -> Add Files to "<your project name>"
1. Find and select the folder that contains the SDK
2. Make sure that "Copy items into destination folder (if needed)" is checked
3. Set Folders to "Create groups for any added folders"
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
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. 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
3. In your Application Delegate:
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
#ifdef __OBJC__ section.
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.
3. Launch TestFlight with your Team Token
-(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// start of your application:didFinishLaunchingWithOptions
// ...
[TestFlight takeOff:@"Insert your Team Token here"];
// The rest of your application:didFinishLaunchingWithOptions method
// ...
}
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.
1. Deployment Post Processing
2. Strip Debug Symbols During Copy
3. Strip Linked Product
4. Use the Checkpoint API to create important checkpoints throughout your application.
When a tester does something you care about in your app you can pass a checkpoint. For example completing a level, adding a todo item, etc. The checkpoint progress is used to provide insight into how your testers are testing your apps. The passed checkpoints are also attached to crashes, which can help when creating steps to replicate.
`[TestFlight passCheckpoint:@"CHECKPOINT_NAME"];`
Use `passCheckpoint:` to track when a user performs certain tasks in your application. This can be useful for making sure testers are hitting all parts of your application, as well as tracking which testers are being thorough.
5. Using the Feedback API
To launch unguided feedback call the `openFeedbackView` method. We recommend that you call this from a GUI element.
-(IBAction)launchFeedback {
[TestFlight openFeedbackView];
}
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.
After you have integrated the SDK into your application you need to upload your build to TestFlight. You can upload from your dashboard or or using the Upload API, full documentation at <https://testflightapp.com/api/doc/>
7. Add Questions to Checkpoints
In order to ask a question, you'll need to associate it with a checkpoint. Make sure your checkpoints are initialized by running your app and hitting them all yourself before you start adding questions.
There are three question types available: Yes/No, Multiple Choice, and Long Answer.
To create questions, visit your builds Questions page and click on 'Add Question'. If you choose Multiple Choice, you'll need to enter a list of possible answers for your testers to choose from &mdash; otherwise, you'll only need to enter your question's, well, question. If your build has no questions, you can also choose to migrate questions from another build (because seriously &mdash; who wants to do all that typing again)?
After restarting your application on an approved device, when you pass the checkpoint associated with your questions a TestFlight modal question form will appear on the screen asking the beta tester to answer your question.
After you upload a new build to TestFlight you will need to associate questions once again. However if your checkpoints and questions have remained the same you can choose "copy questions from an older build" and choose which build to copy the questions from.
8. View your results.
As testers install your build and start to test it you will see their session data on the web on the build report page for the build you've uploaded.
9. Advanced Exception Handling
An uncaught exception means that your application is in an unknown state and there is not much that you can do but try and exit gracefully. Our SDK does its best to get the data we collect in this situation to you while it is crashing, but it is designed in such a way that the important act of saving the data occurs in as safe way a way as possible before trying to send anything. If you do use uncaught exception or signal handlers install your handlers before calling `takeOff`. Our SDK will then call your handler while ours is running. For example:
/*
My Apps Custom uncaught exception catcher, we do special stuff here, and TestFlight takes care of the rest
**/
void HandleExceptions(NSException *exception) {
NSLog(@"This is where we save the application data during a exception");
// Save application data on crash
}
/*
My Apps Custom signal catcher, we do special stuff here, and TestFlight takes care of the rest
**/
void SignalHandler(int sig) {
NSLog(@"This is where we save the application data during a signal");
// Save application data on crash
}
-(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// installs HandleExceptions as the Uncaught Exception Handler
NSSetUncaughtExceptionHandler(&HandleExceptions);
// create the signal action structure
struct sigaction newSignalAction;
// initialize the signal action structure
memset(&newSignalAction, 0, sizeof(newSignalAction));
// set SignalHandler as the handler in the signal action structure
newSignalAction.sa_handler = &SignalHandler;
// set SignalHandler as the handlers for SIGABRT, SIGILL and SIGBUS
sigaction(SIGABRT, &newSignalAction, NULL);
sigaction(SIGILL, &newSignalAction, NULL);
sigaction(SIGBUS, &newSignalAction, NULL);
// Call takeOff after install your own unhandled exception and signal handlers
[TestFlight takeOff:@"Insert your Team Token here"];
// continue with your application initialization
}
You do not need to add the above code if your application does not use exception handling already.
10. Remote Logging
To perform remote logging you can use the TFLog method which logs in a few different methods described below. In order to make the transition from NSLog to TFLog easy we have used the same method signature for TFLog as NSLog. You can easily switch over to TFLog by adding the following macro to your header
#define NSLog TFLog
That will do a switch from NSLog to TFLog, if you want more information, such as file name and line number you can use a macro like
#define NSLog(__FORMAT__, ...) TFLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
Which will produce output that looks like
-[HTFCheckpointsController showYesNoQuestion:] [Line 45] Pressed YES/NO
We have implemented three different loggers.
1. TestFlight logger
2. Apple System Log logger
3. STDERR logger
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"]];
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.
END
Please contact us if you have any questions.
The TestFlight Team
w. http://www.testflightapp.com
e. beta@testflightapp.com

71
External/TestFlightSDK/TestFlight.h vendored Normal file
View File

@ -0,0 +1,71 @@
//
// TestFlight.h
// libTestFlight
//
// Created by Jonathan Janzen on 06/11/11.
// Copyright 2011 TestFlight. All rights reserved.
#import <Foundation/Foundation.h>
#define TESTFLIGHT_SDK_VERSION @"0.8.2"
#undef TFLog
#if __cplusplus
extern "C" {
#endif
void TFLog(NSString *format, ...);
#if __cplusplus
}
#endif
/**
* TestFlight object
* All methods are class level
*/
@interface TestFlight : NSObject {
}
/**
* Add custom environment information
* If you want to track custom information such as a user name from your application you can add it here
*
* @param information A string containing the environment you are storing
* @param key The key to store the information with
*/
+ (void)addCustomEnvironmentInformation:(NSString *)information forKey:(NSString*)key;
/**
* Starts a TestFlight session
*
* @param teamToken Will be your team token obtained from https://testflightapp.com/dashboard/team/edit/
*/
+ (void)takeOff:(NSString *)teamToken;
/**
* Sets custom options
*
* @param options NSDictionary containing the options you want to set available options are described below
*
* Option Accepted Values Description
* reinstallCrashHandlers [ NSNumber numberWithBool:YES ] Reinstalls crash handlers, to be used if a third party
* 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
* 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
*/
+ (void)setOptions:(NSDictionary*)options;
/**
* Track when a user has passed a checkpoint after the flight has taken off. Eg. passed level 1, posted high score
*
* @param checkpointName The name of the checkpoint, this should be a static string
*/
+ (void)passCheckpoint:(NSString *)checkpointName;
/**
* Opens a feedback window that is not attached to a checkpoint
*/
+ (void)openFeedbackView;
@end

BIN
External/TestFlightSDK/libTestFlight.a vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,94 @@
0.8.2 - December 20, 2011
Promoted 0.8.2 BETA 4 to stable
Known Issues:
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.
Tested compiled library with
Xcode 4.3
Xcode 4.2
Xcode 4.1
Xcode 3.2.6
0.8.2 BETA 4 - December 12, 2011
Prevented "The string argument is NULL" from occuring during finishedHandshake in rare cases
Resolved issue where background data may not be sent
0.8.2 BETA 3 - December 8, 2011
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
Fixed the "pointer being freed was not allocated" bug
0.8.1 - November 18, 2011
Implemented TFLog logging system, see README for more information
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 with Session End events not being sent from some iPod touch models
Tested compiled library with
Xcode 4.2
Xcode 4.1
Xcode 3.2.6
0.8 - November 8, 2011
Added SIGTRAP as a signal type that we catch
Removed all Objective-c from crash reporting
Removed the use of non signal safe functions from signal handling
Created a signal safe way to get symbols from a stack trace
Changed the keyboardType for Long Answer Questions and Feedback to allow for international character input
Changed TESTFLIGHT_SDK_VERSION string to be an NSString
Changed cache folder from Library/Caches/TestFlight to Library/Caches/com.testflight.testflightsdk
Fixed issue with saving data when device is offline
Fixed compability issues with iOS 3
Added calling into the rootViewController shouldAutorotateToInterfaceOrientation if a rootViewController is set
Made the comments in TestFlight.h compatible with Appledoc
Tested compiled library with
Xcode 4.2
Xcode 4.1
Xcode 3.2
0.7.2 - September 29, 2011
Changed TESTFLIGHT_SDK_VERSION string to be an NSString
Fixed an issue where exiting an application while the SDK is active caused modal views to be dismissed
0.7.1 - September 22, 2011
Internal release
Refactoring
0.7 - September 21, 2011
Moved TestFlight images and data to the Library/Caches folder
Resolved an issue where sometimes the rootViewController could not be found and feedback, questions and upgrade views would not be displayed
In application upgrade changed to allow skipping until the next version is installed and allows upgrades to be forced
Fixed a memory leak when launching questions
0.6 - September 2, 2011
Renamed base64_encode to testflight_base64_encode to remove a conflict with other third party libraries
Added ability to reinstall crash handlers when they are overwritten using the setOptions API
Fixed an issue where crash reports might not get sent under certain circumstances
Fixed a deadlock when the application is put in the background and then resumed before all information can be sent
Fixed an issue when attempting to un-install all signal handlers during a signal
Added support for landscape mode on the iPad to the Questions and Feedback views
Crash reporting now works in versions of Xcode earlier than 4.2
Fixed a memory leak during handshake
0.5 - August 19, 2011
Feedback that is not attached to a checkpoint [TestFlight openFeedbackView]
Usability changes to question views
Removed pause and resume sessions, replaced with sessions being stopped and started
Added text auto correction to the Long Answer question type
Crash reports now send on crash instead of next launch
0.4 - August 15, 2011
In Application Feedback with Questions
In application updates
Custom Environment Information added
Networking stack reimplementation
Exception handling fixes
0.3 - June 15, 2011
Removed all mention of JSONKit from the README
Added support for using both the Bundle Version and the Bundle Short Version string
0.2 - June 14, 2011
Removed all categories this allows users to use the SDK without having to set -ObjC and -load_all
Prefixed JSONKit for use in TestFlight to remove reported issues where some users were already using JSONKit
Added support for armv6 again
0.1 - June 11, 2011
Initial Version

View File

@ -15,7 +15,6 @@
DA34DA1114B1BC7E00F721C1 /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA34DA1014B1BC7E00F721C1 /* MPElementEntity.m */; };
DA41A40B14DB3BF100638533 /* guide_page_0.png in Resources */ = {isa = PBXBuildFile; fileRef = DA41A40914DB3BF100638533 /* guide_page_0.png */; };
DA41A40C14DB3BF100638533 /* guide_page_0@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA41A40A14DB3BF100638533 /* guide_page_0@2x.png */; };
DA55B29E14B38272001131B7 /* MPContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA55B29D14B38272001131B7 /* MPContentViewController.m */; };
DA55B2A214B4EB47001131B7 /* MPSearchDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA55B2A114B4EB46001131B7 /* MPSearchDelegate.m */; };
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA48147E415C00F98B1E /* UIKit.framework */; };
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
@ -244,6 +243,7 @@
DA95D5F814DF0B9F008D1B94 /* IASKSpecifierValuesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.xib */; };
DA95D5FD14DF295F008D1B94 /* MPConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA95D5F914DF295E008D1B94 /* MPConfig.m */; };
DA95D5FE14DF295F008D1B94 /* MPTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA95D5FB14DF295F008D1B94 /* MPTypes.m */; };
DA95D60614DF3E67008D1B94 /* libTestFlight.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA95D60114DF3E67008D1B94 /* libTestFlight.a */; };
DAA3B68E14CCCEE700F35AF6 /* icon_addressbook-person@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53814CCCEE700F35AF6 /* icon_addressbook-person@2x.png */; };
DAA3B68F14CCCEE700F35AF6 /* icon_addressbook.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53914CCCEE700F35AF6 /* icon_addressbook.png */; };
DAA3B69014CCCEE700F35AF6 /* icon_addressbook@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA3B53A14CCCEE700F35AF6 /* icon_addressbook@2x.png */; };
@ -666,7 +666,6 @@
DAC781311482AAD800BCF976 /* ValidatingTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DAC780EE1482AAD700BCF976 /* ValidatingTextField.m */; };
DAC781321482AAD800BCF976 /* WebViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC780EF1482AAD700BCF976 /* WebViewController.h */; };
DAC781331482AAD800BCF976 /* WebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAC780F01482AAD700BCF976 /* WebViewController.m */; };
DAC781361482E67300BCF976 /* MPRecentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAC781351482E67300BCF976 /* MPRecentViewController.m */; };
DADC3C4D14C62B350091CB4D /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DADC3C4C14C62B350091CB4D /* Settings.bundle */; };
DAE2C648148247E500BA6B10 /* MPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE2C646148247E500BA6B10 /* MPTypeViewController.m */; };
DAE998D214C1D2A0002D7C22 /* Content-Backdrop.png in Resources */ = {isa = PBXBuildFile; fileRef = DAE9987914C1D2A0002D7C22 /* Content-Backdrop.png */; };
@ -755,8 +754,6 @@
DA34DA1014B1BC7E00F721C1 /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = "<group>"; };
DA41A40914DB3BF100638533 /* guide_page_0.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = guide_page_0.png; sourceTree = "<group>"; };
DA41A40A14DB3BF100638533 /* guide_page_0@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "guide_page_0@2x.png"; sourceTree = "<group>"; };
DA55B29C14B38272001131B7 /* MPContentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPContentViewController.h; sourceTree = "<group>"; };
DA55B29D14B38272001131B7 /* MPContentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPContentViewController.m; sourceTree = "<group>"; };
DA55B2A014B4EB46001131B7 /* MPSearchDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSearchDelegate.h; sourceTree = "<group>"; };
DA55B2A114B4EB46001131B7 /* MPSearchDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSearchDelegate.m; sourceTree = "<group>"; };
DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -994,6 +991,8 @@
DA95D5FA14DF295E008D1B94 /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; };
DA95D5FB14DF295F008D1B94 /* MPTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTypes.m; sourceTree = "<group>"; };
DA95D5FC14DF295F008D1B94 /* MPTypeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypeViewController.h; sourceTree = "<group>"; };
DA95D60114DF3E67008D1B94 /* libTestFlight.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libTestFlight.a; sourceTree = "<group>"; };
DA95D60414DF3E67008D1B94 /* TestFlight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestFlight.h; sourceTree = "<group>"; };
DAA3B53814CCCEE700F35AF6 /* icon_addressbook-person@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook-person@2x.png"; sourceTree = "<group>"; };
DAA3B53914CCCEE700F35AF6 /* icon_addressbook.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_addressbook.png; sourceTree = "<group>"; };
DAA3B53A14CCCEE700F35AF6 /* icon_addressbook@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon_addressbook@2x.png"; sourceTree = "<group>"; };
@ -1415,8 +1414,6 @@
DAC780EE1482AAD700BCF976 /* ValidatingTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ValidatingTextField.m; sourceTree = "<group>"; };
DAC780EF1482AAD700BCF976 /* WebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewController.h; sourceTree = "<group>"; };
DAC780F01482AAD700BCF976 /* WebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewController.m; sourceTree = "<group>"; };
DAC781341482E67300BCF976 /* MPRecentViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRecentViewController.h; sourceTree = "<group>"; };
DAC781351482E67300BCF976 /* MPRecentViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRecentViewController.m; sourceTree = "<group>"; };
DADC3C4C14C62B350091CB4D /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
DAE2C646148247E500BA6B10 /* MPTypeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTypeViewController.m; sourceTree = "<group>"; };
DAE9987914C1D2A0002D7C22 /* Content-Backdrop.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Content-Backdrop.png"; path = "Resources/Content-Backdrop.png"; sourceTree = "<group>"; };
@ -1475,6 +1472,7 @@
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */,
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */,
DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */,
DA95D60614DF3E67008D1B94 /* libTestFlight.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1874,6 +1872,7 @@
DAC6325F1486805C0075AEA5 /* uicolor-utilities */,
DAC6326E148680650075AEA5 /* jrswizzle */,
DA95D59E14DF063C008D1B94 /* InAppSettingsKit */,
DA95D5FF14DF3E67008D1B94 /* TestFlightSDK */,
DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */,
);
@ -1918,12 +1917,8 @@
DA65570514D731F000841C99 /* MPGuideViewController.m */,
DA55B2A014B4EB46001131B7 /* MPSearchDelegate.h */,
DA55B2A114B4EB46001131B7 /* MPSearchDelegate.m */,
DAC781341482E67300BCF976 /* MPRecentViewController.h */,
DAC781351482E67300BCF976 /* MPRecentViewController.m */,
DA95D5FC14DF295F008D1B94 /* MPTypeViewController.h */,
DAE2C646148247E500BA6B10 /* MPTypeViewController.m */,
DA55B29C14B38272001131B7 /* MPContentViewController.h */,
DA55B29D14B38272001131B7 /* MPContentViewController.m */,
DA007F5014B24DCC00251337 /* MPConfig.h */,
DA95D5F914DF295E008D1B94 /* MPConfig.m */,
DA95D5FA14DF295E008D1B94 /* MPTypes.h */,
@ -2156,6 +2151,16 @@
path = External/InAppSettingsKit/InAppSettingsKit/Xibs;
sourceTree = SOURCE_ROOT;
};
DA95D5FF14DF3E67008D1B94 /* TestFlightSDK */ = {
isa = PBXGroup;
children = (
DA95D60114DF3E67008D1B94 /* libTestFlight.a */,
DA95D60414DF3E67008D1B94 /* TestFlight.h */,
);
name = TestFlightSDK;
path = External/TestFlightSDK;
sourceTree = "<group>";
};
DAC6325F1486805C0075AEA5 /* uicolor-utilities */ = {
isa = PBXGroup;
children = (
@ -3256,10 +3261,8 @@
DA5BFA64147E415C00F98B1E /* MasterPassword.xcdatamodeld in Sources */,
DA5BFA67147E415C00F98B1E /* MPMainViewController.m in Sources */,
DAE2C648148247E500BA6B10 /* MPTypeViewController.m in Sources */,
DAC781361482E67300BCF976 /* MPRecentViewController.m in Sources */,
DA34DA0D14B1BC7D00F721C1 /* MPElementStoredEntity.m in Sources */,
DA34DA1114B1BC7E00F721C1 /* MPElementEntity.m in Sources */,
DA55B29E14B38272001131B7 /* MPContentViewController.m in Sources */,
DA55B2A214B4EB47001131B7 /* MPSearchDelegate.m in Sources */,
DA0A848C14C4DFCB0090EA8E /* MPElementGeneratedEntity.m in Sources */,
DAA3B81414CDC77100F35AF6 /* jquery-1.6.1.min.js in Sources */,
@ -3413,6 +3416,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
SDKROOT = iphoneos;
STRIP_INSTALLED_PRODUCT = NO;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@ -3422,8 +3426,8 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
@ -3431,7 +3435,9 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
SDKROOT = iphoneos;
STRIP_INSTALLED_PRODUCT = NO;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@ -3445,6 +3451,10 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "MasterPassword/MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword/MasterPassword-Info.plist";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/External/TestFlightSDK\"",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
@ -3459,6 +3469,10 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "MasterPassword/MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword/MasterPassword-Info.plist";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/External/TestFlightSDK\"",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
@ -3485,6 +3499,92 @@
};
name = Release;
};
DA95D60914DF3F3B008D1B94 /* Production */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PREPROCESSOR_DEFINITIONS = "PRODUCTION=1";
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Production;
};
DA95D60A14DF3F3B008D1B94 /* Production */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword/MasterPassword.entitlements;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "MasterPassword/MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword/MasterPassword-Info.plist";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/External/TestFlightSDK\"",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Production;
};
DA95D60B14DF3F3B008D1B94 /* Production */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
DSTROOT = /tmp/Pearl.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Pearl/Pearl-Prefix.pch";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Production;
};
DA95D60C14DF3F3B008D1B94 /* Production */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
DSTROOT = /tmp/uicolor_utilities.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Production;
};
DA95D60D14DF3F3B008D1B94 /* Production */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_ARC = NO;
DSTROOT = /tmp/jrswizzle.dst;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Production;
};
DA95D60E14DF3F3B008D1B94 /* Production */ = {
isa = XCBuildConfiguration;
buildSettings = {
DSTROOT = /tmp/InAppSettingsKit.dst;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = InAppSettingsKit;
SKIP_INSTALL = YES;
};
name = Production;
};
DAC632661486805C0075AEA5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -3567,6 +3667,7 @@
buildConfigurations = (
DA5BFA6B147E415C00F98B1E /* Debug */,
DA5BFA6C147E415C00F98B1E /* Release */,
DA95D60914DF3F3B008D1B94 /* Production */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
@ -3576,6 +3677,7 @@
buildConfigurations = (
DA5BFA6E147E415C00F98B1E /* Debug */,
DA5BFA6F147E415C00F98B1E /* Release */,
DA95D60A14DF3F3B008D1B94 /* Production */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
@ -3585,6 +3687,7 @@
buildConfigurations = (
DA95D5A514DF063C008D1B94 /* Debug */,
DA95D5A614DF063C008D1B94 /* Release */,
DA95D60E14DF3F3B008D1B94 /* Production */,
);
defaultConfigurationIsVisible = 0;
};
@ -3593,6 +3696,7 @@
buildConfigurations = (
DAC632661486805C0075AEA5 /* Debug */,
DAC632671486805C0075AEA5 /* Release */,
DA95D60C14DF3F3B008D1B94 /* Production */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
@ -3602,6 +3706,7 @@
buildConfigurations = (
DAC63275148680650075AEA5 /* Debug */,
DAC63276148680650075AEA5 /* Release */,
DA95D60D14DF3F3B008D1B94 /* Production */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
@ -3611,6 +3716,7 @@
buildConfigurations = (
DAC77CB5148291A600BCF976 /* Debug */,
DAC77CB6148291A600BCF976 /* Release */,
DA95D60B14DF3F3B008D1B94 /* Production */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Production">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Production"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Production"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Production">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Production"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -66,6 +66,14 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#ifndef PRODUCTION
[TestFlight takeOff:@"bd44885deee7adce0645ce8e5498d80a_NDQ5NDQyMDExLTEyLTAyIDExOjM1OjQ4LjQ2NjM4NA"];
[TestFlight setOptions:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], @"logToConsole",
nil]];
[TestFlight passCheckpoint:MPTestFlightCheckpointLaunched];
#endif
UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsLandscapePhone];
@ -138,11 +146,19 @@
[self showGuide];
else
[self loadKeyPhrase];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointActivated];
#endif
}
- (void)showGuide {
[self.navigationController performSegueWithIdentifier:@"MP_Guide" sender:self];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointShowGuide];
#endif
}
- (void)loadKeyPhrase {
@ -172,7 +188,7 @@
@"Your old sites and passwords will then become available again."
viewStyle:UIAlertViewStyleDefault
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert firstOtherButtonIndex]) {
if (buttonIndex != [alert cancelButtonIndex]) {
// Key phrase reset. Delete it.
dbg(@"Deleting master key phrase and hash from key chain.");
[KeyChain deleteItemForQuery:[MPAppDelegate keyPhraseQuery]];
@ -180,6 +196,10 @@
}
[self loadKeyPhrase];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPChanged];
#endif
}
cancelTitle:[PearlStrings get].commonButtonAbort
otherTitles:[PearlStrings get].commonButtonContinue, nil];
@ -200,6 +220,9 @@
// Key phrase should not be stored in keychain. Delete it.
dbg(@"Deleting master key phrase from key chain.");
[KeyChain deleteItemForQuery:[MPAppDelegate keyPhraseQuery]];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored];
#endif
}
}
@ -236,6 +259,9 @@
if (![keyPhraseHash isEqual:answerHash]) {
dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, answerHash);
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
#endif
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
message:
@"Incorrect master password.\n\n"
@ -250,6 +276,10 @@
return;
}
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPAsked];
#endif
self.keyPhrase = answer;
} cancelTitle:@"Quit" otherTitles:@"Unlock", nil];
});
@ -261,11 +291,19 @@
if (![[MPConfig get].rememberKeyPhrase boolValue])
self.keyPhrase = nil;
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointDeactivated];
#endif
}
- (void)applicationWillTerminate:(UIApplication *)application {
[self saveContext];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointTerminated];
#endif
}
+ (MPAppDelegate *)get {
@ -314,6 +352,10 @@
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
nil]];
}
#ifndef PRODUCTION
[TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointSetKeyphraseLength, _keyPhrase.length]];
#endif
}
}
@ -419,6 +461,11 @@
wrn(@"Deleted datastore: %@", storeURL);
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
#endif
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointStoreIncompatible];
#endif
@throw [NSException exceptionWithName:error.domain reason:error.localizedDescription
userInfo:[NSDictionary dictionaryWithObject:error forKey:@"cause"]];
}

View File

@ -1,16 +0,0 @@
//
// MPContentViewController.h
// MasterPassword
//
// Created by Maarten Billemont on 03/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "MPElementEntity.h"
@interface MPContentViewController : UIViewController
@property (nonatomic, weak) MPElementEntity *activeElement;
@end

View File

@ -1,22 +0,0 @@
//
// MPContentViewController.m
// MasterPassword
//
// Created by Maarten Billemont on 03/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "MPContentViewController.h"
@implementation MPContentViewController
@synthesize activeElement = _activeElement;
#pragma mark - View lifecycle
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end

View File

@ -8,7 +8,6 @@
#import "MPMainViewController.h"
#import "MPAppDelegate.h"
#import "MPContentViewController.h"
#import "MPElementGeneratedEntity.h"
#import "MPElementStoredEntity.h"
#import "IASKAppSettingsViewController.h"
@ -57,8 +56,6 @@
if ([[segue identifier] isEqualToString:@"MP_Main_ChooseType"])
[[segue destinationViewController] setDelegate:self];
if ([[segue identifier] isEqualToString:@"MP_Main_Content"])
((MPContentViewController *)[segue destinationViewController]).activeElement = self.activeElement;
}
- (void)viewWillAppear:(BOOL)animated {
@ -144,13 +141,6 @@
[super viewDidUnload];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self updateAnimated:animated];
}
- (void)updateAnimated:(BOOL)animated {
[[MPAppDelegate get] saveContext];
@ -218,6 +208,10 @@
- (void)setHelpChapter:(NSString *)chapter {
#ifndef PRODUCTION
[TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointHelpChapter, chapter]];
#endif
[self.helpView loadRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:[NSString stringWithFormat:@"#%@", chapter] relativeToURL:
@ -276,6 +270,10 @@
forPasteboardType:(id)kUTTypeUTF8PlainText];
[self showContentTip:@"Copied!" withIcon:nil];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointCopyToPasteboard];
#endif
}
- (IBAction)incrementPasswordCounter {
@ -287,6 +285,10 @@
[self updateElement:^{
++((MPElementGeneratedEntity *) self.activeElement).counter;
}];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointIncrementPasswordCounter];
#endif
}
- (void)updateElement:(void (^)(void))updateElement {
@ -311,6 +313,10 @@
self.contentField.enabled = YES;
[self.contentField becomeFirstResponder];
}
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointEditPassword];
#endif
}
- (IBAction)closeAlert {
@ -320,6 +326,10 @@
} completion:^(BOOL finished) {
self.alertBody.text = nil;
}];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointCloseAlert];
#endif
}
- (IBAction)action:(id)sender {
@ -346,9 +356,23 @@
[self.navigationController pushViewController:settingsVC animated:YES];
break;
}
#ifndef PRODUCTION
case 4:
[TestFlight openFeedbackView];
break;
#endif
}
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointAction];
#endif
} cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil
otherTitles:[self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Settings", nil];
otherTitles:
[self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Settings",
#ifndef PRODUCTION
@"Feedback",
#endif
nil];
}
- (void)didSelectType:(MPElementType)type {
@ -371,6 +395,10 @@
self.activeElement.type = type;
#ifndef PRODUCTION
[TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointSelectType, NSStringFromMPElementType(type)]];
#endif
if (type & MPElementTypeClassStored && ![self.activeElement.description length])
[self showContentTip:@"Tap to set a password." withIcon:self.contentTipEditIcon];
}];
@ -384,11 +412,19 @@
[self.searchDisplayController setActive:NO animated:YES];
self.searchDisplayController.searchBar.text = self.activeElement.name;
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointSelectElement];
#endif
[self updateAnimated:YES];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointCancelSearch];
#endif
[self updateAnimated:YES];
}
@ -420,7 +456,12 @@
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointExternalLink];
#endif
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}

View File

@ -1,15 +0,0 @@
//
// MPRecentViewController.h
// MasterPassword
//
// Created by Maarten Billemont on 27/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MPRecentViewController : UITableViewController
@property(nonatomic,retain) IBOutlet UITableView *tableView;
@end

View File

@ -1,20 +0,0 @@
//
// MPRecentViewController.m
// MasterPassword
//
// Created by Maarten Billemont on 27/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPRecentViewController.h"
@implementation MPRecentViewController
@dynamic tableView;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
@end

View File

@ -252,6 +252,10 @@
[self.fetchedResultsController.managedObjectContext performBlock:^{
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.fetchedResultsController.managedObjectContext deleteObject:element];
#ifndef PRODUCTION
[TestFlight passCheckpoint:MPTestFlightCheckpointDeleteElement];
#endif
}];
}

View File

@ -32,6 +32,31 @@ typedef enum {
MPElementTypeStoredDevicePrivate = MPElementTypeClassStored | 0x02,
} MPElementType;
#ifndef PRODUCTION
#define MPTestFlightCheckpointAction @"MPTestFlightCheckpointAction"
#define MPTestFlightCheckpointHelpChapter @"MPTestFlightCheckpointHelpChapter_%@"
#define MPTestFlightCheckpointCopyToPasteboard @"MPTestFlightCheckpointCopyToPasteboard"
#define MPTestFlightCheckpointIncrementPasswordCounter @"MPTestFlightCheckpointIncrementPasswordCounter"
#define MPTestFlightCheckpointEditPassword @"MPTestFlightCheckpointEditPassword"
#define MPTestFlightCheckpointCloseAlert @"MPTestFlightCheckpointCloseAlert"
#define MPTestFlightCheckpointSelectType @"MPTestFlightCheckpointSelectType_%@"
#define MPTestFlightCheckpointSelectElement @"MPTestFlightCheckpointSelectElement"
#define MPTestFlightCheckpointDeleteElement @"MPTestFlightCheckpointDeleteElement"
#define MPTestFlightCheckpointCancelSearch @"MPTestFlightCheckpointCancelSearch"
#define MPTestFlightCheckpointExternalLink @"MPTestFlightCheckpointExternalLink"
#define MPTestFlightCheckpointLaunched @"MPTestFlightCheckpointLaunched"
#define MPTestFlightCheckpointActivated @"MPTestFlightCheckpointActivated"
#define MPTestFlightCheckpointDeactivated @"MPTestFlightCheckpointDeactivated"
#define MPTestFlightCheckpointTerminated @"MPTestFlightCheckpointTerminated"
#define MPTestFlightCheckpointShowGuide @"MPTestFlightCheckpointShowGuide"
#define MPTestFlightCheckpointMPChanged @"MPTestFlightCheckpointMPChanged"
#define MPTestFlightCheckpointMPUnstored @"MPTestFlightCheckpointMPUnstored"
#define MPTestFlightCheckpointMPMismatch @"MPTestFlightCheckpointMPMismatch"
#define MPTestFlightCheckpointMPAsked @"MPTestFlightCheckpointMPAsked"
#define MPTestFlightCheckpointStoreIncompatible @"MPTestFlightCheckpointStoreIncompatible"
#define MPTestFlightCheckpointSetKeyphraseLength @"MPTestFlightCheckpointSetKeyphraseLength_%d"
#endif
NSString *NSStringFromMPElementType(MPElementType type);
NSString *ClassNameFromMPElementType(MPElementType type);
Class ClassFromMPElementType(MPElementType type);

View File

@ -12,6 +12,10 @@
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#ifndef PRODUCTION
#import "TestFlight.h"
#endif
#endif
#define PEARL