2
0

Tag screens in Localytics + email fix + psc fix

[FIXED]     Sending email with no recipient caused a crash.
[FIXED]     Duplicate persistence coordinator.
[UPDATED]   Crashlytics and Localytics SDKs.
[UPDATED]   Don't continue the Localytics session when device locked.
[UPDATED]   Put Localytics communication on HTTPS for confidentiality.
[ADDED]     Tagging screens in Localytics.
This commit is contained in:
Maarten Billemont 2012-09-12 15:58:54 +02:00
parent 6f82cf7f15
commit bce6b96417
22 changed files with 2807 additions and 2509 deletions

View File

@ -148,6 +148,26 @@ OBJC_EXTERN void CLSNSLog(NSString *format, ...);
@end @end
/**
* The CLSCrashReport protocol exposes methods that you can call on crash report objects passed
* to delegate methods. If you want these values or the entire object to stay in memory retain
* them or copy them.
**/
@protocol CLSCrashReport <NSObject>
@optional
/**
* Returns the session identifier for the crash report.
**/
- (NSString *)identifier;
/**
* Returns the custom key value data for the crash report.
**/
- (NSDictionary *)customKeys;
@end
/** /**
* *
* The CrashlyticsDelegate protocol provides a mechanism for your application to take * The CrashlyticsDelegate protocol provides a mechanism for your application to take
@ -169,4 +189,15 @@ OBJC_EXTERN void CLSNSLog(NSString *format, ...);
**/ **/
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics; - (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics;
/**
*
* Just like crashlyticsDidDetectCrashDuringPreviousExecution this delegate method is
* called once a Crashlytics instance has determined that the last execution of the
* application ended in a crash. A CLSCrashReport is passed back that contains data about
* the last crash report that was generated. See the CLSCrashReport protocol for method details.
* This method is called after crashlyticsDidDetectCrashDuringPreviousExecution.
*
**/
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
@end @end

Binary file not shown.

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit 4ceb992dc59e9d085b241239b63df4754da90de7 Subproject commit b8cf787af604299c5363846c5b2fcbd9e3083253

View File

@ -1,57 +1,70 @@
// //
// LocalyticsDatabase.h // LocalyticsDatabase.h
// LocalyticsDemo // Copyright (C) 2012 Char Software Inc., DBA Localytics
// //
// Created by jkaufman on 5/26/11. // This code is provided under the Localytics Modified BSD License.
// Copyright 2011 Localytics. All rights reserved. // A copy of this license has been distributed in a file called LICENSE
// // with this source code.
//
#import <Foundation/Foundation.h> // Please visit www.localytics.com for more information.
#import <sqlite3.h>
#import <Foundation/Foundation.h>
#define MAX_DATABASE_SIZE 500000 // The maximum allowed disk size of the primary database file at open, in bytes #import <sqlite3.h>
#define VACUUM_THRESHOLD 0.8 // The database is vacuumed after its size exceeds this proportion of the maximum.
#define MAX_DATABASE_SIZE 500000 // The maximum allowed disk size of the primary database file at open, in bytes
@interface LocalyticsDatabase : NSObject { #define VACUUM_THRESHOLD 0.8 // The database is vacuumed after its size exceeds this proportion of the maximum.
sqlite3 *_databaseConnection;
} @interface LocalyticsDatabase : NSObject {
sqlite3 *_databaseConnection;
+ (LocalyticsDatabase *)sharedLocalyticsDatabase; }
- (NSUInteger)databaseSize; + (LocalyticsDatabase *)sharedLocalyticsDatabase;
- (int)eventCount;
- (NSTimeInterval)createdTimestamp; - (NSUInteger)databaseSize;
- (int)eventCount;
- (BOOL)beginTransaction:(NSString *)name; - (NSTimeInterval)createdTimestamp;
- (BOOL)releaseTransaction:(NSString *)name;
- (BOOL)rollbackTransaction:(NSString *)name; - (BOOL)beginTransaction:(NSString *)name;
- (BOOL)releaseTransaction:(NSString *)name;
- (BOOL)incrementLastUploadNumber:(int *)uploadNumber; - (BOOL)rollbackTransaction:(NSString *)name;
- (BOOL)incrementLastSessionNumber:(int *)sessionNumber;
- (BOOL)incrementLastUploadNumber:(int *)uploadNumber;
- (BOOL)addEventWithBlobString:(NSString *)blob; - (BOOL)incrementLastSessionNumber:(int *)sessionNumber;
- (BOOL)addCloseEventWithBlobString:(NSString *)blob;
- (BOOL)addFlowEventWithBlobString:(NSString *)blob; - (BOOL)addEventWithBlobString:(NSString *)blob;
- (BOOL)removeLastCloseAndFlowEvents; - (BOOL)addCloseEventWithBlobString:(NSString *)blob;
- (BOOL)queueCloseEventWithBlobString:(NSString *)blob;
- (BOOL)addHeaderWithSequenceNumber:(int)number blobString:(NSString *)blob rowId:(sqlite3_int64 *)insertedRowId; - (NSString *)dequeueCloseEventBlobString;
- (int)unstagedEventCount; - (BOOL)addFlowEventWithBlobString:(NSString *)blob;
- (BOOL)stageEventsForUpload:(sqlite3_int64)headerId; - (BOOL)removeLastCloseAndFlowEvents;
- (BOOL)updateAppKey:(NSString *)appKey;
- (NSString *)uploadBlobString; - (BOOL)addHeaderWithSequenceNumber:(int)number blobString:(NSString *)blob rowId:(sqlite3_int64 *)insertedRowId;
- (BOOL)deleteUploadedData; - (int)unstagedEventCount;
- (BOOL)resetAnalyticsData; - (BOOL)stageEventsForUpload:(sqlite3_int64)headerId;
- (BOOL)vacuumIfRequired; - (BOOL)updateAppKey:(NSString *)appKey;
- (NSString *)uploadBlobString;
- (NSTimeInterval)lastSessionStartTimestamp; - (BOOL)deleteUploadedData;
- (BOOL)setLastsessionStartTimestamp:(NSTimeInterval)timestamp; - (BOOL)resetAnalyticsData;
- (BOOL)vacuumIfRequired;
- (BOOL)isOptedOut;
- (BOOL)setOptedOut:(BOOL)optOut; - (NSTimeInterval)lastSessionStartTimestamp;
- (NSString *)installId; - (BOOL)setLastsessionStartTimestamp:(NSTimeInterval)timestamp;
- (NSString *)appKey; // Most recent app key-- may not be that used to open the session.
- (BOOL)isOptedOut;
- (NSString *)customDimension:(int)dimension; - (BOOL)setOptedOut:(BOOL)optOut;
- (BOOL)setCustomDimension:(int)dimension value:(NSString *)value; - (NSString *)installId;
- (NSString *)appKey; // Most recent app key-- may not be that used to open the session.
@end
- (NSString *)customDimension:(int)dimension;
- (BOOL)setCustomDimension:(int)dimension value:(NSString *)value;
- (NSString *)customerId;
- (BOOL)setCustomerId:(NSString *)newCustomerId;
- (NSInteger)safeIntegerValueFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
- (NSString *)safeStringValueFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
- (NSDictionary *)safeDictionaryFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
- (NSArray *)safeListFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
@end

File diff suppressed because it is too large Load Diff

View File

@ -1,216 +1,251 @@
// LocalyticsSession.h // LocalyticsSession.h
// Copyright (C) 2009 Char Software Inc., DBA Localytics // Copyright (C) 2012 Char Software Inc., DBA Localytics
// //
// This code is provided under the Localytics Modified BSD License. // This code is provided under the Localytics Modified BSD License.
// A copy of this license has been distributed in a file called LICENSE // A copy of this license has been distributed in a file called LICENSE
// with this source code. // with this source code.
// //
// Please visit www.localytics.com for more information. // Please visit www.localytics.com for more information.
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
// Set this to true to enable localytics traces (useful for debugging)
#define DO_LOCALYTICS_LOGGING false // Set this to true to enable localytics traces (useful for debugging)
#define DO_LOCALYTICS_LOGGING false
/*!
@class LocalyticsSession /*!
@discussion The class which manages creating, collecting, & uploading a Localytics session. @class LocalyticsSession
Please see the following guides for information on how to best use this @discussion The class which manages creating, collecting, & uploading a Localytics session.
library, sample code, and other useful information: Please see the following guides for information on how to best use this
<ul> library, sample code, and other useful information:
<li><a href="http://wiki.localytics.com/index.php?title=Developer's_Integration_Guide">Main Developer's Integration Guide</a></li> <ul>
</ul> <li><a href="http://wiki.localytics.com/index.php?title=Developer's_Integration_Guide">Main Developer's Integration Guide</a></li>
</ul>
<strong>Best Practices</strong>
<ul> <strong>Best Practices</strong>
<li>Instantiate the LocalyticsSession object in applicationDidFinishLaunching.</li> <ul>
<li>Open your session and begin your uploads in applicationDidFinishLaunching. This way the <li>Instantiate the LocalyticsSession object in applicationDidFinishLaunching.</li>
upload has time to complete and it all happens before your users have a <li>Open your session and begin your uploads in applicationDidFinishLaunching. This way the
chance to begin any data intensive actions of their own.</li> upload has time to complete and it all happens before your users have a
<li>Close the session in applicationWillTerminate, and in applicationDidEnterBackground.</li> chance to begin any data intensive actions of their own.</li>
<li>Resume the session in applicationWillEnterForeground.</li> <li>Close the session in applicationWillTerminate, and in applicationDidEnterBackground.</li>
<li>Do not call any Localytics functions inside a loop. Instead, calls <li>Resume the session in applicationWillEnterForeground.</li>
such as <code>tagEvent</code> should follow user actions. This limits the <li>Do not call any Localytics functions inside a loop. Instead, calls
amount of data which is stored and uploaded.</li> such as <code>tagEvent</code> should follow user actions. This limits the
<li>Do not use multiple LocalticsSession objects to upload data with amount of data which is stored and uploaded.</li>
multiple application keys. This can cause invalid state.</li> <li>Do not use multiple LocalticsSession objects to upload data with
</ul> multiple application keys. This can cause invalid state.</li>
</ul>
@author Localytics
*/ @author Localytics
@interface LocalyticsSession : NSObject { */
BOOL _hasInitialized; // Whether or not the session object has been initialized. @interface LocalyticsSession : NSObject {
BOOL _isSessionOpen; // Whether or not this session has been opened.
float _backgroundSessionTimeout; // If an App stays in the background for more BOOL _hasInitialized; // Whether or not the session object has been initialized.
// than this many seconds, start a new session BOOL _isSessionOpen; // Whether or not this session has been opened.
// when it returns to foreground. float _backgroundSessionTimeout; // If an App stays in the background for more
@private // than this many seconds, start a new session
#pragma mark Member Variables // when it returns to foreground.
dispatch_queue_t _queue; // Queue of Localytics block objects. @private
dispatch_group_t _criticalGroup; // Group of blocks the must complete before backgrounding. #pragma mark Member Variables
NSString *_sessionUUID; // Unique identifier for this session. dispatch_queue_t _queue; // Queue of Localytics block objects.
NSString *_applicationKey; // Unique identifier for the instrumented application dispatch_group_t _criticalGroup; // Group of blocks the must complete before backgrounding.
NSTimeInterval _lastSessionStartTimestamp; // The start time of the most recent session. NSString *_sessionUUID; // Unique identifier for this session.
NSDate *_sessionResumeTime; // Time session was started or resumed. NSString *_applicationKey; // Unique identifier for the instrumented application
NSDate *_sessionCloseTime; // Time session was closed. NSTimeInterval _lastSessionStartTimestamp; // The start time of the most recent session.
NSMutableString *_unstagedFlowEvents; // Comma-delimited list of app screens and events tagged during this NSDate *_sessionResumeTime; // Time session was started or resumed.
// session that have NOT been staged for upload. NSDate *_sessionCloseTime; // Time session was closed.
NSMutableString *_stagedFlowEvents; // App screens and events tagged during this session that HAVE been staged NSMutableString *_unstagedFlowEvents; // Comma-delimited list of app screens and events tagged during this
// for upload. // session that have NOT been staged for upload.
NSMutableString *_screens; // Comma-delimited list of screens tagged during this session. NSMutableString *_stagedFlowEvents; // App screens and events tagged during this session that HAVE been staged
NSTimeInterval _sessionActiveDuration; // Duration that session open. // for upload.
BOOL _sessionHasBeenOpen; // Whether or not this session has ever been open. NSMutableString *_screens; // Comma-delimited list of screens tagged during this session.
} NSTimeInterval _sessionActiveDuration; // Duration that session open.
BOOL _sessionHasBeenOpen; // Whether or not this session has ever been open.
@property dispatch_queue_t queue; }
@property dispatch_group_t criticalGroup;
@property BOOL isSessionOpen; @property (nonatomic,readonly) dispatch_queue_t queue;
@property BOOL hasInitialized; @property (nonatomic,readonly) dispatch_group_t criticalGroup;
@property float backgroundSessionTimeout; @property BOOL isSessionOpen;
@property BOOL hasInitialized;
#pragma mark Public Methods @property float backgroundSessionTimeout;
/*!
@method sharedLocalyticsSession - (void)logMessage:(NSString *)message;
@abstract Accesses the Session object. This is a Singleton class which maintains @property (nonatomic, assign, readonly) NSTimeInterval lastSessionStartTimestamp;
a single session throughout your application. It is possible to manage your own @property (nonatomic, assign, readonly) NSInteger sessionNumber;
session, but this is the easiest way to access the Localytics object throughout your code.
The class is accessed within the code using the following syntax:
[[LocalyticsSession sharedLocalyticsSession] functionHere] /*!
So, to tag an event, all that is necessary, anywhere in the code is: @property enableHTTPS
[[LocalyticsSession sharedLocalyticsSession] tagEvent:@"MY_EVENT"]; @abstract Determines whether or not HTTPS is used when calling the Localytics
*/ post URL. The default is NO.
+ (LocalyticsSession *)sharedLocalyticsSession; */
@property (nonatomic, assign) BOOL enableHTTPS; // Defaults to NO.
/*!
@method LocalyticsSession #pragma mark Public Methods
@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 @method sharedLocalyticsSession
*/ @abstract Accesses the Session object. This is a Singleton class which maintains
- (void)LocalyticsSession:(NSString *)appKey; a single session throughout your application. It is possible to manage your own
session, but this is the easiest way to access the Localytics object throughout your code.
/*! The class is accessed within the code using the following syntax:
@method startSession [[LocalyticsSession sharedLocalyticsSession] functionHere]
@abstract An optional convenience initialize method that also calls the LocalyticsSession, open & So, to tag an event, all that is necessary, anywhere in the code is:
upload methods. Best Practice is to call open & upload immediately after Localytics Session when loading an app, [[LocalyticsSession sharedLocalyticsSession] tagEvent:@"MY_EVENT"];
this method fascilitates that behavior. */
It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>. + (LocalyticsSession *)sharedLocalyticsSession;
@param applicationKey The key unique for each application generated
at www.localytics.com /*!
*/ @method LocalyticsSession
- (void)startSession:(NSString *)appKey; @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
/*! */
@method setOptIn - (void)LocalyticsSession:(NSString *)appKey;
@abstract (OPTIONAL) Allows the application to control whether or not it will collect user data.
Even if this call is used, it is necessary to continue calling upload(). No new data will be /*!
collected, so nothing new will be uploaded but it is necessary to upload an event telling the @method startSession
server this user has opted out. @abstract An optional convenience initialize method that also calls the LocalyticsSession, open &
@param optedIn True if the user is opted in, false otherwise. upload methods. Best Practice is to call open & upload immediately after Localytics Session when loading an app,
*/ this method fascilitates that behavior.
- (void)setOptIn:(BOOL)optedIn; It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
@param applicationKey The key unique for each application generated
/*! at www.localytics.com
@method isOptedIn */
@abstract (OPTIONAL) Whether or not this user has is opted in or out. The only way they can be - (void)startSession:(NSString *)appKey;
opted out is if setOptIn(false) has been called before this. This function should only be
used to pre-populate a checkbox in an options menu. It is not recommended that an application /*!
branch based on Localytics instrumentation because this creates an additional test case. If @method setOptIn
the app is opted out, all subsequent Localytics calls will return immediately. @abstract (OPTIONAL) Allows the application to control whether or not it will collect user data.
@result true if the user is opted in, false otherwise. Even if this call is used, it is necessary to continue calling upload(). No new data will be
*/ collected, so nothing new will be uploaded but it is necessary to upload an event telling the
- (BOOL)isOptedIn; server this user has opted out.
@param optedIn True if the user is opted in, false otherwise.
/*! */
@method open - (void)setOptIn:(BOOL)optedIn;
@abstract Opens the Localytics session. Not necessary if you choose to use startSession.
The session time as presented on the website is the time between <code>open</code> and the /*!
final <code>close</code> so it is recommended to open the session as early as possible, and close @method isOptedIn
it at the last moment. The session must be opened before any tags can @abstract (OPTIONAL) Whether or not this user has is opted in or out. The only way they can be
be written. It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>. opted out is if setOptIn(false) has been called before this. This function should only be
<br> used to pre-populate a checkbox in an options menu. It is not recommended that an application
If for any reason this is called more than once every subsequent open call branch based on Localytics instrumentation because this creates an additional test case. If
will be ignored. the app is opted out, all subsequent Localytics calls will return immediately.
*/ @result true if the user is opted in, false otherwise.
- (void)open; */
- (BOOL)isOptedIn;
/*!
@method resume /*!
@abstract Resumes the Localytics session. When the App enters the background, the session is @method open
closed and the time of closing is recorded. When the app returns to the foreground, the session @abstract Opens the Localytics session. Not necessary if you choose to use startSession.
is resumed. If the time since closing is greater than BACKGROUND_SESSION_TIMEOUT, (15 seconds The session time as presented on the website is the time between <code>open</code> and the
by default) a new session is created, and uploading is triggered. Otherwise, the previous session final <code>close</code> so it is recommended to open the session as early as possible, and close
is reopened. it at the last moment. The session must be opened before any tags can
*/ be written. It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
- (void)resume; <br>
If for any reason this is called more than once every subsequent open call
/*! will be ignored.
@method close */
@abstract Closes the Localytics session. This should be called in - (void)open;
<code>applicationWillTerminate</code>.
<br> /*!
If close is not called, the session will still be uploaded but no @method resume
events will be processed and the session time will not appear. This is @abstract Resumes the Localytics session. When the App enters the background, the session is
because the session is not yet closed so it should not be used in closed and the time of closing is recorded. When the app returns to the foreground, the session
comparison with sessions which are closed. is resumed. If the time since closing is greater than BACKGROUND_SESSION_TIMEOUT, (15 seconds
*/ by default) a new session is created, and uploading is triggered. Otherwise, the previous session
- (void)close; is reopened. It is possible to use the return value to determine whether or not a session was resumed.
This may be useful to some customers looking to do conditional instrumentation at the close of a session.
/*! It is perfectly reasonable to ignore the return value.
@method tagEvent @result YES if the sesion was resumed NO if it wasn't (suggesting a new session was created instead).*/
@abstract Allows a session to tag a particular event as having occurred. For - (BOOL)resume;
example, if a view has three buttons, it might make sense to tag
each button click with the name of the button which was clicked. /*!
For another example, in a game with many levels it might be valuable @method close
to create a new tag every time the user gets to a new level in order @abstract Closes the Localytics session. This should be called in
to determine how far the average user is progressing in the game. <code>applicationWillTerminate</code>.
<br> <br>
<strong>Tagging Best Practices</strong> If close is not called, the session will still be uploaded but no
<ul> events will be processed and the session time will not appear. This is
<li>DO NOT use tags to record personally identifiable information.</li> because the session is not yet closed so it should not be used in
<li>The best way to use tags is to create all the tag strings as predefined comparison with sessions which are closed.
constants and only use those. This is more efficient and removes the risk of */
collecting personal information.</li> - (void)close;
<li>Do not set tags inside loops or any other place which gets called
frequently. This can cause a lot of data to be stored and uploaded.</li> /*!
</ul> @method tagEvent
<br> @abstract Allows a session to tag a particular event as having occurred. For
See the tagging guide at: http://wiki.localytics.com/ example, if a view has three buttons, it might make sense to tag
@param event The name of the event which occurred. each button click with the name of the button which was clicked.
*/ For another example, in a game with many levels it might be valuable
- (void)tagEvent:(NSString *)event; to create a new tag every time the user gets to a new level in order
to determine how far the average user is progressing in the game.
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes; <br>
<strong>Tagging Best Practices</strong>
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes reportAttributes:(NSDictionary *)reportAttributes; <ul>
<li>DO NOT use tags to record personally identifiable information.</li>
/*! <li>The best way to use tags is to create all the tag strings as predefined
@method tagScreen constants and only use those. This is more efficient and removes the risk of
@abstract Allows tagging the flow of screens encountered during the session. collecting personal information.</li>
@param screen The name of the screen <li>Do not set tags inside loops or any other place which gets called
*/ frequently. This can cause a lot of data to be stored and uploaded.</li>
- (void)tagScreen:(NSString *)screen; </ul>
<br>
/*! See the tagging guide at: http://wiki.localytics.com/
@method upload @param event The name of the event which occurred.
@abstract Creates a low priority thread which uploads any Localytics data already stored */
on the device. This should be done early in the process life in order to - (void)tagEvent:(NSString *)event;
guarantee as much time as possible for slow connections to complete. It is also reasonable
to upload again when the application is exiting because if the upload is cancelled the data - (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes;
will just get uploaded the next time the app comes up.
*/ - (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes reportAttributes:(NSDictionary *)reportAttributes;
- (void)upload;
/*!
/*! @method tagScreen
@method setCustomDimension @abstract Allows tagging the flow of screens encountered during the session.
@abstract (ENTERPRISE ONLY) Sets the value of a custom dimension. Custom dimensions are dimensions @param screen The name of the screen
which contain user defined data unlike the predefined dimensions such as carrier, model, and country. */
Once a value for a custom dimension is set, the device it was set on will continue to upload that value - (void)tagScreen:(NSString *)screen;
until the value is changed. To clear a value pass nil as the value.
The proper use of custom dimensions involves defining a dimension with less than ten distinct possible /*!
values and assigning it to one of the four available custom dimensions. Once assigned this definition should @method upload
never be changed without changing the App Key otherwise old installs of the application will pollute new data. @abstract Creates a low priority thread which uploads any Localytics data already stored
*/ on the device. This should be done early in the process life in order to
- (void)setCustomDimension:(int)dimension value:(NSString *)value; guarantee as much time as possible for slow connections to complete. It is also reasonable
to upload again when the application is exiting because if the upload is cancelled the data
@end will just get uploaded the next time the app comes up.
*/
- (void)upload;
/*!
@method setCustomDimension
@abstract (ENTERPRISE ONLY) Sets the value of a custom dimension. Custom dimensions are dimensions
which contain user defined data unlike the predefined dimensions such as carrier, model, and country.
Once a value for a custom dimension is set, the device it was set on will continue to upload that value
until the value is changed. To clear a value pass nil as the value.
The proper use of custom dimensions involves defining a dimension with less than ten distinct possible
values and assigning it to one of the four available custom dimensions. Once assigned this definition should
never be changed without changing the App Key otherwise old installs of the application will pollute new data.
*/
- (void)setCustomDimension:(int)dimension value:(NSString *)value;
/*!
@method setLocation
@abstract Stores the user's location. This will be used in all event and session calls.
If your application has already collected the user's location, it may be passed to Localytics
via this function. This will cause all events and the session close to include the locatin
information. It is not required that you call this function.
@param deviceLocation The user's location.
*/
- (void)setLocation:(CLLocationCoordinate2D)deviceLocation;
/*!
@method ampTrigger
@abstract Displays the AMP message for the specific event.
Is a stub implementation here to prevent crashes if this class is accidentally used inplace of
the LocalyticsAmpSession
@param event Name of the event.
*/
- (void)ampTrigger:(NSString *)event;
@end

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// LocalyticsUploader.h // LocalyticsUploader.h
// Copyright (C) 2009 Char Software Inc., DBA Localytics // Copyright (C) 2012 Char Software Inc., DBA Localytics
// //
// This code is provided under the Localytics Modified BSD License. // This code is provided under the Localytics Modified BSD License.
// A copy of this license has been distributed in a file called LICENSE // A copy of this license has been distributed in a file called LICENSE
@ -9,6 +9,8 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
extern NSString * const kLocalyticsKeyResponseBody;
/*! /*!
@class LocalyticsUploader @class LocalyticsUploader
@discussion Singleton class to handle data uploads @discussion Singleton class to handle data uploads
@ -35,8 +37,28 @@
writing data regardless of whether or not the upload succeeds. Files writing data regardless of whether or not the upload succeeds. Files
which have been renamed still count towards the total number of Localytics which have been renamed still count towards the total number of Localytics
files which can be stored on the disk. files which can be stored on the disk.
This version of the method now just calls the second version of it with a nil target and NULL callback method.
@param localyticsApplicationKey the Localytics application ID @param localyticsApplicationKey the Localytics application ID
@param useHTTPS Flag determining whether HTTP or HTTPS is used for the post URL.
@param installId Install id passed to the server in the x-install-id header field.
*/ */
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey; - (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId;
/*!
@method LocalyticsUploader
@abstract Creates a thread which uploads all queued header and event data.
All files starting with sessionFilePrefix are renamed,
uploaded and deleted on upload. This way the sessions can continue
writing data regardless of whether or not the upload succeeds. Files
which have been renamed still count towards the total number of Localytics
files which can be stored on the disk.
@param localyticsApplicationKey the Localytics application ID
@param useHTTPS Flag determining whether HTTP or HTTPS is used for the post URL.
@param installId Install id passed to the server in the x-install-id header field.
@param resultTarget Result target is the target for the callback method that knows how to handle response data
@param callback Callback is the method of the target class that is to be called with the data begin returned by an upload
*/
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId resultTarget:(id)target callback:(SEL)callbackMethod;
@end @end

View File

@ -1,5 +1,5 @@
// LocalyticsUploader.m // LocalyticsUploader.m
// Copyright (C) 2009 Char Software Inc., DBA Localytics // Copyright (C) 2012 Char Software Inc., DBA Localytics
// //
// This code is provided under the Localytics Modified BSD License. // This code is provided under the Localytics Modified BSD License.
// A copy of this license has been distributed in a file called LICENSE // A copy of this license has been distributed in a file called LICENSE
@ -10,16 +10,25 @@
#import "LocalyticsUploader.h" #import "LocalyticsUploader.h"
#import "LocalyticsSession.h" #import "LocalyticsSession.h"
#import "LocalyticsDatabase.h" #import "LocalyticsDatabase.h"
#import "WebserviceConstants.h"
#import <zlib.h> #import <zlib.h>
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads" #ifndef LOCALYTICS_URL
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads"
#endif
#ifndef LOCALYTICS_URL_SECURED
#define LOCALYTICS_URL_SECURED @"https://analytics.localytics.com/api/v2/applications/%@/uploads"
#endif
static LocalyticsUploader *_sharedUploader = nil; static LocalyticsUploader *_sharedUploader = nil;
NSString * const kLocalyticsKeyResponseBody = @"localytics.key.responseBody";
@interface LocalyticsUploader () @interface LocalyticsUploader ()
- (void)finishUpload; - (void)finishUpload;
- (NSData *)gzipDeflatedDataWithData:(NSData *)data; - (NSData *)gzipDeflatedDataWithData:(NSData *)data;
- (void)logMessage:(NSString *)message; - (void)logMessage:(NSString *)message;
- (NSString *)uploadTimeStamp;
@property (readwrite) BOOL isUploading; @property (readwrite) BOOL isUploading;
@ -40,7 +49,13 @@ static LocalyticsUploader *_sharedUploader = nil;
#pragma mark - Class Methods #pragma mark - Class Methods
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey { - (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId
{
[self uploaderWithApplicationKey:localyticsApplicationKey useHTTPS:useHTTPS installId:installId resultTarget:nil callback:NULL];
}
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId resultTarget:(id)target callback:(SEL)callbackMethod;
{
// Do nothing if already uploading. // Do nothing if already uploading.
if (self.isUploading == true) if (self.isUploading == true)
@ -77,17 +92,26 @@ static LocalyticsUploader *_sharedUploader = nil;
NSData *requestData = [blobString dataUsingEncoding:NSUTF8StringEncoding]; NSData *requestData = [blobString dataUsingEncoding:NSUTF8StringEncoding];
NSString *myString = [[[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding] autorelease]; NSString *myString = [[[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding] autorelease];
[self logMessage:[NSString stringWithFormat:@"Uploading data (length: %u)", [myString length]]]; [self logMessage:[NSString stringWithFormat:@"Uploading data (length: %u)", [myString length]]];
[self logMessage:myString];
// Step 2 // Step 2
NSData *deflatedRequestData = [[self gzipDeflatedDataWithData:requestData] retain]; NSData *deflatedRequestData = [[self gzipDeflatedDataWithData:requestData] retain];
[pool drain]; [pool drain];
NSString *apiUrlString = [NSString stringWithFormat:LOCALYTICS_URL, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; NSString *urlStringFormat;
if (useHTTPS) {
urlStringFormat = LOCALYTICS_URL_SECURED;
} else {
urlStringFormat = LOCALYTICS_URL;
}
NSString *apiUrlString = [NSString stringWithFormat:urlStringFormat, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSMutableURLRequest *submitRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:apiUrlString] NSMutableURLRequest *submitRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:apiUrlString]
cachePolicy:NSURLRequestReloadIgnoringCacheData cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0]; timeoutInterval:60.0];
[submitRequest setHTTPMethod:@"POST"]; [submitRequest setHTTPMethod:@"POST"];
[submitRequest setValue:[self uploadTimeStamp] forHTTPHeaderField:HEADER_CLIENT_TIME];
[submitRequest setValue:installId forHTTPHeaderField:HEADER_INSTALL_ID];
[submitRequest setValue:@"application/x-gzip" forHTTPHeaderField:@"Content-Type"]; [submitRequest setValue:@"application/x-gzip" forHTTPHeaderField:@"Content-Type"];
[submitRequest setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; [submitRequest setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
[submitRequest setValue:[NSString stringWithFormat:@"%d", [deflatedRequestData length]] forHTTPHeaderField:@"Content-Length"]; [submitRequest setValue:[NSString stringWithFormat:@"%d", [deflatedRequestData length]] forHTTPHeaderField:@"Content-Length"];
@ -100,7 +124,7 @@ static LocalyticsUploader *_sharedUploader = nil;
@try { @try {
NSURLResponse *response = nil; NSURLResponse *response = nil;
NSError *responseError = nil; NSError *responseError = nil;
[NSURLConnection sendSynchronousRequest:submitRequest returningResponse:&response error:&responseError]; NSData *responseData = [NSURLConnection sendSynchronousRequest:submitRequest returningResponse:&response error:&responseError];
NSInteger responseStatusCode = [(NSHTTPURLResponse *)response statusCode]; NSInteger responseStatusCode = [(NSHTTPURLResponse *)response statusCode];
if (responseError) { if (responseError) {
@ -123,6 +147,18 @@ static LocalyticsUploader *_sharedUploader = nil;
[[LocalyticsDatabase sharedLocalyticsDatabase] deleteUploadedData]; [[LocalyticsDatabase sharedLocalyticsDatabase] deleteUploadedData];
} }
} }
if ([responseData length] > 0) {
if (DO_LOCALYTICS_LOGGING) {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[self logMessage:[NSString stringWithFormat:@"Response body: %@", responseString]];
[responseString release];
}
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:responseData forKey:kLocalyticsKeyResponseBody];
if (target) {
[target performSelector:callbackMethod withObject:userInfo];
}
}
} }
@catch (NSException * e) {} @catch (NSException * e) {}
@ -195,6 +231,15 @@ static LocalyticsUploader *_sharedUploader = nil;
} }
} }
/*!
@method uploadTimeStamp
@abstract Gets the current time, along with local timezone, formatted as a DateTime for the webservice.
@return a DateTime of the current local time and timezone.
*/
- (NSString *)uploadTimeStamp {
return [ NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970] ];
}
#pragma mark - System Functions #pragma mark - System Functions
+ (id)allocWithZone:(NSZone *)zone { + (id)allocWithZone:(NSZone *)zone {
@synchronized(self) { @synchronized(self) {

View File

@ -1,48 +0,0 @@
// UploaderThread.h
// Copyright (C) 2009 Char Software Inc., DBA Localytics
//
// This code is provided under the Localytics Modified BSD License.
// A copy of this license has been distributed in a file called LICENSE
// with this source code.
//
// Please visit www.localytics.com for more information.
#import <UIKit/UIKit.h>
/*!
@class UploaderThread
@discussion Singleton class to handle data uploads
*/
@interface UploaderThread : NSObject {
NSURLConnection *_uploadConnection; // The connection which uploads the bits
NSInteger _responseStatusCode; // The HTTP response status code for the current connection
BOOL _isUploading; // A flag to gaurantee only one uploader instance can happen at once
}
@property (nonatomic, retain) NSURLConnection *uploadConnection;
@property BOOL isUploading;
/*!
@method sharedUploaderThread
@abstract Establishes this as a Singleton Class allowing for data persistence.
The class is accessed within the code using the following syntax:
[[UploaderThread sharedUploaderThread] functionHere]
*/
+ (UploaderThread *)sharedUploaderThread;
/*!
@method UploaderThread
@abstract Creates a thread which uploads all queued header and event data.
All files starting with sessionFilePrefix are renamed,
uploaded and deleted on upload. This way the sessions can continue
writing data regardless of whether or not the upload succeeds. Files
which have been renamed still count towards the total number of Localytics
files which can be stored on the disk.
@param localyticsApplicationKey the Localytics application ID
*/
- (void)uploaderThreadwithApplicationKey:(NSString *)localyticsApplicationKey;
@end

View File

@ -1,260 +0,0 @@
// UploaderThread.m
// Copyright (C) 2009 Char Software Inc., DBA Localytics
//
// This code is provided under the Localytics Modified BSD License.
// A copy of this license has been distributed in a file called LICENSE
// with this source code.
//
// Please visit www.localytics.com for more information.
#import "UploaderThread.h"
#import "LocalyticsSession.h"
#import "LocalyticsDatabase.h"
#import <zlib.h>
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads" // url to send the
static UploaderThread *_sharedUploaderThread = nil;
@interface UploaderThread ()
- (void)complete;
- (NSData *)gzipDeflatedDataWithData:(NSData *)data;
- (void)logMessage:(NSString *)message;
@end
@implementation UploaderThread
@synthesize uploadConnection = _uploadConnection;
@synthesize isUploading = _isUploading;
#pragma mark Singleton Class
+ (UploaderThread *)sharedUploaderThread {
@synchronized(self) {
if (_sharedUploaderThread == nil)
{
_sharedUploaderThread = [[self alloc] init];
}
}
return _sharedUploaderThread;
}
#pragma mark Class Methods
- (void)uploaderThreadwithApplicationKey:(NSString *)localyticsApplicationKey {
// Do nothing if already uploading.
if (self.uploadConnection != nil || self.isUploading == true)
{
[self logMessage:@"Upload already in progress. Aborting."];
return;
}
[self logMessage:@"Beginning upload process"];
self.isUploading = true;
// Prepare the data for upload. The upload could take a long time, so some effort has to be made to be sure that events
// which get written while the upload is taking place don't get lost or duplicated. To achieve this, the logic is:
// 1) Append every header row blob string and and those of its associated events to the upload string.
// 2) Deflate and upload the data.
// 3) On success, delete all blob headers and staged events. Events added while an upload is in process are not
// deleted because they are not associated a header (and cannot be until the upload completes).
// Step 1
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
LocalyticsDatabase *db = [LocalyticsDatabase sharedLocalyticsDatabase];
NSString *blobString = [db uploadBlobString];
if ([blobString length] == 0) {
// There is nothing outstanding to upload.
[self logMessage:@"Abandoning upload. There are no new events."];
[pool drain];
[self complete];
return;
}
NSData *requestData = [blobString dataUsingEncoding:NSUTF8StringEncoding];
NSString *myString = [[[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding] autorelease];
[self logMessage:@"Upload data:"];
[self logMessage:myString];
// Step 2
NSData *deflatedRequestData = [[self gzipDeflatedDataWithData:requestData] retain];
[pool drain];
NSString *apiUrlString = [NSString stringWithFormat:LOCALYTICS_URL, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSMutableURLRequest *submitRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:apiUrlString]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0];
[submitRequest setHTTPMethod:@"POST"];
[submitRequest setValue:@"application/x-gzip" forHTTPHeaderField:@"Content-Type"];
[submitRequest setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
[submitRequest setValue:[NSString stringWithFormat:@"%d", [deflatedRequestData length]] forHTTPHeaderField:@"Content-Length"];
[submitRequest setHTTPBody:deflatedRequestData];
[deflatedRequestData release];
// The NSURLConnection Object automatically spawns its own thread as a default behavior.
@try
{
[self logMessage:@"Spawning new thread for upload"];
self.uploadConnection = [NSURLConnection connectionWithRequest:submitRequest delegate:self];
// Step 3 is handled by connectionDidFinishLoading.
}
@catch (NSException * e)
{
[self complete];
}
}
#pragma mark **** NSURLConnection FUNCTIONS ****
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Used to gather response data from server - Not utilized in this version
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// Could receive multiple response callbacks, likely due to redirection.
// Record status and act only when connection completes load.
_responseStatusCode = [(NSHTTPURLResponse *)response statusCode];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// If the connection finished loading, the files should be deleted. While response status codes in the 5xx range
// leave upload rows intact, the default case is to delete.
if (_responseStatusCode >= 500 && _responseStatusCode < 600)
{
[self logMessage:[NSString stringWithFormat:@"Upload failed with response status code %d", _responseStatusCode]];
} else
{
// The connection finished loading and uploaded data should be deleted. Because only one instance of the
// uploader can be running at a time it should not be possible for new upload rows to appear so there is no
// fear of deleting data which has not yet been uploaded.
[self logMessage:[NSString stringWithFormat:@"Upload completed successfully. Response code %d", _responseStatusCode]];
[[LocalyticsDatabase sharedLocalyticsDatabase] deleteUploadData];
}
// Close upload session
[self complete];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// On error, simply print the error and close the uploader. We have to assume the data was not transmited
// so it is not deleted. In the event that we accidently store data which was succesfully uploaded, the
// duplicate data will be ignored by the server when it is next uploaded.
[self logMessage:[NSString stringWithFormat:
@"Error Uploading. Code: %d, Description: %s",
[error code],
[error localizedDescription]]];
[self complete];
}
/*!
@method complete
@abstract closes the upload connection and reports back to the session that the upload is complete
*/
- (void)complete {
_responseStatusCode = 0;
self.uploadConnection = nil;
self.isUploading = false;
}
/*!
@method gzipDeflatedDataWithData
@abstract Deflates the provided data using gzip at the default compression level (6). Complete NSData gzip category available on CocoaDev. http://www.cocoadev.com/index.pl?NSDataCategory.
@return the deflated data
*/
- (NSData *)gzipDeflatedDataWithData:(NSData *)data
{
if ([data length] == 0) return data;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.total_out = 0;
strm.next_in=(Bytef *)[data bytes];
strm.avail_in = [data length];
// Compresssion Levels:
// Z_NO_COMPRESSION
// Z_BEST_SPEED
// Z_BEST_COMPRESSION
// Z_DEFAULT_COMPRESSION
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
do {
if (strm.total_out >= [compressed length])
[compressed increaseLengthBy: 16384];
strm.next_out = [compressed mutableBytes] + strm.total_out;
strm.avail_out = [compressed length] - strm.total_out;
deflate(&strm, Z_FINISH);
} while (strm.avail_out == 0);
deflateEnd(&strm);
[compressed setLength: strm.total_out];
return [NSData dataWithData:compressed];
}
/*!
@method logMessage
@abstract Logs a message with (localytics uploader) prepended to it
@param message The message to log
*/
- (void) logMessage:(NSString *)message {
if(DO_LOCALYTICS_LOGGING) {
NSLog(@"(localytics uploader) %s\n", [message UTF8String]);
}
}
#pragma mark System Functions
+ (id)allocWithZone:(NSZone *)zone {
@synchronized(self) {
if (_sharedUploaderThread == nil) {
_sharedUploaderThread = [super allocWithZone:zone];
return _sharedUploaderThread;
}
}
// returns nil on subsequent allocations
return nil;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
// maximum value of an unsigned int - prevents additional retains for the class
return UINT_MAX;
}
- (oneway void)release {
// ignore release commands
}
- (id)autorelease {
return self;
}
- (void)dealloc {
[_uploadConnection release];
[_sharedUploaderThread release];
[super dealloc];
}
@end

View File

@ -1,5 +1,5 @@
// WebserviceConstants.h // WebserviceConstants.h
// Copyright (C) 2009 Char Software Inc., DBA Localytics // Copyright (C) 2012 Char Software Inc., DBA Localytics
// //
// This code is provided under the Localytics Modified BSD License. // This code is provided under the Localytics Modified BSD License.
// A copy of this license has been distributed in a file called LICENSE // A copy of this license has been distributed in a file called LICENSE
@ -11,6 +11,12 @@
// To save disk space and network bandwidth all the keywords have been // To save disk space and network bandwidth all the keywords have been
// abbreviated and are exploded by the server. // abbreviated and are exploded by the server.
/*****************
* Upload Header *
*****************/
#define HEADER_CLIENT_TIME @"x-upload-time"
#define HEADER_INSTALL_ID @"x-install-id"
/********************* /*********************
* Shared Attributes * * Shared Attributes *
*********************/ *********************/
@ -22,6 +28,7 @@
#define PARAM_SESSION_UUID @"su" // UUID for an existing session #define PARAM_SESSION_UUID @"su" // UUID for an existing session
#define PARAM_NEW_SESSION_UUID @"u" // UUID for a new session #define PARAM_NEW_SESSION_UUID @"u" // UUID for a new session
#define PARAM_ATTRIBUTES @"attrs" // Attributes (dictionary) #define PARAM_ATTRIBUTES @"attrs" // Attributes (dictionary)
#define PARAM_SESSION_ELAPSE_TIME @"sl" // Number of seconds since the previous session start
/*************** /***************
* Blob Header * * Blob Header *
@ -42,9 +49,8 @@
// PARAM_DATA_TYPE // PARAM_DATA_TYPE
#define PARAM_APP_KEY @"au" // Localytics Application ID #define PARAM_APP_KEY @"au" // Localytics Application ID
#define PARAM_DEVICE_UUID @"du" // Device UUID
#define PARAM_DEVICE_UUID_HASHED @"udid" // Hashed version of the UUID #define PARAM_DEVICE_UUID_HASHED @"udid" // Hashed version of the UUID
#define PARAM_DEVICE_MAC @"wmac" // Hashed version of the device Mac #define PARAM_DEVICE_ADID @"adid" // Advertising Identifier
#define PARAM_INSTALL_ID @"iu" // Install ID #define PARAM_INSTALL_ID @"iu" // Install ID
#define PARAM_JAILBROKEN @"j" // Jailbroken (boolean) #define PARAM_JAILBROKEN @"j" // Jailbroken (boolean)
#define PARAM_LIBRARY_VERSION @"lv" // Client Version #define PARAM_LIBRARY_VERSION @"lv" // Client Version
@ -52,14 +58,11 @@
#define PARAM_DEVICE_PLATFORM @"dp" // Device Platform #define PARAM_DEVICE_PLATFORM @"dp" // Device Platform
#define PARAM_LOCALE_LANGUAGE @"dll" // Locale Language #define PARAM_LOCALE_LANGUAGE @"dll" // Locale Language
#define PARAM_LOCALE_COUNTRY @"dlc" // Locale Country #define PARAM_LOCALE_COUNTRY @"dlc" // Locale Country
#define PARAM_NETWORK_COUNTRY @"nc" // Network Country (iso code) // ???: Never used on iPhone.
#define PARAM_DEVICE_COUNTRY @"dc" // Device Country (iso code) #define PARAM_DEVICE_COUNTRY @"dc" // Device Country (iso code)
#define PARAM_DEVICE_MANUFACTURER @"dma" // Device Manufacturer // ???: Never used on iPhone. Used to be "Device Make".
#define PARAM_DEVICE_MODEL @"dmo" // Device Model #define PARAM_DEVICE_MODEL @"dmo" // Device Model
#define PARAM_DEVICE_OS_VERSION @"dov" // Device OS Version #define PARAM_DEVICE_OS_VERSION @"dov" // Device OS Version
#define PARAM_NETWORK_CARRIER @"nca" // Network Carrier #define PARAM_NETWORK_CARRIER @"nca" // Network Carrier
#define PARAM_DATA_CONNECTION @"dac" // Data Connection Type // ???: Never used on iPhone. #define PARAM_OPT_VALUE @"out" // Opt Out (boolean)
#define PARAM_OPT_VALUE @"optin" // Opt In (boolean)
#define PARAM_DEVICE_MEMORY @"dmem" // Device Memory #define PARAM_DEVICE_MEMORY @"dmem" // Device Memory
/***************** /*****************

View File

@ -46,11 +46,7 @@
}]; }];
} }
if (![managedObjectContext.persistentStoreCoordinator.persistentStores count]) [[self storeManager] persistentStoreCoordinator];
[managedObjectContext performBlockAndWait:^{
managedObjectContext.persistentStoreCoordinator = [self storeManager].persistentStoreCoordinator;
}];
if (![self storeManager].isReady) if (![self storeManager].isReady)
return nil; return nil;

View File

@ -116,6 +116,7 @@
NSString *localyticsKey = [self localyticsKey]; NSString *localyticsKey = [self localyticsKey];
if ([localyticsKey length]) { if ([localyticsKey length]) {
inf(@"Initializing Localytics"); inf(@"Initializing Localytics");
[LocalyticsSession sharedLocalyticsSession].enableHTTPS = YES;
[[LocalyticsSession sharedLocalyticsSession] startSession:localyticsKey]; [[LocalyticsSession sharedLocalyticsSession] startSession:localyticsKey];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
if (message.level >= PearlLogLevelWarn) if (message.level >= PearlLogLevelWarn)
@ -321,23 +322,11 @@
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
wrn(@"Received memory warning."); inf(@"Received memory warning.");
[super applicationDidReceiveMemoryWarning:application]; [super applicationDidReceiveMemoryWarning:application];
} }
- (void)applicationDidBecomeActive:(UIApplication *)application {
inf(@"Re-activated");
[[MPAppDelegate get] checkConfig];
if (FBSession.activeSession.state == FBSessionStateCreatedOpening)
// An old Facebook Login session that wasn't finished. Clean it up.
[FBSession.activeSession close];
[super applicationDidBecomeActive:application];
}
- (void)applicationDidEnterBackground:(UIApplication *)application { - (void)applicationDidEnterBackground:(UIApplication *)application {
[[LocalyticsSession sharedLocalyticsSession] close]; [[LocalyticsSession sharedLocalyticsSession] close];
@ -369,10 +358,31 @@
- (void)applicationWillResignActive:(UIApplication *)application { - (void)applicationWillResignActive:(UIApplication *)application {
inf(@"Will deactivate"); inf(@"Will deactivate");
[self saveContext]; [self saveContext];
if (![[MPiOSConfig get].rememberLogin boolValue]) if (![[MPiOSConfig get].rememberLogin boolValue])
[self signOutAnimated:NO]; [self signOutAnimated:NO];
[[LocalyticsSession sharedLocalyticsSession] close];
[[LocalyticsSession sharedLocalyticsSession] upload];
[super applicationWillResignActive:application];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
inf(@"Re-activated");
[[MPAppDelegate get] checkConfig];
if (FBSession.activeSession.state == FBSessionStateCreatedOpening)
// An old Facebook Login session that wasn't finished. Clean it up.
[FBSession.activeSession close];
[[LocalyticsSession sharedLocalyticsSession] resume];
[[LocalyticsSession sharedLocalyticsSession] upload];
[super applicationDidBecomeActive:application];
} }
#pragma mark - Behavior #pragma mark - Behavior

View File

@ -77,6 +77,13 @@
[super viewWillAppear:animated]; [super viewWillAppear:animated];
} }
- (void)viewDidAppear:(BOOL)animated {
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Apps"];
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide]; [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];

View File

@ -7,6 +7,7 @@
// //
#import "MPGuideViewController.h" #import "MPGuideViewController.h"
#import "LocalyticsSession.h"
@implementation MPGuideViewController @implementation MPGuideViewController
@ -35,6 +36,8 @@
- (void)viewDidAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated {
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Guide"];
[super viewDidAppear:animated]; [super viewDidAppear:animated];
} }

View File

@ -149,7 +149,7 @@
inf(@"Main will appear"); inf(@"Main will appear");
// Sometimes, the search bar gets stuck in some sort of first-responder mode that it can't get out of... // Sometimes, the search bar gets stuck in some sort of first-responder mode that it can't get out of...
[self.searchDisplayController.searchBar resignFirstResponder]; [[self.view.window findFirstResponderInHierarchy] resignFirstResponder];
// Needed for when we appear after a modal VC dismisses: // Needed for when we appear after a modal VC dismisses:
// We can't present until the other modal VC has been fully dismissed and presenting in viewDidAppear will fail. // We can't present until the other modal VC has been fully dismissed and presenting in viewDidAppear will fail.
@ -204,6 +204,8 @@
}]; }];
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Main"];
[super viewDidAppear:animated]; [super viewDidAppear:animated];
} }
@ -676,7 +678,7 @@
- (IBAction)action:(id)sender { - (IBAction)action:(id)sender {
[PearlSheet showSheetWithTitle:nil message:nil viewStyle:UIActionSheetStyleAutomatic [PearlSheet showSheetWithTitle:nil viewStyle:UIActionSheetStyleAutomatic
initSheet:nil initSheet:nil
tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex]) if (buttonIndex == [sheet cancelButtonIndex])

View File

@ -11,6 +11,7 @@
#import "MPAppDelegate.h" #import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "LocalyticsSession.h"
@interface MPPreferencesViewController () @interface MPPreferencesViewController ()
@ -80,6 +81,13 @@
[super viewWillAppear:animated]; [super viewWillAppear:animated];
} }
- (void)viewDidAppear:(BOOL)animated {
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Preferences"];
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
inf(@"Preferences will disappear"); inf(@"Preferences will disappear");
@ -152,6 +160,7 @@
vc.showDoneButton = NO; vc.showDoneButton = NO;
[self.navigationController pushViewController:vc animated:YES]; [self.navigationController pushViewController:vc animated:YES];
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Settings"];
} }
@end @end

View File

@ -7,6 +7,7 @@
// //
#import "MPTypeViewController.h" #import "MPTypeViewController.h"
#import "LocalyticsSession.h"
@interface MPTypeViewController () @interface MPTypeViewController ()
@ -44,6 +45,8 @@
} }
}]; }];
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Type Selection"];
[super viewDidAppear:animated]; [super viewDidAppear:animated];
} }

View File

@ -16,6 +16,7 @@
#import "MPAppDelegate.h" #import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "LocalyticsSession.h"
@interface MPUnlockViewController () @interface MPUnlockViewController ()
@ -193,6 +194,8 @@
self.uiContainer.alpha = 1; self.uiContainer.alpha = 1;
}]; }];
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Unlock"];
[super viewDidAppear:animated]; [super viewDidAppear:animated];
} }
@ -749,7 +752,7 @@
return; return;
[PearlSheet showSheetWithTitle:targetedUser.name [PearlSheet showSheetWithTitle:targetedUser.name
message:nil viewStyle:UIActionSheetStyleBlackTranslucent viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex]) if (buttonIndex == [sheet cancelButtonIndex])
return; return;
@ -823,7 +826,7 @@
- (IBAction)add:(UIButton *)sender { - (IBAction)add:(UIButton *)sender {
[PearlSheet showSheetWithTitle:@"Follow Master Password" message:nil viewStyle:UIActionSheetStyleBlackTranslucent [PearlSheet showSheetWithTitle:@"Follow Master Password" viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex]) if (buttonIndex == [sheet cancelButtonIndex])
return; return;