2
0

Merge branch 'master' into ios7

Conflicts:
	MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj
This commit is contained in:
Maarten Billemont 2013-08-27 22:18:40 -04:00
commit b2a608824c
9 changed files with 314 additions and 170 deletions

View File

@ -13,6 +13,9 @@
#define CLIENT_VERSION @"2.18.0" #define CLIENT_VERSION @"2.18.0"
#define MARKETING_PLATFORM #define MARKETING_PLATFORM
// Forward declaration
@protocol LocalyticsSessionDelegate;
/*! /*!
@class LocalyticsSession @class LocalyticsSession
@discussion The class which manages creating, collecting, & uploading a Localytics session. @discussion The class which manages creating, collecting, & uploading a Localytics session.
@ -40,10 +43,6 @@
@author Localytics @author Localytics
*/ */
// Forward declaration
@protocol LocalyticsSessionDelegate;
@interface LocalyticsSession : NSObject @interface LocalyticsSession : NSObject
@ -65,7 +64,7 @@
/*! /*!
@property sessionTimeoutInterval @property sessionTimeoutInterval
@abstrac (Optional) If an App stays in the background for more than this many seconds, @abstract (Optional) If an App stays in the background for more than this many seconds,
start a new session when it returns to foreground. start a new session when it returns to foreground.
*/ */
@property (atomic) float sessionTimeoutInterval; @property (atomic) float sessionTimeoutInterval;
@ -98,7 +97,7 @@
upload methods. Best Practice is to call open & upload immediately after Localytics Session when loading an app, upload methods. Best Practice is to call open & upload immediately after Localytics Session when loading an app,
this method fascilitates that behavior. this method fascilitates that behavior.
It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>. It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
@param applicationKey The key unique for each application generated @param appKey The key unique for each application generated
at www.localytics.com at www.localytics.com
*/ */
- (void)startSession:(NSString *)appKey; - (void)startSession:(NSString *)appKey;
@ -161,18 +160,14 @@
@param event The name of the event which occurred. @param event The name of the event which occurred.
@param attributes (Optional) An object/hash/dictionary of key-value pairs, contains @param attributes (Optional) An object/hash/dictionary of key-value pairs, contains
contextual data specific to the event. contextual data specific to the event.
@param rerportAttributes (Optional) Additional attributes used for custom reporting. @param reportAttributes (Optional) Additional attributes used for custom reporting.
Available to Enterprise customers, please contact services for more details. Available to Enterprise customers, please contact services for more details.
@param customerValueIncrease (Optional) Numeric value, added to customer lifetime value. @param customerValueIncrease (Optional) Numeric value, added to customer lifetime value.
Integer expected. Try to use lowest possible unit, such as cents for US currency. Integer expected. Try to use lowest possible unit, such as cents for US currency.
*/ */
- (void)tagEvent:(NSString *)event;
- (void)tagEvent:(NSString *)event
attributes:(NSDictionary *)attributes;
- (void)tagEvent:(NSString *)event - (void)tagEvent:(NSString *)event
attributes:(NSDictionary *)attributes attributes:(NSDictionary *)attributes
reportAttributes:(NSDictionary *)reportAttributes
customerValueIncrease:(NSNumber *)customerValueIncrease; customerValueIncrease:(NSNumber *)customerValueIncrease;
- (void)tagEvent:(NSString *)event - (void)tagEvent:(NSString *)event
@ -181,9 +176,12 @@ reportAttributes:(NSDictionary *)reportAttributes;
- (void)tagEvent:(NSString *)event - (void)tagEvent:(NSString *)event
attributes:(NSDictionary *)attributes attributes:(NSDictionary *)attributes
reportAttributes:(NSDictionary *)reportAttributes
customerValueIncrease:(NSNumber *)customerValueIncrease; customerValueIncrease:(NSNumber *)customerValueIncrease;
- (void)tagEvent:(NSString *)event
attributes:(NSDictionary *)attributes;
- (void)tagEvent:(NSString *)event;
/*! /*!
@method tagScreen @method tagScreen
@ -205,7 +203,7 @@ customerValueIncrease:(NSNumber *)customerValueIncrease;
/*! /*!
@method LocalyticsSession @method LocalyticsSession
@abstract Initializes the Localytics Object. Not necessary if you choose to use startSession. @abstract Initializes the Localytics Object. Not necessary if you choose to use startSession.
@param applicationKey The key unique for each application generated at www.localytics.com @param appKey The key unique for each application generated at www.localytics.com
*/ */
- (void)LocalyticsSession:(NSString *)appKey; - (void)LocalyticsSession:(NSString *)appKey;

View File

@ -1,30 +1,26 @@
## Introduction ## Introduction
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. 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, and automatic recording of any crashes they encounter.
To get the most out of the SDK we have provided the Checkpoint API. The SDK can track more information if you pass it to TestFlight. 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? See "Checkpoint API" down below to see how.
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. The SDK also offers a remote logging solution. Find out more about our logging system in the "Remote Logging" section.
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. ## Requirements
For more detailed debugging we have a remote logging solution. Find out more about our logging system with TFLog in the Remote Logging section. The TestFlight SDK requires iOS 4.3 or above, the Apple LLVM compiler, and the libz library to run.
The AdSupport.framework is required for iOS 6.0+ in order to uniquely identify users so we can estimate the number of users your app has (using `ASIdentifierManager`). You may weak link the framework in you app. If your app does not link with the AdSupport.framework, the TestFlight SDK will automatically load it for apps running on iOS 6.0+.
##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.
## Integration ## Integration
1. Add the files to your project: File -&gt; Add Files to " " 1. Add the files to your project: File -> Add Files to " "
1. Find and select the folder that contains the SDK 1. Find and select the folder that contains the SDK
2. Make sure that "Copy items into destination folder (if needed)" is checked 2. Make sure that "Copy items into destination folder (if needed)" is checked
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 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
@ -32,6 +28,7 @@ This SDK can be run from both the iPhone Simulator and Device and has been teste
4. Open the Link Binary With Libraries Phase 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 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. Add libz to your Link Binary With Libraries Build Phase 3. Add libz to your Link Binary With Libraries Build Phase
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
@ -41,95 +38,158 @@ This SDK can be run from both the iPhone Simulator and Device and has been teste
6. Find libz.dylib in the list and add it 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 7. Repeat Steps 2 - 6 until all targets you want to use the SDK with have libz.dylib
4. In your Application Delegate: 4. Get your App Token
1. If this is a new application, and you have not uploaded it to TestFlight before, first register it here: [https://testflightapp.com/dashboard/applications/create/]().
Otherwise, if you have previously uploaded your app to TestFlight, go to your list of applications ([http://testflightapp.com/dashboard/applications/]()) and click on the application you are using from the list.
2. Click on the "App Token" tab on the left. The App Token for that application will be there.
5. In your Application Delegate:
1. Import TestFlight: `#import "TestFlight.h"` 1. Import TestFlight: `#import "TestFlight.h"`
***NOTE:*** Rather than importing `TestFlight.h` in every file you may add the above line into you pre-compiled header (`<projectname>_Prefix.pch`) file inside of the 2. Launch TestFlight with your App Token
In your `-application:didFinishLaunchingWithOptions:` method, call `+[TestFlight takeOff:]` with your App Token.
#ifdef __OBJC__
section. This will give you access to the SDK across all files.
2. Get your Application Token which you can find at [http://testflightapp.com/dashboard/applications/](http://testflightapp.com/dashboard/applications/) select the application you are using from the list choose the SDK option and the application token for this application will be there. To ensure that your testers do not show up as anonymous place the call to setDeviceIdentifer before calling takeOff. Remove #define TESTING 1 before building your release build for the App Store.
3. Launch TestFlight with your Application Token
-(BOOL)application:(UIApplication *)application -(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// start of your application:didFinishLaunchingWithOptions // start of your application:didFinishLaunchingWithOptions
// !!!: Use the next line only during beta
// [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
[TestFlight takeOff:@"Insert your Application Token here"]; [TestFlight takeOff:@"Insert your Application Token here"];
// The rest of your application:didFinishLaunchingWithOptions method // 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. 3. 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.
##Beta Testing and Release Differentiation ## Setting the UDID
In order to provide more information about your testers while beta testing you will need to provide the device's unique identifier. This identifier is not something that the SDK will collect from the device and we do not recommend using this in production. To send the device identifier to us put the following code **before your call to takeOff**. For **BETA** apps only: In order for "In App Updates" to work and for user data not to be anonymized, you may provide the device's unique identifier. To send the device identifier call the following method **before** your call to `+[TestFlight takeOff:]` like so:
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]]; [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
[TestFlight takeOff:@"Insert your Application Token here"]; [TestFlight takeOff:@"Insert your Application Token here"];
This will allow you to have the best possible information during testing. **When it is time to submit to the App Store comment this line out**. Apple may reject your app if you leave this line in. If you decide to not include the device's unique identifier during your testing phase TestFlight will still collect all of the information that you send but it may be anonymized. Note: `[[UIDevice currentDevice] uniqueIdentifier]` is deprecated, which means it may be removed from iOS in the future and that it should not be used in production apps. We recommend using it **only** in beta apps. If using it makes you feel uncomfortable, you are not required to include it.
**Note on iOS 7 and Xcode 5**: In iOS 7, `uniqueIdentifier` no longer returns the device's UDID, so iOS 7 users will show up anonymously on TestFlight. Also, when building with ARC, Xcode 5 will not allow you to call `uniqueIdentifier` because it has been removed in iOS 7 from `UIDevice`'s header. We are working on a workaround for this issue.
**DO NOT USE THIS IN PRODUCTION APPS**. When it is time to submit to the App Store comment this line out. Apple will probably reject your app if you leave this line in.
##Checkpoint API ## Uploading your build
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. After you have integrated the SDK into your application you need to upload your build to TestFlight. You can upload your build on our [website](https://testflightapp.com/dashboard/builds/add/), using our [desktop app](https://testflightapp.com/desktop/), or by using our [upload API](https://testflightapp.com/api/doc/).
`[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.
##Feedback API ## Basic Features
To launch unguided feedback call the openFeedbackView method. We recommend that you call this from a GUI element. ### Session Information
-(IBAction)launchFeedback { View anonymous information about how often users use your app, how long they use it for, and when they use it. You can see what type of device the user is using, which OS, which language, etc.
[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. Sessions automatically start at app launch, app did become active, and app will enter foreground and end at app will resign active, app did enter background, or app will terminate. Sessions that start shortly after an end continue the session instead of starting a new one.
For **beta** users, you can see who the users are if you are **setting the UDID**, they have a TestFlight account, and their device is registered to TestFlight. (See Setting the UDID for more information).
### Crash Reports
The TestFlight SDK automatically reports all crashes (beta and prod) to TestFlight's website where you can view them. Crash reports are sent **at** crash time. TestFlight will also automatically symbolicate all crashes (if you have uploaded your dSYM). For **beta** apps, on the site, you can see which checkpoints the user passed before the crash and see remote logs that were sent before the crash. For **prod** apps, you can see remote logs that were sent before the crash.
### Beta In App Updates
If a user is using a **beta** version of your app, you are **setting the UDID**, a new beta version is available, and that user has permission to install it; an in app popup will ask them if they would like to install the update. If they tap "Install", the new version is installed from inside the app.
NB: For this to work, you must increment your build version before uploading. Otherwise the new and old builds will have the same version number and we won't know if the user needs to update or is already using the new version.
To turn this off set this option before calling `takeOff:`
[TestFlight setOptions:@{ TFOptionDisableInAppUpdates : @YES }];
## Additional Features
### Checkpoints
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. Checkpoints are visible for all beta and prod builds.
[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.
Checkpoints are meant to tell you if a user visited a place in your app or completed a task. They should not be used for debugging purposes. Instead, use Remote Logging for debugging information (more information below).
### Custom Environment Information
In **beta** builds, if you want to see some extra information about your user, you can add some custom environment information. You must add this information before the session starts (a session starts at `takeOff:`) to see it on TestFlight's website. NB: You can only see this information for **beta** users.
[TestFlight addCustomEnvironmentInformation:@"info" forKey:@"key"];
You may call this method as many times as you would like to add more information.
### User Feedback
In **beta** builds, if you collect feedback from your users, you may pass it back to TestFlight which will associate it with the user's current session.
-(IBAction)submitFeedbackPressed:(id)sender {
NSString *feedback = [self getUserFeedback];
[TestFlight submitFeedback:feedback]; [TestFlight submitFeedback: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.
##Upload your build ### Remote Logging
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/](https://testflightapp.com/api/doc/) Remote Logging allows you to see the logs your app prints out remotely, on TestFlight's website. You can see logs for **beta sessions** and **prod sessions with crashes**. NB: you cannot see the logs for all prod sessions.
##Questions Interface To use it, simply replace all of your `NSLog` calls with `TFLog` calls. An easy way to do this without rewriting all your `NSLog` calls is to add the following macro to your `.pch` file.
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. #import "TestFlight.h"
#define NSLog TFLog
There are three question types available: Yes/No, Multiple Choice, and Long Answer. Not only will `TFLog` log remotely to TestFlight, it will also log to the console (viewable in a device's logs) and STDERR (shown while debugging) just like NSLog does, providing a complete replacement.
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 — 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 — who wants to do all that typing again)? For even better information in your remote logs, such as file name and line number, you can use this macro instead:
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. #define NSLog(__FORMAT__, ...) TFLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
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. Which will produce output that looks like
##View the results -[MyAppDelegate application:didFinishLaunchingWithOptions:] [Line 45] Launched!
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. **Custom Logging**
##Advanced Exception Handling If you have your own custom logging, call `TFLog` from your custom logging function. If you do not need `TFLog` to log to the console or STDERR because you handle those yourself, you can turn them off with these calls:
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: [TestFlight setOptions:@{ TFOptionLogToConsole : @NO }];
[TestFlight setOptions:@{ TFOptionLogToSTDERR : @NO }];
## Advanced Notes
### Checkpoint API
When passing a checkpoint, TestFlight logs the checkpoint synchronously (See Remote Logging for more information). If your app has very high performance needs, you can turn the logging off with the `TFOptionLogOnCheckpoint` option.
### Remote Logging
All logging is done synchronously. Every time the SDK logs, it must write data to a file. This is to ensure log integrity at crash time. Without this, we could not trust logs at crash time. If you have a high performance app, please email support@testflightapp.com for more options.
### Advanced Session Control
Continuing sessions: You can adjust the amount of time a user can leave the app for and still continue the same session when they come back by changing the `TFOptionSessionKeepAliveTimeout` option. Change it to 0 to turn the feature off.
Manual Session Control: If your app is a music player that continues to play music in the background, a navigation app that continues to function in the background, or any app where a user is considered to be "using" the app even while the app is not active you should use Manual Session Control. Please only use manual session control if you know exactly what you are doing. There are many pitfalls which can result in bad session duration and counts. See `TestFlight+ManualSessions.h` for more information and instructions.
### Advanced Exception/Signal 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 My Apps Custom uncaught exception catcher, we do special stuff here, and TestFlight takes care of the rest
@ -167,55 +227,3 @@ An uncaught exception means that your application is in an unknown state and the
You do not need to add the above code if your application does not use exception handling already. You do not need to add the above code if your application does not use exception handling already.
##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:{ TFOptionLogToConsole : @NO }];
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. If you do not wish to use the STDERR logger you can disable it by turning it off in your TestFlight options
[TestFlight setOptions:{ TFOptionLogToSTDERR : @NO }];
The default option is YES.
## Advanced Remote Logging
For most users we expect using TFLog to provide all of the logging functionality that they need. For the occasion where you need to provide a wrapper around TFLog we provide
void TFLogv(NSString *format, va_list arg_list);
Using TFLogv you can have your method that accepts a variable number of arguments that then passes that format and argument list to TFLog.
##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:
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 libSystem.dylib in the list and add it
7. To the right of libSystem.dylib in the Link Binary With Libraries pane change "Required" to "Optional"

View File

@ -0,0 +1,30 @@
//
// TestFlight+AsyncLogging.h
// libTestFlight
//
// Created by Jason Gregori on 2/12/13.
// Copyright (c) 2013 TestFlight. All rights reserved.
//
/*
When logging, it is important that logs are written synchronously. In the event of a crash, all logs that happened before the crash are gauranteed to be on disk. If they were written asynchronously and a crash occurs, you might lose some very valuable logs that might have helped fixed the crash.
However, because TFLog waits until writing to disk is complete, it takes a while. If you have a very high preformance app that can't afford to wait for logs, these functions are for you.
USE THESE, BUT KNOW YOU RISK LOSING SOME LOGS AT CRASH TIME
*/
#import "TestFlight.h"
#if __cplusplus
extern "C" {
#endif
void TFLog_async(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
void TFLogv_async(NSString *format, va_list arg_list);
#if __cplusplus
}
#endif

View File

@ -0,0 +1,58 @@
//
// TestFlight+ManualSessions.h
// libTestFlight
//
// Created by Jason Gregori on 5/16/13.
// Copyright (c) 2013 TestFlight. All rights reserved.
//
/*
YOU ARE STRONGLY ADVISED NOT TO USE THESE METHODS unless you know exactly what you are doing. By using these you take on the responsibility of ensuring your session data is reported accurately.
The way TestFlight normally does sessions is to automatically start them at app launch, app did become active, and app will enter foreground and end them at app will resign active, app did enter background, or app will terminate.
If your app is a music player that continues to play music in the background, a navigation app that continues to function in the background, or any app where a user is considered to be "using" the app even while the app is not active, this file is for you.
Usage
-----
1. Add this file to your project.
2. Set the manual sessions option to true **before** calling `takeOff:`
[TestFlight setOptions:@{ TFOptionManualSessions : @YES }];
3. Use the manually start/end session methods to control you sessions.
Pitfalls
--------
When using manual sessions in the background, you must always be aware of the fact that iOS may suspend your app at any time without any warning. You must end your session before that happens. If you do not, the session will continue and include all the time the app was suspended in it's duration if the app is brought back from suspension. This will lead to very inaccurate session lengths and counts.
On app termination: For the most accurate sessions, try to end your session if you know the app is about to terminate. If you do not, the session will still be ended on the next launch, however, it's end time will not be exact. In that case, the end time will be within 30 seconds of the correct time (session information is saved every 30 seconds and when a checkpoint is sent).
Sessions do not continue across termination if you do not end a session before termination.
On crashes: Do not worry about ending sessions in the event of a crash. Even manual sessions are automatically ended in the event of a crash.
Continuing sessions: If a session is started without 30 seconds of the last session ending (and their was no termination between the sessions), the last session will continue instead of a new session starting. This is the case in manual and automatic sessions. You may change the timeout or turn this feature off using the `TFOptionSessionKeepAliveTimeout` option.
*/
#import "TestFlight.h"
extern NSString *const TFOptionManualSessions; // Defaults to @NO. Set to @YES before calling `takeOff:` in order to use manual session methods.
@interface TestFlight (ManualSessions)
// these methods are thread safe
+ (void)manuallyStartSession;
+ (void)manuallyEndSession;
@end

View File

@ -6,14 +6,19 @@
// Copyright 2011 TestFlight. All rights reserved. // Copyright 2011 TestFlight. All rights reserved.
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#define TESTFLIGHT_SDK_VERSION @"1.2.4" #define TESTFLIGHT_SDK_VERSION @"2.0.0"
#undef TFLog #undef TFLog
#if __cplusplus #if __cplusplus
extern "C" { extern "C" {
#endif #endif
void TFLog(NSString *format, ...); /*
* Remote Logging
* Note: All Logging is synchronous, see the README for more information.
*/
void TFLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
void TFLogv(NSString *format, va_list arg_list); void TFLogv(NSString *format, va_list arg_list);
void TFLogPreFormatted(NSString *message);
#if __cplusplus #if __cplusplus
} }
#endif #endif
@ -22,13 +27,12 @@ extern "C" {
* TestFlight object * TestFlight object
* All methods are class level * All methods are class level
*/ */
@interface TestFlight : NSObject { @interface TestFlight : NSObject
}
/** /**
* Add custom environment information * Add custom environment information
* If you want to track custom information such as a user name from your application you can add it here * If you want to track custom information such as a user name from your application you can add it here.
* NB: This information must be added before the session starts, it is recorded only on session start.
* *
* @param information A string containing the environment you are storing * @param information A string containing the environment you are storing
* @param key The key to store the information with * @param key The key to store the information with
@ -43,7 +47,6 @@ extern "C" {
* The token for this application can be retrieved by going to https://testflightapp.com/dashboard/applications/ * The token for this application can be retrieved by going to https://testflightapp.com/dashboard/applications/
* selecting this application from the list then selecting SDK. * selecting this application from the list then selecting SDK.
*/ */
+ (void)takeOff:(NSString *)applicationToken; + (void)takeOff:(NSString *)applicationToken;
/** /**
@ -55,17 +58,14 @@ extern "C" {
+ (void)setOptions:(NSDictionary*)options; + (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 * Track when a user has passed a checkpoint after the flight has taken off. Eg. passed level 1, posted high score.
* Checkpoints are sent in the background.
* Note: The checkpoint is logged synchronously (See TFLog and TFOptionLogOnCheckpoint for more information).
* *
* @param checkpointName The name of the checkpoint, this should be a static string * @param checkpointName The name of the checkpoint, this should be a static string
*/ */
+ (void)passCheckpoint:(NSString *)checkpointName; + (void)passCheckpoint:(NSString *)checkpointName;
/**
* Opens a feedback window that is not attached to a checkpoint
*/
+ (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 * 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. * feedback from custom feedback forms.
@ -105,10 +105,13 @@ extern "C" {
* Pass these as keys to the dictionary you pass to +`[TestFlight setOptions:]`. * Pass these as keys to the dictionary you pass to +`[TestFlight setOptions:]`.
* The values should be NSNumber BOOLs (`[NSNumber numberWithBool:YES]` or `@YES`) * The values should be NSNumber BOOLs (`[NSNumber numberWithBool:YES]` or `@YES`)
*/ */
extern NSString *const TFOptionAttachBacktraceToFeedback; // Defaults to @NO. Setting to @YES attaches the current backtrace, with symbols, to the feedback.
extern NSString *const TFOptionDisableInAppUpdates; // Defaults to @NO. Setting to @YES, disables the in app update screen shown in BETA apps when there is a new version available on TestFlight. extern NSString *const TFOptionDisableInAppUpdates; // Defaults to @NO. Setting to @YES, disables the in app update screen shown in BETA apps when there is a new version available on TestFlight.
extern NSString *const TFOptionFlushSecondsInterval; // Defaults to @60. Set to a number. @0 turns off the flush timer. 30 seconds is the minimum flush interval.
extern NSString *const TFOptionLogOnCheckpoint; // Defaults to @YES. Because logging is synchronous, if you have a high preformance app, you might want to turn this off.
extern NSString *const TFOptionLogToConsole; // Defaults to @YES. Prints remote logs to Apple System Log. extern NSString *const TFOptionLogToConsole; // Defaults to @YES. Prints remote logs to Apple System Log.
extern NSString *const TFOptionLogToSTDERR; // Defaults to @YES. Sends remote logs to STDERR when debugger is attached. extern NSString *const TFOptionLogToSTDERR; // Defaults to @YES. Sends remote logs to STDERR when debugger is attached.
extern NSString *const TFOptionReinstallCrashHandlers; // If set to @YES: Reinstalls crash handlers, to be used if a third party library installs crash handlers overtop of the TestFlight Crash Handlers. extern NSString *const TFOptionReinstallCrashHandlers; // If set to @YES: Reinstalls crash handlers, to be used if a third party library installs crash handlers overtop of the TestFlight Crash Handlers.
extern NSString *const TFOptionReportCrashes; // Defaults to @YES. If set to @NO, crash handlers are never installed. Must be set **before** calling `takeOff:`.
extern NSString *const TFOptionSendLogOnlyOnCrash; // Defaults to @NO. Setting to @YES stops remote logs from being sent when sessions end. They would only be sent in the event of a crash. extern NSString *const TFOptionSendLogOnlyOnCrash; // Defaults to @NO. Setting to @YES stops remote logs from being sent when sessions end. They would only be sent in the event of a crash.
extern NSString *const TFOptionSessionKeepAliveTimeout; // Defaults to @30. This is the amount of time a user can leave the app for and still continue the same session when they come back. If they are away from the app for longer, a new session is created when they come back. Must be a number. Change to @0 to turn off.

Binary file not shown.

View File

@ -1,3 +1,37 @@
## 2.0
Improvements
- ARC
- All public TestFlight methods may be called from any thread or dispatch_queue
- All public TestFlight methods (except for `TFLog` and `takeOff:`) are asynchronous, so there is never a wait on them
- TestFlight never uses more than 1 network connection at a time
- All network traffic is grouped together, sent at once, and transferred in MessagePack. This results in using less bandwidth and less network calls.
- All network traffic if server is not reachable
- Size of SDK reduced by 70%
- New In App Update UI in an alert with landscape support. Should work for all different types of apps.
- Manual Sessions: You can manually control session start and end. See `TestFlight+ManualSessions.h` for more information
- Combining of back to back sessions. If a session starts less than 30 seconds from the last session which ended, the previous session is continued. You may change the time limit (or turn this off) using the `TFOptionSessionKeepAliveTimeout` option key.
- No longer automatically starts a session on `+takeOff:` in order to support new background modes that might launch an app in the background.
- `TFOptionReportCrashes` option to not install crash handlers
- Remove all calls to `dispatch_get_current_queue`, it is deprecated
Changes
- Removed all access to mac address
- Added AdSupport.framework requirement (as a replacement for mac address to get accurate user counts)
- Add format attribute to TFLog to show warnings for wrong format specifiers or not using a format string
- Removed Questions
- Removed Feedback View (along with backtrace option)
Bug Fixes
- Fixed addrinfo memory leak
- Fixed possible `-[TFAirTrafficController getNumberOrNilFrom:withKey:]` crash when bad data is received.
- CoreTelephony crash work around: this is a workaround of a iOS bug that causes deallocated instances of `CTTelephonyNetworkInfo` to receive notifications which causes crashes. Core Telephony is used to retrieve the device's mobile carrier.
- Fix bug with crash reporting in iOS 7
## 1.2.4 - February 19, 2013 ## 1.2.4 - February 19, 2013
- Fixed bug that caused crash reports to sometimes not send immediately (they would be resent later) - Fixed bug that caused crash reports to sometimes not send immediately (they would be resent later)
@ -7,7 +41,7 @@
- Fixed typos in readme - Fixed typos in readme
- Fixed bug where logs not sent on crash - Fixed bug where logs not sent on crash
- Fixed bug where empty crash files were created (but not sent) - Fixed bug where empty crash files were created (but not sent)
- Cache cache path - Cache path to TF's directory so it does not need to be regenerated every time
- Use consts for `setOptions:` - Use consts for `setOptions:`
- Updated `setDeviceIdentifier:` comments to make them clearer - Updated `setDeviceIdentifier:` comments to make them clearer
- Remove potentially conflicting function name `UIColorFromRGB` - Remove potentially conflicting function name `UIColorFromRGB`

View File

@ -41,11 +41,6 @@
NSString *testFlightToken = [self testFlightToken]; NSString *testFlightToken = [self testFlightToken];
if ([testFlightToken length]) { if ([testFlightToken length]) {
inf(@"Initializing TestFlight"); inf(@"Initializing TestFlight");
#ifdef ADHOC
[TestFlight setDeviceIdentifier:[(id)[UIDevice currentDevice] uniqueIdentifier]];
#else
[TestFlight setDeviceIdentifier:[PearlKeyChain deviceIdentifier]];
#endif
[TestFlight addCustomEnvironmentInformation:@"Anonymous" forKey:@"username"]; [TestFlight addCustomEnvironmentInformation:@"Anonymous" forKey:@"username"];
[TestFlight addCustomEnvironmentInformation:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"]; [TestFlight addCustomEnvironmentInformation:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
[TestFlight setOptions:@{ [TestFlight setOptions:@{

View File

@ -408,6 +408,8 @@
DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; }; DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; };
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; }; DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; }; DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
DA340E9E17CD830E00712B77 /* TestFlight+AsyncLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestFlight+AsyncLogging.h"; sourceTree = "<group>"; };
DA340E9F17CD830E00712B77 /* TestFlight+ManualSessions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestFlight+ManualSessions.h"; sourceTree = "<group>"; };
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; }; DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = "<group>"; };
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; }; DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = "<group>"; };
DA3EF17915A47744003ABF4E /* Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; DA3EF17915A47744003ABF4E /* Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
@ -2353,6 +2355,8 @@
DACA22BF1705DE9D002C6C22 /* TestFlight */ = { DACA22BF1705DE9D002C6C22 /* TestFlight */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA340E9E17CD830E00712B77 /* TestFlight+AsyncLogging.h */,
DA340E9F17CD830E00712B77 /* TestFlight+ManualSessions.h */,
DACA22C11705DE9D002C6C22 /* TestFlight.h */, DACA22C11705DE9D002C6C22 /* TestFlight.h */,
DACA22C31705DE9D002C6C22 /* libTestFlight.a */, DACA22C31705DE9D002C6C22 /* libTestFlight.a */,
); );
@ -3272,7 +3276,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/bash -e"; shellPath = "/bin/bash -e";
shellScript = "PATH+=:/usr/libexec\n\naddPlistWithKey() {\n local key=$1 type=$2 value=$3 plist=${4:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Delete :'$key'\" \"$plist\" 2>/dev/null || true\n PlistBuddy -c \"Add :'$key' '$type' '$value'\" \"$plist\"\n}\nsetPlistWithKey() {\n local key=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Set :'$key' '$value'\" \"$plist\"\n}\ngetPlistWithKey() {\n local key=$1 plist=${2:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Print :'$key'\" \"$plist\"\n}\nsetSettingWithTitle() {\n local i title=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Settings.bundle/Root.plist\"}\n \n for (( i=0; 1; ++i )); do\n PlistBuddy -c \"Print :PreferenceSpecifiers:$i\" \"$plist\" &>/dev/null || break\n echo \"Checking preference specifier $i\"\n \n [[ $(PlistBuddy -c \"Print :PreferenceSpecifiers:$i:Title\" \"$plist\" 2>/dev/null) = $title ]] || continue\n \n echo \"Correct title, setting value.\"\n PlistBuddy -c \"Set :PreferenceSpecifiers:$i:DefaultValue $value\" \"$plist\"\n break\n done\n}\n\ndescription=$(git describe --always --dirty --long)\ncommit=${description##*-g} commit=$((0x$commit))\nversion=${description%-g*} version=${version//-/.}\n\naddPlistWithKey GITDescription string \"$description\"\nsetPlistWithKey CFBundleVersion \"${version//.}$commit\" # No separator between version and commit because I had already submitted a CFBundleVersion with a really high major. Cry.\nsetPlistWithKey CFBundleShortVersionString \"$version\"\n\nsetSettingWithTitle \"Build\" \"$commit\"\nsetSettingWithTitle \"Version\" \"$version\"\nsetSettingWithTitle \"Copyright\" \"$(getPlistWithKey NSHumanReadableCopyright)\"\n\nif [[ $DEPLOYMENT_LOCATION = YES ]]; then\n # This build is a release. Do some release checks.\n [[ $description = *-dirty ]] && \\\n { echo >&2 \"ERROR: Cannot release a dirty version, first commit any changes.\"; exit 1; }\n [[ ! $(PlistBuddy -c \"Print :'API Key'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Crashlytics.plist\") ]] && \\\n { echo >&2 \"ERROR: Cannot release: Crashlytics API key is missing.\"; exit 1; }\n [[ ! $(PlistBuddy -c \"Print :'ClientID'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Google+.plist\") ]] && \\\n { echo >&2 \"ERROR: Cannot release: Google+ ClientID is missing.\"; exit 1; }\n [[ ! $(PlistBuddy -c \"Print :'Key.distribution'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Localytics.plist\") ]] && \\\n { echo >&2 \"ERROR: Cannot release: Localytics distribution key is missing.\"; exit 1; }\nfi"; shellScript = "PATH+=:/usr/libexec\n\naddPlistWithKey() {\n local key=$1 type=$2 value=$3 plist=${4:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Delete :'$key'\" \"$plist\" 2>/dev/null || true\n PlistBuddy -c \"Add :'$key' '$type' '$value'\" \"$plist\"\n}\nsetPlistWithKey() {\n local key=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Set :'$key' '$value'\" \"$plist\"\n}\ngetPlistWithKey() {\n local key=$1 plist=${2:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Print :'$key'\" \"$plist\"\n}\nsetSettingWithTitle() {\n local i title=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Settings.bundle/Root.plist\"}\n \n for (( i=0; 1; ++i )); do\n PlistBuddy -c \"Print :PreferenceSpecifiers:$i\" \"$plist\" &>/dev/null || break\n echo \"Checking preference specifier $i\"\n \n [[ $(PlistBuddy -c \"Print :PreferenceSpecifiers:$i:Title\" \"$plist\" 2>/dev/null) = $title ]] || continue\n \n echo \"Correct title, setting value.\"\n PlistBuddy -c \"Set :PreferenceSpecifiers:$i:DefaultValue $value\" \"$plist\"\n break\n done\n}\n\ndescription=$(git describe --always --dirty --long)\ncommit=${description##*-g} commit=$((0x$commit))\nversion=${description%-g*} version=${version//-/.}\n\naddPlistWithKey GITDescription string \"$description\"\nsetPlistWithKey CFBundleVersion \"${version//.}$commit\" # No separator between version and commit because I had already submitted a CFBundleVersion with a really high major. Cry.\nsetPlistWithKey CFBundleShortVersionString \"$version\"\n\nsetSettingWithTitle \"Build\" \"$commit\"\nsetSettingWithTitle \"Version\" \"$version\"\nsetSettingWithTitle \"Copyright\" \"$(getPlistWithKey NSHumanReadableCopyright)\"\n\nif [[ $DEPLOYMENT_LOCATION = YES ]]; then\n # This build is a release. Do some release checks.\n passed=1\n [[ $description != *-dirty ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release a dirty version, first commit any changes.\"; }\n [[ $(PlistBuddy -c \"Print :'API Key'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Crashlytics.plist\") ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release: Crashlytics API key is missing.\"; }\n [[ $(PlistBuddy -c \"Print :'ClientID'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Google+.plist\") ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release: Google+ ClientID is missing.\"; }\n [[ $(PlistBuddy -c \"Print :'Key.distribution'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Localytics.plist\") ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release: Localytics distribution key is missing.\"; }\n (( passed )) || \\\n { echo >&2 \"Failed to pass release checks. Fix the above errors and re-try. Aborting.\"; exit 1; }\nfi";
}; };
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = { DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
@ -3591,16 +3595,21 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_WARN_CXX0X_EXTENSIONS = YES; CLANG_WARN_CXX0X_EXTENSIONS = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Maarten Billemont (DWGU95U4ZD)"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherit)", "$(inherit)",
@ -3617,9 +3626,9 @@
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
@ -3649,7 +3658,6 @@
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "${TARGET_NAME}"; PRODUCT_NAME = "${TARGET_NAME}";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "C1EC2842-EF33-4340-AC88-8A12B106413D";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -3660,11 +3668,17 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_CXX0X_EXTENSIONS = YES; CLANG_WARN_CXX0X_EXTENSIONS = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -3689,9 +3703,9 @@
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
@ -3720,7 +3734,7 @@
); );
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "${TARGET_NAME}"; PRODUCT_NAME = "${TARGET_NAME}";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -3739,6 +3753,7 @@
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "EBD2A2E4-AEC3-4EEA-8FF0-5F1A0D9FFA1C";
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = 1;
}; };
@ -3749,13 +3764,12 @@
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
EXCLUDED_SOURCE_FILE_NAMES = ""; EXCLUDED_SOURCE_FILE_NAMES = "";
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "20B988BD-7A10-49E6-9FF7-367BD5714A31"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = "E0204B5E-CCA7-447B-828F-53912F920A0C";
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = 1;
}; };
@ -3786,11 +3800,17 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_CXX0X_EXTENSIONS = YES; CLANG_WARN_CXX0X_EXTENSIONS = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
CLANG_WARN_OBJC_RECEIVER_WEAK = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = NO;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -3814,9 +3834,9 @@
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
@ -3845,7 +3865,7 @@
); );
OTHER_LDFLAGS = "-ObjC"; OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "${TARGET_NAME}"; PRODUCT_NAME = "${TARGET_NAME}";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -3858,13 +3878,11 @@
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = MasterPassword.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
EXCLUDED_SOURCE_FILE_NAMES = libTestFlight.a; EXCLUDED_SOURCE_FILE_NAMES = libTestFlight.a;
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist"; INFOPLIST_FILE = "MasterPassword-Info.plist";
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "3EF104A5-610A-429E-ACF0-232A2C8A7E1C"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = "6C6B84DD-9D8F-4321-BD77-5F737DBE1778";
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = 1;
}; };