Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyxiao committed Apr 29, 2017
2 parents 3684a60 + e671b3d commit 3378779
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 60 deletions.
2 changes: 1 addition & 1 deletion Analytics.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Analytics"
s.version = "3.6.0"
s.version = "3.6.1-SNAPSHOT"
s.summary = "The hassle-free way to add analytics to your iOS app."

s.description = <<-DESC
Expand Down
2 changes: 1 addition & 1 deletion Analytics/Classes/Integrations/SEGIdentifyPayload.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface SEGIdentifyPayload : SEGPayload

@property (nonatomic, readonly) NSString *userId;
@property (nonatomic, readonly, nullable) NSString *userId;

@property (nonatomic, readonly, nullable) NSString *anonymousId;

Expand Down
4 changes: 2 additions & 2 deletions Analytics/Classes/Internal/SEGAnalyticsUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ void SEGLog(NSString *format, ...);

// JSON Utils

JSON_DICT SEGCoerceDictionary(NSDictionary *dict);
JSON_DICT SEGCoerceDictionary(NSDictionary * _Nullable dict);

NSString *SEGIDFA(void);
NSString * _Nullable SEGIDFA(void);

NSString *SEGEventNameForScreenTitle(NSString *title);

Expand Down
3 changes: 3 additions & 0 deletions Analytics/Classes/Internal/SEGHTTPClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ - (NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(NSString *
completionHandler(YES);
}];
[task resume];
[session finishTasksAndInvalidate];
return task;
}

Expand Down Expand Up @@ -140,6 +141,7 @@ - (NSURLSessionDataTask *)settingsForWriteKey:(NSString *)writeKey completionHan
completionHandler(YES, responseJson);
}];
[task resume];
[session finishTasksAndInvalidate];
return task;
}

Expand Down Expand Up @@ -200,6 +202,7 @@ - (NSURLSessionDataTask *)attributionWithWriteKey:(NSString *)writeKey forDevice
completionHandler(YES, responseJson);
}];
[task resume];
[session finishTasksAndInvalidate];
return task;
}

Expand Down
6 changes: 3 additions & 3 deletions Analytics/Classes/Internal/SEGReachability.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ typedef void (^SEGNetworkUnreachable)(SEGReachability *reachability);

@property (nonatomic, assign) BOOL reachableOnWWAN;

+ (SEGReachability *)reachabilityWithHostname:(NSString *)hostname;
+ (SEGReachability *)reachabilityForInternetConnection;
+ (SEGReachability *)reachabilityForLocalWiFi;
+ (SEGReachability * _Nullable)reachabilityWithHostname:(NSString *)hostname;
+ (SEGReachability * _Nullable)reachabilityForInternetConnection;
+ (SEGReachability * _Nullable)reachabilityForLocalWiFi;

- (SEGReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;

Expand Down
29 changes: 15 additions & 14 deletions Analytics/Classes/Internal/SEGSegmentIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)ht
}
return self;
}

- (void)setupFlushTimer {

- (void)setupFlushTimer
{
self.flushTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(flush) userInfo:nil repeats:YES];
}

Expand Down Expand Up @@ -174,17 +175,6 @@ - (NSDictionary *)staticContext
@"version" : device.systemVersion
};

#if TARGET_OS_IOS
static dispatch_once_t networkInfoOnceToken;
dispatch_once(&networkInfoOnceToken, ^{
_telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init];
});

CTCarrier *carrier = [_telephonyNetworkInfo subscriberCellularProvider];
if (carrier.carrierName.length)
dict[@"network"] = @{ @"carrier" : carrier.carrierName };
#endif

CGSize screenSize = [UIScreen mainScreen].bounds.size;
dict[@"screen"] = @{
@"width" : @(screenSize.width),
Expand Down Expand Up @@ -232,6 +222,17 @@ - (NSDictionary *)liveContext
network[@"cellular"] = @(self.reachability.isReachableViaWWAN);
}

#if TARGET_OS_IOS
static dispatch_once_t networkInfoOnceToken;
dispatch_once(&networkInfoOnceToken, ^{
_telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init];
});

CTCarrier *carrier = [_telephonyNetworkInfo subscriberCellularProvider];
if (carrier.carrierName.length)
network[@"carrier"] = carrier.carrierName;
#endif

network;
});

Expand Down Expand Up @@ -542,7 +543,7 @@ - (void)sendData:(NSArray *)batch
[self endBackgroundTask];
return;
}

[self.queue removeObjectsInArray:batch];
[self persistQueue];
[self notifyForName:SEGSegmentRequestDidSucceedNotification userInfo:batch];
Expand Down
7 changes: 7 additions & 0 deletions Analytics/Classes/Internal/SEGStoreKitTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProdu
#pragma mark - Track
- (void)trackTransaction:(SKPaymentTransaction *)transaction forProduct:(SKProduct *)product
{
// it seems the identifier is nil for renewable subscriptions
// see http://stackoverflow.com/questions/14827059/skpaymenttransactions-originaltransaction-transactionreceipt-nil-for-restore-on
// there isn't a spec'd event for this case ( https://segment.com/docs/spec/ecommerce/v2/ ) so ignoring it for now
if (transaction.transactionIdentifier == nil) {
return;
}

NSString *currency = [product.priceLocale objectForKey:NSLocaleCurrencyCode];

[self.analytics track:@"Order Completed" properties:@{
Expand Down
6 changes: 3 additions & 3 deletions Analytics/Classes/SEGAnalytics.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ NS_ASSUME_NONNULL_BEGIN
When you learn more about who your user is, you can record that information with identify.
*/
- (void)identify:(NSString *)userId traits:(SERIALIZABLE_DICT _Nullable)traits options:(SERIALIZABLE_DICT _Nullable)options;
- (void)identify:(NSString *)userId traits:(SERIALIZABLE_DICT _Nullable)traits;
- (void)identify:(NSString *)userId;
- (void)identify:(NSString * _Nullable)userId traits:(SERIALIZABLE_DICT _Nullable)traits options:(SERIALIZABLE_DICT _Nullable)options;
- (void)identify:(NSString * _Nullable)userId traits:(SERIALIZABLE_DICT _Nullable)traits;
- (void)identify:(NSString * _Nullable)userId;


/*!
Expand Down
64 changes: 43 additions & 21 deletions Analytics/Classes/SEGAnalytics.m
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ - (instancetype)initWithConfiguration:(SEGAnalyticsConfiguration *)configuration
_storeKitTracker = [SEGStoreKitTracker trackTransactionsForAnalytics:self];
}

[self trackApplicationLifecycleEvents:configuration.trackApplicationLifecycleEvents];

#if !TARGET_OS_TV
if (configuration.trackPushNotifications && configuration.launchOptions) {
NSDictionary *remoteNotification = configuration.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
Expand All @@ -99,13 +97,24 @@ - (void)dealloc
NSString *const SEGBuildKeyV1 = @"SEGBuildKey";
NSString *const SEGBuildKeyV2 = @"SEGBuildKeyV2";

- (void)trackApplicationLifecycleEvents:(BOOL)trackApplicationLifecycleEvents
- (void)handleAppStateNotification:(NSNotification *)note
{
if (!trackApplicationLifecycleEvents) {
return;
SEGApplicationLifecyclePayload *payload = [[SEGApplicationLifecyclePayload alloc] init];
payload.notificationName = note.name;
[self run:SEGEventTypeApplicationLifecycle payload:payload];

if ([note.name isEqualToString:UIApplicationDidFinishLaunchingNotification]) {
[self _applicationDidFinishLaunchingWithOptions:note.userInfo];
} else if ([note.name isEqualToString:UIApplicationWillEnterForegroundNotification]) {
[self _applicationWillEnterForeground];
}
}

// Previously SEGBuildKey was stored an integer. This was incorrect because the CFBundleVersion
- (void)_applicationDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (!self.configuration.trackApplicationLifecycleEvents) {
return;
}
// Previously SEGBuildKey was stored an integer. This was incorrect because the CFBundleVersion
// can be a string. This migrates SEGBuildKey to be stored as a string.
NSInteger previousBuildV1 = [[NSUserDefaults standardUserDefaults] integerForKey:SEGBuildKeyV1];
if (previousBuildV1) {
Expand All @@ -121,34 +130,47 @@ - (void)trackApplicationLifecycleEvents:(BOOL)trackApplicationLifecycleEvents

if (!previousBuildV2) {
[self track:@"Application Installed" properties:@{
@"version" : currentVersion,
@"build" : currentBuild
@"version" : currentVersion ?: [NSNull null],
@"build" : currentBuild ?: [NSNull null],
}];
} else if (![currentBuild isEqualToString:previousBuildV2]) {
[self track:@"Application Updated" properties:@{
@"previous_version" : previousVersion,
@"previous_build" : previousBuildV2,
@"version" : currentVersion,
@"build" : currentBuild
@"previous_version" : previousVersion ?: [NSNull null],
@"previous_build" : previousBuildV2 ?: [NSNull null],
@"version" : currentVersion ?: [NSNull null],
@"build" : currentBuild ?: [NSNull null],
}];
}

[self track:@"Application Opened" properties:@{
@"version" : currentVersion,
@"build" : currentBuild
@"from_background": @NO,
@"version" : currentVersion ?: [NSNull null],
@"build" : currentBuild ?: [NSNull null],
@"referring_application": launchOptions[UIApplicationLaunchOptionsSourceApplicationKey] ?: [NSNull null],
@"url": launchOptions[UIApplicationLaunchOptionsURLKey] ?: [NSNull null],
}];


[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:SEGVersionKey];
[[NSUserDefaults standardUserDefaults] setObject:currentBuild forKey:SEGBuildKeyV2];

[[NSUserDefaults standardUserDefaults] synchronize];
}

- (void)handleAppStateNotification:(NSNotification *)note
{
SEGApplicationLifecyclePayload *payload = [[SEGApplicationLifecyclePayload alloc] init];
payload.notificationName = note.name;
[self run:SEGEventTypeApplicationLifecycle payload:payload];
- (void)_applicationWillEnterForeground {
if (!self.configuration.trackApplicationLifecycleEvents) {
return;
}
NSString *currentVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"];
NSString *currentBuild = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"];
[self track:@"Application Opened" properties:@{
@"from_background": @YES,
@"version" : currentVersion ?: [NSNull null],
@"build" : currentBuild ?: [NSNull null],
}];
}


#pragma mark - Public API

- (NSString *)description
Expand Down Expand Up @@ -383,7 +405,7 @@ + (void)debug:(BOOL)showDebugLogs

+ (NSString *)version
{
return @"3.6.0";
return @"3.6.1-SNAPSHOT";
}

#pragma mark - Helpers
Expand Down
39 changes: 38 additions & 1 deletion AnalyticsTests/AnalyticsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ extension SEGSegmentIntegration {
}
}

class TestMiddleware: SEGMiddleware {
var lastContext: SEGContext?
var swallowEvent = false
func context(_ context: SEGContext, next: @escaping SEGMiddlewareNext) {
lastContext = context
if !swallowEvent {
next(context)
}
}
}


class AnalyticsTests: QuickSpec {
override func spec() {
Expand All @@ -46,11 +57,16 @@ class AnalyticsTests: QuickSpec {
"integrations": [
"Segment.io": ["apiKey": "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE"]
],
"plan": ["track": []],
"plan": ["track": [:]],
] as NSDictionary
var analytics: SEGAnalytics!
var testMiddleware: TestMiddleware!

beforeEach {
testMiddleware = TestMiddleware()
config.middlewares = [testMiddleware]
config.trackApplicationLifecycleEvents = true

analytics = SEGAnalytics(configuration: config)
analytics.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings)
}
Expand Down Expand Up @@ -90,6 +106,27 @@ class AnalyticsTests: QuickSpec {
let referrer = analytics.test_integrationsManager()?.test_segmentIntegration()?.test_referrer()
expect(referrer?["url"] as? String) == "http://www.segment.com"
}

it("fires Application Opened for UIApplicationDidFinishLaunching") {
testMiddleware.swallowEvent = true
NotificationCenter.default.post(name: .UIApplicationDidFinishLaunching, object: nil, userInfo: [
UIApplicationLaunchOptionsKey.sourceApplication: "testApp",
UIApplicationLaunchOptionsKey.url: "test://test",
])
let event = testMiddleware.lastContext?.payload as? SEGTrackPayload
expect(event?.event) == "Application Opened"
expect(event?.properties?["from_background"] as? Bool) == false
expect(event?.properties?["referring_application"] as? String) == "testApp"
expect(event?.properties?["url"] as? String) == "test://test"
}

it("fires Application Opened during UIApplicationWillEnterForeground") {
testMiddleware.swallowEvent = true
NotificationCenter.default.post(name: .UIApplicationWillEnterForeground, object: nil)
let event = testMiddleware.lastContext?.payload as? SEGTrackPayload
expect(event?.event) == "Application Opened"
expect(event?.properties?["from_background"] as? Bool) == true
}
}

}
14 changes: 6 additions & 8 deletions Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,33 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
[SEGAnalytics setupWithConfiguration:configuration];
[[SEGAnalytics sharedAnalytics] track:@"Cocoapods Example Launched"];
[[SEGAnalytics sharedAnalytics] flush];
NSLog(@"application:didFinishLaunchingWithOptions: %@", launchOptions);
return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
NSLog(@"applicationWillResignActive:");
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(@"applicationDidEnterBackground:");
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
NSLog(@"applicationWillEnterForeground:");
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog(@"applicationDidBecomeActive:");
}


- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSLog(@"applicationWillTerminate:");
}


@end
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
Expand Down
Loading

0 comments on commit 3378779

Please sign in to comment.