From a2269dccf34e239fbda4ccf87c7dbca84d0ae162 Mon Sep 17 00:00:00 2001 From: Vijaye Raji Date: Wed, 1 Aug 2012 13:34:03 -0700 Subject: [PATCH] Putting back, again, the attribute code Summary: This reverts commit c89fea45e7b1d6e7046de27d0dca6f501356782c. Based on new information, we're putting back the app attribution code into the SDK. Test Plan: Build + run unit tests Reviewers: jacl, mmarucheck, clang, jketchpaw Reviewed By: jketchpaw CC: msdkexp@ Differential Revision: https://phabricator.fb.com/D536848 --- src/FBSession.m | 3 + src/FBSettings+Internal.h | 23 ++++ src/FBSettings.h | 28 ++++- src/FBSettings.m | 104 ++++++++++++++++++ src/FBURLConnection.m | 5 + src/FBUtility.h | 3 +- src/FBUtility.m | 4 + .../project.pbxproj | 4 + 8 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 src/FBSettings+Internal.h diff --git a/src/FBSession.m b/src/FBSession.m index 19b5d7d4f5..b7b7e5d766 100644 --- a/src/FBSession.m +++ b/src/FBSession.m @@ -21,6 +21,7 @@ #import "FBSession+Protected.h" #import "FBSessionTokenCachingStrategy.h" #import "FBSettings.h" +#import "FBSettings+Internal.h" #import "FBError.h" #import "FBLogger.h" #import "FBUtility.h" @@ -264,6 +265,8 @@ - (id)initWithAppID:(NSString*)appID [tokenCachingStrategy clearToken]; } } + + [FBSettings autoPublishInstall:self.appID]; } return self; } diff --git a/src/FBSettings+Internal.h b/src/FBSettings+Internal.h new file mode 100644 index 0000000000..8ea155e0bd --- /dev/null +++ b/src/FBSettings+Internal.h @@ -0,0 +1,23 @@ +/* + * Copyright 2012 Facebook + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FBSettings.h" + +@interface FBSettings (Internal) + ++ (void)autoPublishInstall:(NSString *)appID; + +@end diff --git a/src/FBSettings.h b/src/FBSettings.h index 1a8960b6e0..898c674e16 100644 --- a/src/FBSettings.h +++ b/src/FBSettings.h @@ -47,12 +47,36 @@ extern NSString *const FBLoggingBehaviorPerformanceCharacteristics; /*! @method - + @abstract Set the current Facebook SDK logging behavior. This should consist of strings defined as constants with FBLogBehavior*, and can be constructed with [NSSet initWithObjects:]. - + @param loggingBehavior A set of strings indicating what information should be logged. */ + (void)setLoggingBehavior:(NSSet *)loggingBehavior; +/*! @abstract Retreive the current auto publish behavior. Defaults to YES. */ ++ (BOOL)shouldAutoPublishInstall; + +/*! + @method + + @abstract Sets whether the SDK will automatically publish an install to Facebook during first FBSession init + or on first network request to Facebook. + */ ++ (void)setShouldAutoPublishInstall:(BOOL)autoPublishInstall; + +// For best results, call this function during app activation. +/*! + @method + + @abstract Manually publish an attributed install to the facebook graph. Use this method if you have disabled + auto publish and wish to manually send an install from your code. This method acquires the current attribution + id from the facebook application, queries the graph API to determine if the application has install + attribution enabled, publishes the id, and records success to avoid reporting more than once. + + @param appID A specific appID to publish an install for. If nil, uses [FBSession defaultAppID]. + */ ++ (void) publishInstall:(NSString *)appID; + @end diff --git a/src/FBSettings.m b/src/FBSettings.m index 7f46ea964b..2c6ec6f080 100644 --- a/src/FBSettings.m +++ b/src/FBSettings.m @@ -14,7 +14,12 @@ * limitations under the License. */ +#import "FBRequest.h" +#import "FBSession.h" #import "FBSettings.h" +#import "FBSettings+Internal.h" + +#import NSString *const FBLoggingBehaviorFBRequests = @"fb_requests"; NSString *const FBLoggingBehaviorFBURLConnections = @"fburl_connections"; @@ -22,9 +27,28 @@ NSString *const FBLoggingBehaviorSessionStateTransitions = @"state_transitions"; NSString *const FBLoggingBehaviorPerformanceCharacteristics = @"perf_characteristics"; +NSString *const FBLastAttributionPing = @"com.facebook.sdk:lastAttributionPing%@"; +NSString *const FBSupportsAttributionPath = @"%@?fields=supports_attribution"; +NSString *const FBPublishActivityPath = @"%@/activities"; +NSString *const FBMobileInstallEvent = @"MOBILE_APP_INSTALL"; +NSString *const FBAttributionPasteboard = @"fb_app_attribution"; +NSString *const FBSupportsAttribution = @"supports_attribution"; + +NSTimeInterval const FBPublishDelay = 0.1; + +@protocol FBActivity + +@property (retain, nonatomic) NSString *event; +@property (retain, nonatomic) NSString *attribution; + +@end + + @implementation FBSettings static NSSet *g_loggingBehavior; +static BOOL g_autoPublishInstall = YES; +static dispatch_once_t g_publishInstallOnceToken; + (NSSet *)loggingBehavior { return g_loggingBehavior; @@ -36,4 +60,84 @@ + (void)setLoggingBehavior:(NSSet *)newValue { g_loggingBehavior = newValue; } ++ (BOOL)shouldAutoPublishInstall { + return g_autoPublishInstall; +} + ++ (void)setShouldAutoPublishInstall:(BOOL)newValue { + g_autoPublishInstall = newValue; +} + ++ (void)autoPublishInstall:(NSString *)appID { + if ([FBSettings shouldAutoPublishInstall]) { + dispatch_once(&g_publishInstallOnceToken, ^{ + // dispatch_once is great, but not re-entrant. Inside publishInstall we use FBRequest, which will + // cause this function to get invoked a second time. By scheduling the work, we can sidestep the problem. + [[FBSettings class] performSelector:@selector(publishInstall:) withObject:appID afterDelay:FBPublishDelay]; + }); + } +} + + +#pragma mark - +#pragma mark proto-activity publishing code + ++ (void)publishInstall:(NSString *)appID { + if (!appID) { + appID = [FBSession defaultAppID]; + } + + if (!appID) { + // if the appID is still nil, exit early. + return; + } + + // look for a previous ping & grab the facebook app's current attribution id. + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *pingKey = [NSString stringWithFormat:FBLastAttributionPing, appID, nil]; + NSDate *lastPing = [defaults objectForKey:pingKey]; + NSString *attributionID = [[UIPasteboard pasteboardWithName:FBAttributionPasteboard create:NO] string]; + + if (attributionID && !lastPing) { + FBRequestHandler publishCompletionBlock = ^(FBRequestConnection *connection, + id result, + NSError *error) { + if (!error) { + // if server communication was successful, take note of the current time. + [defaults setObject:[NSDate date] forKey:pingKey]; + [defaults synchronize]; + } else { + // there was a problem. allow a repeat execution. + g_publishInstallOnceToken = 0; + } + }; + + FBRequestHandler pingCompletionBlock = ^(FBRequestConnection *connection, + id result, + NSError *error) { + if (!error) { + if ([result respondsToSelector:@selector(objectForKey:)] && + [[result objectForKey:FBSupportsAttribution] boolValue]) { + // set up the HTTP POST to publish the attribution ID. + NSString *publishPath = [NSString stringWithFormat:FBPublishActivityPath, appID, nil]; + id installActivity = (id)[FBGraphObject graphObject]; + installActivity.event = FBMobileInstallEvent; + installActivity.attribution = attributionID; + + FBRequest *publishRequest = [[[FBRequest alloc] initForPostWithSession:nil graphPath:publishPath graphObject:installActivity] autorelease]; + [publishRequest startWithCompletionHandler:publishCompletionBlock]; + } else { + // the app has turned off install insights. prevent future attempts. + [defaults setObject:[NSDate date] forKey:pingKey]; + [defaults synchronize]; + } + } + }; + + NSString *pingPath = [NSString stringWithFormat:FBSupportsAttributionPath, appID, nil]; + FBRequest *pingRequest = [[[FBRequest alloc] initWithSession:nil graphPath:pingPath] autorelease]; + [pingRequest startWithCompletionHandler:pingCompletionBlock]; + } +} + @end diff --git a/src/FBURLConnection.m b/src/FBURLConnection.m index 83cc149eb1..2aa2fc7a79 100644 --- a/src/FBURLConnection.m +++ b/src/FBURLConnection.m @@ -21,6 +21,7 @@ #import "FBLogger.h" #import "FBUtility.h" #import "FBSettings.h" +#import "FBSettings+Internal.h" static NSArray* _cdnHosts; @@ -111,6 +112,10 @@ - (FBURLConnection *)initWithRequest:(NSURLRequest *)request self.handler = handler; } + + // always attempt to autoPublish. this function internally + // handles only executing once. + [FBSettings autoPublishInstall:nil]; } return self; } diff --git a/src/FBUtility.h b/src/FBUtility.h index d3fb8ba081..a3463b1562 100644 --- a/src/FBUtility.h +++ b/src/FBUtility.h @@ -26,7 +26,8 @@ + (NSString*)stringByURLEncodingString:(NSString*)unescapedString; + (id)graphObjectInArray:(NSArray*)array withSameIDAs:(id)item; -+ (unsigned long)currentTimeInMilliseconds; ++ (unsigned long)currentTimeInMilliseconds; ++ (NSTimeInterval)randomTimeInterval:(NSTimeInterval)minValue withMaxValue:(NSTimeInterval)maxValue; + (void)centerView:(UIView*)view tableView:(UITableView*)tableView; + (NSString *)stringFBIDFromObject:(id)object; diff --git a/src/FBUtility.m b/src/FBUtility.m index 29731adab7..23f2408d06 100644 --- a/src/FBUtility.m +++ b/src/FBUtility.m @@ -75,6 +75,10 @@ + (unsigned long)currentTimeInMilliseconds { return (time.tv_sec * 1000) + (time.tv_usec / 1000); } ++ (NSTimeInterval)randomTimeInterval:(NSTimeInterval)minValue withMaxValue:(NSTimeInterval)maxValue { + return minValue + (maxValue - minValue) * (double)arc4random() / UINT32_MAX; +} + + (id)graphObjectInArray:(NSArray*)array withSameIDAs:(id)item { for (id obj in array) { if ([FBGraphObject isGraphObjectID:obj sameAs:item]) { diff --git a/src/facebook-ios-sdk.xcodeproj/project.pbxproj b/src/facebook-ios-sdk.xcodeproj/project.pbxproj index 379c02e4ef..966affd28a 100644 --- a/src/facebook-ios-sdk.xcodeproj/project.pbxproj +++ b/src/facebook-ios-sdk.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 2A68590615C1E37E001D4EDD /* FBSettings+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A68590515C1E37E001D4EDD /* FBSettings+Internal.h */; }; 5F7CB4201553ACC600C183CF /* FBLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F7CB41E1553ACC600C183CF /* FBLogger.h */; }; 5F7CB4211553ACC600C183CF /* FBLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7CB41F1553ACC600C183CF /* FBLogger.m */; }; 8409694C1541F41100479AD9 /* FBOpenGraphAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 8409694B1541F41100479AD9 /* FBOpenGraphAction.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -172,6 +173,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 2A68590515C1E37E001D4EDD /* FBSettings+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSettings+Internal.h"; sourceTree = ""; }; 5F7CB41E1553ACC600C183CF /* FBLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBLogger.h; sourceTree = ""; }; 5F7CB41F1553ACC600C183CF /* FBLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBLogger.m; sourceTree = ""; }; 8409694B1541F41100479AD9 /* FBOpenGraphAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBOpenGraphAction.h; sourceTree = ""; }; @@ -438,6 +440,7 @@ 84137156152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.h */, 84137157152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.m */, DDB7C34A15A6181100C8DCE6 /* FBSettings.h */, + 2A68590515C1E37E001D4EDD /* FBSettings+Internal.h */, DDB7C34B15A6181100C8DCE6 /* FBSettings.m */, 8525A5B8156F2049009F6F3F /* FBTestSession.h */, 85F29E9315785D72001F0531 /* FBTestSession+Internal.h */, @@ -582,6 +585,7 @@ DDB7C34C15A6181100C8DCE6 /* FBSettings.h in Headers */, 85E4AC7715B63CB600F17346 /* FBUserSettingsViewController.h in Headers */, 85E4AC7D15B63CC500F17346 /* FBViewController.h in Headers */, + 2A68590615C1E37E001D4EDD /* FBSettings+Internal.h in Headers */, 85F99E3F15C3751100D807A5 /* FBViewController+Internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0;