adding pods method of package managing

This commit is contained in:
talksik
2021-12-13 12:34:20 -08:00
parent dad674aca7
commit 705203d7bd
5871 changed files with 1259393 additions and 3 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,33 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKMetadataIndexer.h"
#import "FBSDKMetadataIndexing.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKMetadataIndexer (MetadataIndexing) <FBSDKMetadataIndexing>
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,37 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(MetadataIndexer)
@interface FBSDKMetadataIndexer : NSObject
@property (class, nonatomic, readonly) FBSDKMetadataIndexer *shared;
- (void)enable;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,385 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKMetadataIndexer.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import <sys/sysctl.h>
#import <sys/utsname.h>
#import "FBSDKAppEventsUtility.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKServerConfigurationManager.h"
#import "FBSDKSwizzler.h"
#import "FBSDKUtility.h"
#import "FBSDKViewHierarchy.h"
@interface FBSDKUserDataStore (Internal)
+ (void)setInternalHashData:(nullable NSString *)hashData
forType:(FBSDKAppEventUserDataType)type;
+ (void)setEnabledRules:(NSArray<NSString *> *)rules;
+ (nullable NSString *)getInternalHashedDataForType:(FBSDKAppEventUserDataType)type;
@end
static const int FBSDKMetadataIndexerMaxTextLength = 100;
static const int FBSDKMetadataIndexerMaxIndicatorLength = 100;
static const int FBSDKMetadataIndexerMaxValue = 5;
static NSString *const FIELD_K = @"k";
static NSString *const FIELD_V = @"v";
static NSString *const FIELD_K_DELIMITER = @",";
@interface FBSDKMetadataIndexer ()
@property (nonatomic, readonly, strong) NSMutableDictionary<NSString *, NSDictionary<NSString *, NSString *> *> *rules;
@property (nonatomic, readonly, strong) NSMutableDictionary<NSString *, NSMutableArray<NSString *> *> *store;
@property (nonatomic, readonly, strong) dispatch_queue_t serialQueue;
@end
@implementation FBSDKMetadataIndexer
+ (instancetype)shared
{
static dispatch_once_t nonce;
static FBSDKMetadataIndexer *instance;
dispatch_once(&nonce, ^{
instance = [self new];
});
return instance;
}
- (instancetype)init
{
_rules = [NSMutableDictionary new];
_serialQueue = dispatch_queue_create("com.facebook.appevents.MetadataIndexer", DISPATCH_QUEUE_SERIAL);
return self;
}
- (void)enable
{
@try {
if ([FBSDKAppEventsUtility shouldDropAppEvent]) {
return;
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSDictionary<NSString *, id> *AAMRules = FBSDKServerConfigurationManager.shared.cachedServerConfiguration.AAMRules;
if (AAMRules) {
[self setupWithRules:AAMRules];
}
});
} @catch (NSException *exception) {
NSLog(@"Fail to enable Automatic Advanced Matching, exception reason: %@", exception.reason);
}
}
- (void)setupWithRules:(NSDictionary<NSString *, id> *_Nullable)rules
{
if (0 == rules.count) {
return;
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self constructRules:rules];
[self initStore];
BOOL isEnabled = NO;
for (NSString *key in _rules) {
if (_rules[key]) {
isEnabled = YES;
break;
}
}
if (isEnabled) {
[FBSDKUserDataStore setEnabledRules:_rules.allKeys];
[self setupMetadataIndexing];
}
});
}
- (void)initStore
{
_store = [NSMutableDictionary new];
for (NSString *key in _rules) {
NSString *data = [FBSDKUserDataStore getInternalHashedDataForType:key];
if (data.length > 0) {
[FBSDKTypeUtility dictionary:_store setObject:[NSMutableArray arrayWithArray:[data componentsSeparatedByString:FIELD_K_DELIMITER]] forKey:key];
}
}
for (NSString *key in _rules) {
if (!_store[key]) {
[FBSDKTypeUtility dictionary:_store setObject:[NSMutableArray new] forKey:key];
}
}
}
- (void)constructRules:(NSDictionary<NSString *, id> *_Nullable)rules
{
for (NSString *key in rules) {
NSDictionary<NSString *, NSString *> *value = [FBSDKTypeUtility dictionaryValue:rules[key]];
if (value[FIELD_K].length > 0 && value[FIELD_V]) {
[FBSDKTypeUtility dictionary:_rules setObject:value forKey:key];
}
}
}
- (void)setupMetadataIndexing
{
void (^block)(UIView *) = ^(UIView *view) {
// Indexing when the view is removed from window and conforms to UITextInput, and skip UIFieldEditor, which is an internval view of UITextField
if (![view window] && ![NSStringFromClass([view class]) isEqualToString:@"UIFieldEditor"] && [view conformsToProtocol:@protocol(UITextInput)]) {
NSString *text = [FBSDKViewHierarchy getText:view];
NSString *placeholder = [FBSDKViewHierarchy getHint:view];
BOOL secureTextEntry = [self checkSecureTextEntry:view];
NSArray<NSString *> *labels = [self getLabelsOfView:view];
UIKeyboardType keyboardType = [self getKeyboardType:view];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[self getMetadataWithText:text
placeholder:placeholder
labels:labels
secureTextEntry:secureTextEntry
inputType:keyboardType];
});
}
};
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:[UIView class] withBlock:block named:@"metadataIndexingUIView"];
// iOS 12: UITextField implements didMoveToWindow without calling parent implementation
if (@available(iOS 12, *)) {
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:[UITextField class] withBlock:block named:@"metadataIndexingUITextField"];
} else {
[FBSDKSwizzler swizzleSelector:@selector(didMoveToWindow) onClass:[UIControl class] withBlock:block named:@"metadataIndexingUIControl"];
}
}
- (NSArray<UIView *> *)getSiblingViewsOfView:(UIView *)view
{
NSObject *parent = [FBSDKViewHierarchy getParent:view];
if (parent) {
NSArray<id> *views = [FBSDKViewHierarchy getChildren:parent];
if (views) {
NSMutableArray<id> *siblings = [NSMutableArray arrayWithArray:views];
[siblings removeObject:view];
return [siblings copy];
}
}
return nil;
}
- (NSArray<NSString *> *)getLabelsOfView:(UIView *)view
{
NSMutableArray<NSString *> *labels = [NSMutableArray new];
NSString *placeholder = [self normalizeField:[FBSDKViewHierarchy getHint:view]];
if (placeholder.length > 0) {
[FBSDKTypeUtility array:labels addObject:placeholder];
}
NSArray<id> *siblingViews = [self getSiblingViewsOfView:view];
for (id sibling in siblingViews) {
if ([sibling isKindOfClass:[UILabel class]]) {
NSString *text = [self normalizeField:[FBSDKViewHierarchy getText:sibling]];
if (text.length > 0) {
[FBSDKTypeUtility array:labels addObject:text];
}
}
}
return [labels copy];
}
- (BOOL)checkSecureTextEntry:(UIView *)view
{
if ([view isKindOfClass:[UITextField class]]) {
return ((UITextField *)view).secureTextEntry;
}
if ([view isKindOfClass:[UITextView class]]) {
return ((UITextView *)view).secureTextEntry;
}
return NO;
}
- (UIKeyboardType)getKeyboardType:(UIView *)view
{
if ([view isKindOfClass:[UITextField class]]) {
return ((UITextField *)view).keyboardType;
}
if ([view isKindOfClass:[UITextView class]]) {
return ((UITextView *)view).keyboardType;
}
return UIKeyboardTypeDefault;
}
- (void)getMetadataWithText:(NSString *)text
placeholder:(NSString *)placeholder
labels:(NSArray<NSString *> *)labels
secureTextEntry:(BOOL)secureTextEntry
inputType:(UIKeyboardType)inputType
{
text = [self normalizeValue:text];
placeholder = [self normalizeField:placeholder];
if (secureTextEntry || [placeholder containsString:@"password"]
|| text.length == 0
|| text.length > FBSDKMetadataIndexerMaxTextLength
|| placeholder.length >= FBSDKMetadataIndexerMaxIndicatorLength) {
return;
}
for (NSString *key in _rules) {
NSDictionary<NSString *, NSString *> *rule = _rules[key];
BOOL isRuleKMatched = [self checkMetadataHint:placeholder matchRuleK:rule[FIELD_K]]
|| [self checkMetadataLabels:labels matchRuleK:rule[FIELD_K]];
if (!isRuleKMatched) {
continue;
}
NSString *preProcessedText = text;
if ([key isEqualToString:@"r2"]) {
preProcessedText = [[text componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"+- ()."]] componentsJoinedByString:@""];
}
BOOL isRuleVMatched = [rule[FIELD_V] isEqualToString:@""] || [self checkMetadataText:preProcessedText matchRuleV:rule[FIELD_V]];
if (isRuleVMatched) {
NSString *prunedText = [self pruneValue:preProcessedText forKey:key];
[self checkAndAppendData:prunedText forKey:key];
continue;
}
}
}
#pragma mark - Helper Methods
- (void)checkAndAppendData:(NSString *)data
forKey:(NSString *)key
{
NSString *hashData = [FBSDKUtility SHA256Hash:data];
__weak typeof(_store) weakStore = _store;
dispatch_block_t checkAndAppendDataBlock = ^{
if (hashData.length == 0 || [weakStore[key] containsObject:hashData]) {
return;
}
while (weakStore[key].count >= FBSDKMetadataIndexerMaxValue) {
[weakStore[key] removeObjectAtIndex:0];
}
[FBSDKTypeUtility array:weakStore[key] addObject:hashData];
[FBSDKUserDataStore setInternalHashData:[weakStore[key] componentsJoinedByString:FIELD_K_DELIMITER]
forType:key];
};
#if FBTEST
checkAndAppendDataBlock();
#else
dispatch_async(_serialQueue, checkAndAppendDataBlock);
#endif
}
- (BOOL)checkMetadataLabels:(NSArray<NSString *> *)labels
matchRuleK:(NSString *)ruleK
{
for (NSString *label in labels) {
if ([self checkMetadataHint:label matchRuleK:ruleK]) {
return YES;
}
}
return NO;
}
- (BOOL)checkMetadataHint:(NSString *)hint
matchRuleK:(NSString *)ruleK
{
if (hint.length > 0 && ruleK) {
NSArray<NSString *> *items = [ruleK componentsSeparatedByString:FIELD_K_DELIMITER];
for (NSString *item in items) {
if ([hint containsString:item]) {
return YES;
}
}
}
return NO;
}
- (BOOL)checkMetadataText:(NSString *)text
matchRuleV:(NSString *)ruleV
{
if (text.length > 0 && ruleV) {
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:ruleV
options:NSRegularExpressionCaseInsensitive
error:nil];
return [regex numberOfMatchesInString:text options:0 range:NSMakeRange(0, text.length)] == 1;
}
return NO;
}
- (NSString *)normalizeField:(NSString *)field
{
if (field.length == 0) {
return @"";
}
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[_-]|\\s"
options:NSRegularExpressionCaseInsensitive
error:nil];
return [regex stringByReplacingMatchesInString:field
options:0
range:NSMakeRange(0, field.length)
withTemplate:@""].lowercaseString;
}
- (NSString *)normalizeValue:(NSString *)value
{
if (value.length == 0) {
return @"";
}
return [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].lowercaseString;
}
- (NSString *)pruneValue:(NSString *)value forKey:(NSString *)key
{
if (value.length == 0) {
return @"";
}
if ([key isEqualToString:@"r3"]) {
if ([value hasPrefix:@"m"] || [value hasPrefix:@"b"] || [value hasPrefix:@"ge"]) {
value = @"m";
} else {
value = @"f";
}
} else if ([key isEqualToString:@"r4"] || [key isEqualToString:@"r5"]) {
value = [[value componentsSeparatedByCharactersInSet:[[NSCharacterSet letterCharacterSet] invertedSet]] componentsJoinedByString:@""];
} else if ([key isEqualToString:@"r6"]) {
value = [FBSDKTypeUtility array:[value componentsSeparatedByString:@"-"] objectAtIndex:0];
}
return value;
}
@end
#endif
@@ -0,0 +1,36 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(MetadataIndexing)
@protocol FBSDKMetadataIndexing
- (void)enable;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,42 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
#import "FBSDKCoreKitAEMImport.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(SDKAEMNetworker)
@interface FBSDKAEMNetworker : NSObject <FBAEMNetworking>
- (void)startGraphRequestWithGraphPath:(NSString *)graphPath
parameters:(NSDictionary *)parameters
tokenString:(nullable NSString *)tokenString
HTTPMethod:(nullable NSString *)method
completion:(FBGraphRequestCompletion)completion;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,50 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKAEMNetworker.h"
#import <Foundation/Foundation.h>
#import "FBSDKGraphRequest+Internal.h"
#import "FBSDKGraphRequestFlags.h"
@implementation FBSDKAEMNetworker
- (void)startGraphRequestWithGraphPath:(NSString *)graphPath
parameters:(NSDictionary *)parameters
tokenString:(nullable NSString *)tokenString
HTTPMethod:(nullable NSString *)method
completion:(FBGraphRequestCompletion)completion
{
id<FBSDKGraphRequest> graphRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath
parameters:parameters
tokenString:tokenString
HTTPMethod:method
flags:FBSDKGraphRequestFlagSkipClientToken | FBSDKGraphRequestFlagDisableErrorRecovery];
[graphRequest startWithCompletion:^(id<FBSDKGraphRequestConnecting> _Nullable connection, id _Nullable result, NSError *_Nullable error) {
completion(result, error);
}];
}
@end
#endif
@@ -0,0 +1,47 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
// TODO: Can these all be forward decls?
#import "FBSDKAdvertiserIDProviding.h"
#import "FBSDKCodelessIndexer.h"
#import "FBSDKDataPersisting.h"
#import "FBSDKGraphRequestConnectionProviding.h"
#import "FBSDKGraphRequestProviding.h"
#import "FBSDKServerConfigurationProviding.h"
#import "FBSDKSettingsProtocol.h"
#import "FBSDKSwizzling.h"
@interface FBSDKCodelessIndexer (Internal)
+ (void)configureWithRequestProvider:(id<FBSDKGraphRequestProviding>)requestProvider
serverConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider
store:(id<FBSDKDataPersisting>)store
connectionProvider:(id<FBSDKGraphRequestConnectionProviding>)connectionProvider
swizzler:(Class<FBSDKSwizzling>)swizzler
settings:(id<FBSDKSettings>)settings
advertiserIDProvider:(id<FBSDKAdvertiserIDProviding>)advertisingIDProvider;
@end
#endif
@@ -0,0 +1,40 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^FBSDKCodelessSettingLoadBlock)(BOOL isCodelessSetupEnabled, NSError *_Nullable error);
NS_SWIFT_NAME(CodelessIndexer)
@interface FBSDKCodelessIndexer : NSObject
@property (class, nonatomic, copy, readonly) NSString *extInfo;
+ (void)enable;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,551 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKCodelessIndexer.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import <sys/sysctl.h>
#import <sys/utsname.h>
#import "FBSDKAdvertiserIDProviding.h"
#import "FBSDKAppEventsUtility.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKDataPersisting.h"
#import "FBSDKGraphRequestConnecting.h"
#import "FBSDKGraphRequestConnectionProviding.h"
#import "FBSDKGraphRequestHTTPMethod.h"
#import "FBSDKGraphRequestProtocol.h"
#import "FBSDKGraphRequestProviding.h"
#import "FBSDKInternalUtility+Internal.h"
#import "FBSDKObjectDecoding.h"
#import "FBSDKServerConfiguration.h"
#import "FBSDKServerConfigurationManager.h"
#import "FBSDKServerConfigurationProviding.h"
#import "FBSDKSettings+Internal.h"
#import "FBSDKSettingsProtocol.h"
#import "FBSDKSwizzling.h"
#import "FBSDKUnarchiverProvider.h"
#import "FBSDKUtility.h"
#import "FBSDKViewHierarchy.h"
#import "FBSDKViewHierarchyMacros.h"
@interface FBSDKCodelessIndexer ()
@property (class, nullable, nonatomic, readonly) id<FBSDKGraphRequestProviding> requestProvider;
@property (class, nullable, nonatomic, readonly) id<FBSDKServerConfigurationProviding> serverConfigurationProvider;
@property (class, nullable, nonatomic, readonly) id<FBSDKDataPersisting> store;
@property (class, nullable, nonatomic, readonly, copy) id<FBSDKGraphRequestConnectionProviding> connectionProvider;
@property (class, nullable, nonatomic, readonly, copy) Class<FBSDKSwizzling> swizzler;
@property (class, nullable, nonatomic, readonly) id<FBSDKSettings> settings;
@property (class, nullable, nonatomic, readonly) id<FBSDKAdvertiserIDProviding> advertiserIDProvider;
@end
#if FBSDK_SWIFT_PACKAGE
NS_EXTENSION_UNAVAILABLE("The Facebook iOS SDK is not currently supported in extensions")
#endif
@implementation FBSDKCodelessIndexer
static BOOL _isCodelessIndexing;
static BOOL _isCheckingSession;
static BOOL _isCodelessIndexingEnabled;
static BOOL _isGestureSet;
static NSMutableDictionary<NSString *, id> *_codelessSetting;
static const NSTimeInterval kTimeout = 4.0;
static NSString *_deviceSessionID;
static NSTimer *_appIndexingTimer;
static NSString *_lastTreeHash;
static id<FBSDKGraphRequestProviding> _requestProvider;
static id<FBSDKServerConfigurationProviding> _serverConfigurationProvider;
static id<FBSDKDataPersisting> _store;
static id<FBSDKGraphRequestConnectionProviding> _connectionProvider;
static Class<FBSDKSwizzling> _swizzler;
static id<FBSDKSettings> _settings;
static id<FBSDKAdvertiserIDProviding> _advertiserIDProvider;
static id<FBSDKSettings> _settings;
+ (void)configureWithRequestProvider:(id<FBSDKGraphRequestProviding>)requestProvider
serverConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider
store:(id<FBSDKDataPersisting>)store
connectionProvider:(id<FBSDKGraphRequestConnectionProviding>)connectionProvider
swizzler:(Class<FBSDKSwizzling>)swizzler
settings:(id<FBSDKSettings>)settings
advertiserIDProvider:(id<FBSDKAdvertiserIDProviding>)advertiserIDProvider
{
if (self == [FBSDKCodelessIndexer class]) {
_requestProvider = requestProvider;
_serverConfigurationProvider = serverConfigurationProvider;
_store = store;
_connectionProvider = connectionProvider;
_swizzler = swizzler;
_settings = settings;
_advertiserIDProvider = advertiserIDProvider;
}
}
+ (id<FBSDKGraphRequestProviding>)requestProvider
{
return _requestProvider;
}
+ (id<FBSDKServerConfigurationProviding>)serverConfigurationProvider
{
return _serverConfigurationProvider;
}
+ (id<FBSDKDataPersisting>)store
{
return _store;
}
+ (id<FBSDKGraphRequestConnectionProviding>)connectionProvider
{
return _connectionProvider;
}
+ (Class<FBSDKSwizzling>)swizzler
{
return _swizzler;
}
+ (id<FBSDKSettings>)settings
{
return _settings;
}
+ (id<FBSDKAdvertiserIDProviding>)advertiserIDProvider
{
return _advertiserIDProvider;
}
+ (void)enable
{
if (_isGestureSet) {
return;
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if TARGET_OS_SIMULATOR
[self setupGesture];
#else
[self loadCodelessSettingWithCompletionBlock:^(BOOL isCodelessSetupEnabled, NSError *error) {
if (isCodelessSetupEnabled) {
[self setupGesture];
}
}];
#endif
});
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// DO NOT call this function, it is only called once in the enable function
+ (void)loadCodelessSettingWithCompletionBlock:(FBSDKCodelessSettingLoadBlock)completionBlock
{
NSString *appID = [self.settings appID];
if (appID == nil) {
return;
}
[self.serverConfigurationProvider loadServerConfigurationWithCompletionBlock:^(FBSDKServerConfiguration *serverConfiguration, NSError *serverConfigurationLoadingError) {
if (!serverConfiguration.isCodelessEventsEnabled) {
return;
}
// load the defaults
NSString *defaultKey = [NSString stringWithFormat:CODELESS_SETTING_KEY, appID];
NSData *data = [self.store objectForKey:defaultKey];
if ([data isKindOfClass:[NSData class]]) {
NSMutableDictionary<NSString *, id> *codelessSetting = nil;
id<FBSDKObjectDecoding> unarchiver = [FBSDKUnarchiverProvider createInsecureUnarchiverFor:data];
@try {
codelessSetting = [unarchiver decodeObjectOfClass:NSDictionary.class forKey:NSKeyedArchiveRootObjectKey];
} @catch (NSException *ex) {
// ignore decoding exceptions
}
if (codelessSetting) {
_codelessSetting = codelessSetting;
}
}
if (
_codelessSetting
&& [self _codelessSetupTimestampIsValid:[FBSDKTypeUtility dictionary:_codelessSetting objectForKey:CODELESS_SETTING_TIMESTAMP_KEY ofType:NSObject.class]]
) {
completionBlock([FBSDKTypeUtility boolValue:[FBSDKTypeUtility dictionary:_codelessSetting objectForKey:CODELESS_SETUP_ENABLED_KEY ofType:NSObject.class]], nil);
} else {
_codelessSetting = [NSMutableDictionary new];
id<FBSDKGraphRequest> request = [self requestToLoadCodelessSetup:appID];
if (request == nil) {
return;
}
id<FBSDKGraphRequestConnecting> requestConnection = [self.connectionProvider createGraphRequestConnection];
requestConnection.timeout = kTimeout;
[requestConnection addRequest:request completion:^(id<FBSDKGraphRequestConnecting> connection, id result, NSError *codelessLoadingError) {
if (codelessLoadingError) {
return;
}
NSDictionary<NSString *, id> *resultDictionary = [FBSDKTypeUtility dictionaryValue:result];
if (resultDictionary) {
BOOL isCodelessSetupEnabled = [FBSDKTypeUtility boolValue:resultDictionary[CODELESS_SETUP_ENABLED_FIELD]];
[FBSDKTypeUtility dictionary:_codelessSetting setObject:@(isCodelessSetupEnabled) forKey:CODELESS_SETUP_ENABLED_KEY];
[FBSDKTypeUtility dictionary:_codelessSetting setObject:[NSDate date] forKey:CODELESS_SETTING_TIMESTAMP_KEY];
// update the cached copy in user defaults
[self.store setObject:[NSKeyedArchiver archivedDataWithRootObject:_codelessSetting] forKey:defaultKey];
completionBlock(isCodelessSetupEnabled, codelessLoadingError);
}
}];
[requestConnection start];
}
}];
}
#pragma clang diagnostic pop
+ (id<FBSDKGraphRequest>)requestToLoadCodelessSetup:(NSString *)appID
{
NSString *advertiserID = self.advertiserIDProvider.advertiserID;
if (!advertiserID) {
return nil;
}
NSDictionary<NSString *, NSString *> *parameters = @{
@"fields" : CODELESS_SETUP_ENABLED_FIELD,
@"advertiser_id" : advertiserID
};
id<FBSDKGraphRequest> request = [self.requestProvider createGraphRequestWithGraphPath:appID
parameters:parameters
tokenString:nil
HTTPMethod:nil
flags:FBSDKGraphRequestFlagSkipClientToken | FBSDKGraphRequestFlagDisableErrorRecovery];
return request;
}
+ (BOOL)_codelessSetupTimestampIsValid:(NSDate *)timestamp
{
return (timestamp != nil && [[NSDate date] timeIntervalSinceDate:timestamp] < CODELESS_SETTING_CACHE_TIMEOUT);
}
+ (void)setupGesture
{
_isGestureSet = YES;
[UIApplication sharedApplication].applicationSupportsShakeToEdit = YES;
Class class = [UIApplication class];
[self.swizzler swizzleSelector:@selector(motionBegan:withEvent:)
onClass:class
withBlock:^{
if (FBSDKServerConfigurationManager.shared.cachedServerConfiguration.isCodelessEventsEnabled) {
[self checkCodelessIndexingSession];
}
}
named:@"motionBegan"];
}
+ (void)checkCodelessIndexingSession
{
if (_isCheckingSession) {
return;
}
_isCheckingSession = YES;
NSDictionary *parameters = @{
CODELESS_INDEXING_SESSION_ID_KEY : [self currentSessionDeviceID],
CODELESS_INDEXING_EXT_INFO_KEY : [self extInfo]
};
id<FBSDKGraphRequest> request = [_requestProvider createGraphRequestWithGraphPath:[NSString stringWithFormat:@"%@/%@",
[self.settings appID],
CODELESS_INDEXING_SESSION_ENDPOINT]
parameters:parameters
HTTPMethod:FBSDKHTTPMethodPOST];
[request startWithCompletion:^(id<FBSDKGraphRequestConnecting> connection, id result, NSError *error) {
_isCheckingSession = NO;
if ([result isKindOfClass:[NSDictionary class]]) {
_isCodelessIndexingEnabled = [((NSDictionary *)result)[CODELESS_INDEXING_STATUS_KEY] boolValue];
if (_isCodelessIndexingEnabled) {
_lastTreeHash = nil;
if (!_appIndexingTimer) {
_appIndexingTimer = [NSTimer timerWithTimeInterval:CODELESS_INDEXING_UPLOAD_INTERVAL_IN_SECONDS
target:self
selector:@selector(startIndexing)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:_appIndexingTimer forMode:NSDefaultRunLoopMode];
}
} else {
_deviceSessionID = nil;
}
}
}];
}
+ (NSString *)currentSessionDeviceID
{
if (!_deviceSessionID) {
_deviceSessionID = [NSUUID UUID].UUIDString;
}
return _deviceSessionID;
}
+ (NSString *)extInfo
{
struct utsname systemInfo;
uname(&systemInfo);
NSString *machine = @(systemInfo.machine);
NSString *advertiserID = [FBSDKAppEventsUtility.shared advertiserID] ?: @"";
machine = machine ?: @"";
NSString *debugStatus = [FBSDKAppEventsUtility isDebugBuild] ? @"1" : @"0";
#if TARGET_OS_SIMULATOR
NSString *isSimulator = @"1";
#else
NSString *isSimulator = @"0";
#endif
NSLocale *locale = [NSLocale currentLocale];
NSString *languageCode = [locale objectForKey:NSLocaleLanguageCode];
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
NSString *localeString = locale.localeIdentifier;
if (languageCode && countryCode) {
localeString = [NSString stringWithFormat:@"%@_%@", languageCode, countryCode];
}
NSString *extinfo = [FBSDKBasicUtility JSONStringForObject:@[machine,
advertiserID,
debugStatus,
isSimulator,
localeString]
error:NULL
invalidObjectHandler:NULL];
return extinfo ?: @"";
}
+ (void)startIndexing
{
if (!_isCodelessIndexingEnabled) {
return;
}
if (UIApplicationStateActive != [UIApplication sharedApplication].applicationState) {
return;
}
// If userAgentSuffix begins with Unity, trigger unity code to upload view hierarchy
NSString *userAgentSuffix = [FBSDKSettings userAgentSuffix];
if (userAgentSuffix != nil && [userAgentSuffix hasPrefix:@"Unity"]) {
Class FBUnityUtility = objc_lookUpClass("FBUnityUtility");
SEL selector = NSSelectorFromString(@"triggerUploadViewHierarchy");
if (FBUnityUtility && selector && [FBUnityUtility respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[FBUnityUtility performSelector:selector];
#pragma clang diagnostic pop
}
} else {
[self uploadIndexing];
}
}
+ (void)uploadIndexing
{
if (_isCodelessIndexing) {
return;
}
NSString *tree = [FBSDKCodelessIndexer currentViewTree];
[self uploadIndexing:tree];
}
+ (void)uploadIndexing:(NSString *)tree
{
if (_isCodelessIndexing) {
return;
}
if (!tree) {
return;
}
NSString *currentTreeHash = [FBSDKUtility SHA256Hash:tree];
if (_lastTreeHash && [_lastTreeHash isEqualToString:currentTreeHash]) {
return;
}
_lastTreeHash = currentTreeHash;
NSBundle *mainBundle = [NSBundle mainBundle];
NSString *version = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
id<FBSDKGraphRequest> request = [_requestProvider createGraphRequestWithGraphPath:[NSString stringWithFormat:@"%@/%@",
[self.settings appID],
CODELESS_INDEXING_ENDPOINT]
parameters:@{
CODELESS_INDEXING_TREE_KEY : tree,
CODELESS_INDEXING_APP_VERSION_KEY : version ?: @"",
CODELESS_INDEXING_PLATFORM_KEY : @"iOS",
CODELESS_INDEXING_SESSION_ID_KEY : [self currentSessionDeviceID]
}
HTTPMethod:FBSDKHTTPMethodPOST];
_isCodelessIndexing = YES;
[request startWithCompletion:^(id<FBSDKGraphRequestConnecting> connection, id result, NSError *error) {
_isCodelessIndexing = NO;
if ([result isKindOfClass:[NSDictionary class]]) {
_isCodelessIndexingEnabled = [result[CODELESS_INDEXING_STATUS_KEY] boolValue];
if (!_isCodelessIndexingEnabled) {
_deviceSessionID = nil;
}
}
}];
}
+ (NSString *)currentViewTree
{
NSMutableArray *trees = [NSMutableArray array];
NSArray *windows = [UIApplication sharedApplication].windows;
for (UIWindow *window in windows) {
NSDictionary *tree = [FBSDKViewHierarchy recursiveCaptureTreeWithCurrentNode:window
targetNode:nil
objAddressSet:nil
hash:YES];
if (tree) {
if (window.isKeyWindow) {
[trees insertObject:tree atIndex:0];
} else {
[FBSDKTypeUtility array:trees addObject:tree];
}
}
}
if (0 == trees.count) {
return nil;
}
NSArray *viewTrees = [trees reverseObjectEnumerator].allObjects;
NSData *data = UIImageJPEGRepresentation([FBSDKCodelessIndexer screenshot], 0.5);
NSString *screenshot = [data base64EncodedStringWithOptions:0];
NSMutableDictionary *treeInfo = [NSMutableDictionary dictionary];
[FBSDKTypeUtility dictionary:treeInfo setObject:viewTrees forKey:@"view"];
[FBSDKTypeUtility dictionary:treeInfo setObject:screenshot ?: @"" forKey:@"screenshot"];
NSString *tree = nil;
data = [FBSDKTypeUtility dataWithJSONObject:treeInfo options:0 error:nil];
if (data) {
tree = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
return tree;
}
+ (UIImage *)screenshot
{
UIWindow *window = [FBSDKInternalUtility.sharedUtility findWindow];
if (!window) {
return nil;
}
UIGraphicsBeginImageContext(window.bounds.size);
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (NSDictionary<NSString *, NSNumber *> *)dimensionOf:(NSObject *)obj
{
UIView *view = nil;
if ([obj isKindOfClass:[UIView class]]) {
view = (UIView *)obj;
} else if ([obj isKindOfClass:[UIViewController class]]) {
view = ((UIViewController *)obj).view;
}
CGRect frame = view.frame;
CGPoint offset = CGPointZero;
if ([view isKindOfClass:[UIScrollView class]]) {
offset = ((UIScrollView *)view).contentOffset;
}
return @{
CODELESS_VIEW_TREE_TOP_KEY : @((int)frame.origin.y),
CODELESS_VIEW_TREE_LEFT_KEY : @((int)frame.origin.x),
CODELESS_VIEW_TREE_WIDTH_KEY : @((int)frame.size.width),
CODELESS_VIEW_TREE_HEIGHT_KEY : @((int)frame.size.height),
CODELESS_VIEW_TREE_OFFSET_X_KEY : @((int)offset.x),
CODELESS_VIEW_TREE_OFFSET_Y_KEY : @((int)offset.y),
CODELESS_VIEW_TREE_VISIBILITY_KEY : view.isHidden ? @4 : @0
};
}
#if DEBUG
#if FBTEST
+ (void)reset
{
_isCheckingSession = NO;
_isCodelessIndexing = NO;
_isCodelessIndexingEnabled = NO;
_isGestureSet = NO;
_codelessSetting = nil;
_requestProvider = nil;
_serverConfigurationProvider = nil;
_store = nil;
_connectionProvider = nil;
_swizzler = nil;
_settings = nil;
_advertiserIDProvider = nil;
_deviceSessionID = nil;
_lastTreeHash = nil;
}
+ (void)resetIsCodelessIndexing
{
_isCodelessIndexing = NO;
}
+ (BOOL)isCheckingSession
{
return _isCheckingSession;
}
+ (NSTimer *)appIndexingTimer
{
return _appIndexingTimer;
}
#endif
#endif
@end
#endif
@@ -0,0 +1,38 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
NS_SWIFT_NAME(CodelessParameterComponent)
@interface FBSDKCodelessParameterComponent : NSObject
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, copy, readonly) NSString *value;
@property (nonatomic, readonly) NSArray *path;
@property (nonatomic, copy, readonly) NSString *pathType;
- (instancetype)initWithJSON:(NSDictionary *)dict;
- (BOOL)isEqualToParameter:(FBSDKCodelessParameterComponent *)parameter;
@end
#endif
@@ -0,0 +1,80 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKCodelessParameterComponent.h"
#import "FBSDKCodelessPathComponent.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKViewHierarchyMacros.h"
@implementation FBSDKCodelessParameterComponent
- (instancetype)initWithJSON:(NSDictionary *)dict
{
if (self = [super init]) {
_name = [dict[CODELESS_MAPPING_PARAMETER_NAME_KEY] copy];
_value = [dict[CODELESS_MAPPING_PARAMETER_VALUE_KEY] copy];
_pathType = [dict[CODELESS_MAPPING_PATH_TYPE_KEY] copy];
NSArray *ary = dict[CODELESS_MAPPING_PATH_KEY];
NSMutableArray *mut = [NSMutableArray array];
for (NSDictionary *info in ary) {
FBSDKCodelessPathComponent *component = [[FBSDKCodelessPathComponent alloc] initWithJSON:info];
[FBSDKTypeUtility array:mut addObject:component];
}
_path = [mut copy];
}
return self;
}
- (BOOL)isEqualToParameter:(FBSDKCodelessParameterComponent *)parameter
{
if (_path.count != parameter.path.count) {
return NO;
}
NSString *current = [NSString stringWithFormat:@"%@|%@|%@",
_name ?: @"",
_value ?: @"",
_pathType ?: @""];
NSString *compared = [NSString stringWithFormat:@"%@|%@|%@",
parameter.name ?: @"",
parameter.value ?: @"",
parameter.pathType ?: @""];
if (![current isEqualToString:compared]) {
return NO;
}
for (int i = 0; i < _path.count; i++) {
if (![[FBSDKTypeUtility array:_path objectAtIndex:i] isEqualToPath:[FBSDKTypeUtility array:parameter.path objectAtIndex:i]]) {
return NO;
}
}
return YES;
}
@end
#endif
@@ -0,0 +1,52 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
typedef NS_OPTIONS(int, FBSDKCodelessMatchBitmaskField)
{
FBSDKCodelessMatchBitmaskFieldID = 1,
FBSDKCodelessMatchBitmaskFieldText = 1 << 1,
FBSDKCodelessMatchBitmaskFieldTag = 1 << 2,
FBSDKCodelessMatchBitmaskFieldDescription = 1 << 3,
FBSDKCodelessMatchBitmaskFieldHint = 1 << 4
};
NS_SWIFT_NAME(CodelessPathComponent)
@interface FBSDKCodelessPathComponent : NSObject
@property (nonatomic, copy, readonly) NSString *className;
@property (nonatomic, copy, readonly) NSString *text;
@property (nonatomic, copy, readonly) NSString *hint;
@property (nonatomic, copy, readonly) NSString *desc; // description
@property (nonatomic, readonly) int index;
@property (nonatomic, readonly) int tag;
@property (nonatomic, readonly) int section;
@property (nonatomic, readonly) int row;
@property (nonatomic, readonly) int matchBitmask;
- (instancetype)initWithJSON:(NSDictionary *)dict;
- (BOOL)isEqualToPath:(FBSDKCodelessPathComponent *)path;
@end
#endif
@@ -0,0 +1,81 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKCodelessPathComponent.h"
#import "FBSDKViewHierarchyMacros.h"
@implementation FBSDKCodelessPathComponent
- (instancetype)initWithJSON:(NSDictionary *)dict
{
if (self = [super init]) {
_className = [dict[CODELESS_MAPPING_CLASS_NAME_KEY] copy];
_text = [dict[CODELESS_MAPPING_TEXT_KEY] copy];
_hint = [dict[CODELESS_MAPPING_HINT_KEY] copy];
_desc = [dict[CODELESS_MAPPING_DESC_KEY] copy];
if (dict[CODELESS_MAPPING_INDEX_KEY]) {
_index = [dict[CODELESS_MAPPING_INDEX_KEY] intValue];
} else {
_index = -1;
}
if (dict[CODELESS_MAPPING_SECTION_KEY]) {
_section = [dict[CODELESS_MAPPING_SECTION_KEY] intValue];
} else {
_section = -1;
}
if (dict[CODELESS_MAPPING_ROW_KEY]) {
_row = [dict[CODELESS_MAPPING_ROW_KEY] intValue];
} else {
_row = -1;
}
_tag = [dict[CODELESS_MAPPING_TAG_KEY] intValue];
_matchBitmask = [dict[CODELESS_MAPPING_MATCH_BITMASK_KEY] intValue];
}
return self;
}
- (BOOL)isEqualToPath:(FBSDKCodelessPathComponent *)path
{
NSString *current = [NSString stringWithFormat:@"%@|%@|%@|%@|%d|%d|%d|%d|%d",
_className ?: @"",
_text ?: @"",
_hint ?: @"",
_desc ?: @"",
_index, _section, _row, _tag, _matchBitmask];
NSString *compared = [NSString stringWithFormat:@"%@|%@|%@|%@|%d|%d|%d|%d|%d",
path.className ?: @"",
path.text ?: @"",
path.hint ?: @"",
path.desc ?: @"",
path.index, path.section, path.row, path.tag, path.matchBitmask];
return [current isEqualToString:compared];
}
@end
#endif
@@ -0,0 +1,52 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <UIKit/UIKit.h>
#import "FBSDKAppEventsNumberParser.h"
#import "FBSDKCodelessParameterComponent.h"
@protocol FBSDKEventLogging;
NS_SWIFT_NAME(EventBinding)
@interface FBSDKEventBinding : NSObject
@property (class, nonatomic, readonly) id<FBSDKNumberParsing> numberParser;
@property (nonatomic, copy, readonly) NSString *eventName;
@property (nonatomic, copy, readonly) NSString *eventType;
@property (nonatomic, copy, readonly) NSString *appVersion;
@property (nonatomic, readonly) NSArray *path;
@property (nonatomic, copy, readonly) NSString *pathType;
@property (nonatomic, readonly) NSArray<FBSDKCodelessParameterComponent *> *parameters;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
+ (BOOL)isViewMatchPath:(UIView *)view path:(NSArray *)path;
+ (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath;
- (FBSDKEventBinding *)initWithJSON:(NSDictionary *)dict
eventLogger:(id<FBSDKEventLogging>)eventLogger;
- (void)trackEvent:(id)sender;
- (BOOL)isEqualToBinding:(FBSDKEventBinding *)binding;
@end
#endif
@@ -0,0 +1,354 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKEventBinding.h"
#import "FBSDKCodelessPathComponent.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKEventLogging.h"
#import "FBSDKInternalUtility+Internal.h"
#import "FBSDKSwizzler.h"
#import "FBSDKUtility.h"
#import "FBSDKViewHierarchy.h"
#import "FBSDKViewHierarchyMacros.h"
#define CODELESS_PATH_TYPE_ABSOLUTE @"absolute"
#define CODELESS_PATH_TYPE_RELATIVE @"relative"
#define CODELESS_CODELESS_EVENT_KEY @"_is_fb_codeless"
#define PARAMETER_NAME_PRICE @"_valueToSum"
@interface FBSDKEventBinding ()
@property (nonnull, nonatomic) id<FBSDKEventLogging> eventLogger;
@end
@implementation FBSDKEventBinding
static id<FBSDKNumberParsing> _numberParser;
+ (id<FBSDKNumberParsing>)numberParser
{
return _numberParser;
}
+ (void)setNumberParser:(id<FBSDKNumberParsing>)numberParser
{
_numberParser = numberParser;
}
+ (void)initialize
{
_numberParser = [[FBSDKAppEventsNumberParser alloc] initWithLocale:NSLocale.currentLocale];
}
- (FBSDKEventBinding *)initWithJSON:(NSDictionary *)dict
eventLogger:(id<FBSDKEventLogging>)eventLogger
{
if ((self = [super init])) {
_eventLogger = eventLogger;
_eventName = [dict[CODELESS_MAPPING_EVENT_NAME_KEY] copy];
_eventType = [dict[CODELESS_MAPPING_EVENT_TYPE_KEY] copy];
_appVersion = [dict[CODELESS_MAPPING_APP_VERSION_KEY] copy];
_pathType = [dict[CODELESS_MAPPING_PATH_TYPE_KEY] copy];
NSArray *pathComponents = dict[CODELESS_MAPPING_PATH_KEY];
NSMutableArray *mut = [NSMutableArray array];
for (NSDictionary *info in pathComponents) {
FBSDKCodelessPathComponent *component = [[FBSDKCodelessPathComponent alloc] initWithJSON:info];
[FBSDKTypeUtility array:mut addObject:component];
}
_path = [mut copy];
NSArray *parameters = dict[CODELESS_MAPPING_PARAMETERS_KEY];
mut = [NSMutableArray array];
for (NSDictionary *info in parameters) {
FBSDKCodelessParameterComponent *component = [[FBSDKCodelessParameterComponent alloc] initWithJSON:info];
[FBSDKTypeUtility array:mut addObject:component];
}
_parameters = [mut copy];
}
return self;
}
- (void)trackEvent:(id)sender
{
UIView *sourceView = [sender isKindOfClass:[UIView class]] ? (UIView *)sender : nil;
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[FBSDKTypeUtility dictionary:params setObject:@"1" forKey:CODELESS_CODELESS_EVENT_KEY];
for (FBSDKCodelessParameterComponent *component in self.parameters) {
NSString *text = component.value;
if (!text || text.length == 0) {
text = [FBSDKEventBinding findParameterOfPath:component.path
pathType:component.pathType
sourceView:sourceView];
}
if (text.length > 0) {
if ([component.name isEqualToString:PARAMETER_NAME_PRICE]) {
NSNumber *value = [self.class.numberParser parseNumberFrom:text];
[FBSDKTypeUtility dictionary:params setObject:value forKey:component.name];
} else {
[FBSDKTypeUtility dictionary:params setObject:text forKey:component.name];
}
}
}
[self.eventLogger logEvent:_eventName parameters:[params copy]];
}
+ (BOOL)matchAnyView:(NSArray *)views
pathComponent:(FBSDKCodelessPathComponent *)component
{
for (NSObject *view in views) {
if ([self match:view pathComponent:component]) {
return YES;
}
}
return NO;
}
+ (BOOL) match:(NSObject *)view
pathComponent:(FBSDKCodelessPathComponent *)component
{
if (!view) {
return NO;
}
NSString *className = NSStringFromClass([view class]);
if (![className isEqualToString:component.className]) {
return NO;
}
if (component.index >= 0) {
NSObject *parent = [FBSDKViewHierarchy getParent:view];
if (parent) {
NSArray *children = [FBSDKViewHierarchy getChildren:[FBSDKViewHierarchy getParent:view]];
NSUInteger index = [children indexOfObject:view];
if (index == NSNotFound || index != component.index) {
return NO;
}
} else {
if (0 != component.index) {
return NO;
}
}
}
if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldText) > 0) {
NSString *text = [FBSDKViewHierarchy getText:view];
BOOL match = ((text.length == 0 && component.text.length == 0)
|| [text isEqualToString:component.text]);
if (!match) {
return NO;
}
}
if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldTag) > 0
&& [view isKindOfClass:[UIView class]]
&& component.tag != ((UIView *)view).tag) {
return NO;
}
if ((component.matchBitmask & FBSDKCodelessMatchBitmaskFieldHint) > 0) {
NSString *hint = [FBSDKViewHierarchy getHint:view];
BOOL match = ((hint.length == 0 && component.hint.length == 0)
|| [hint isEqualToString:component.hint]);
if (!match) {
return NO;
}
}
return YES;
}
+ (BOOL)isViewMatchPath:(UIView *)view path:(NSArray *)path
{
NSArray *viewPath = [FBSDKViewHierarchy getPath:view];
BOOL isMatch = [self isPath:path matchViewPath:viewPath];
return isMatch;
}
+ (BOOL)isPath:(NSArray *)path matchViewPath:(NSArray *)viewPath
{
if ((path.count == 0) || (viewPath.count == 0)) {
return NO;
}
for (NSInteger i = 0; i < MIN(path.count, viewPath.count); i++) {
NSInteger idxPath = path.count - i - 1;
NSInteger idxViewPath = viewPath.count - i - 1;
FBSDKCodelessPathComponent *pathComponent = [FBSDKTypeUtility array:path objectAtIndex:idxPath];
FBSDKCodelessPathComponent *viewPathComponent = [FBSDKTypeUtility array:viewPath objectAtIndex:idxViewPath];
if (![pathComponent.className isEqualToString:viewPathComponent.className]) {
return NO;
}
if (pathComponent.index >= 0
&& pathComponent.index != viewPathComponent.index) {
return NO;
}
if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldText) > 0) {
NSString *text = viewPathComponent.text;
BOOL match = ((text.length == 0 && pathComponent.text.length == 0)
|| [text isEqualToString:pathComponent.text]
|| [[FBSDKUtility SHA256Hash:text] isEqualToString:pathComponent.text]);
if (!match) {
return NO;
}
}
if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldTag) > 0
&& pathComponent.tag != viewPathComponent.tag) {
return NO;
}
if ((pathComponent.matchBitmask & FBSDKCodelessMatchBitmaskFieldHint) > 0) {
NSString *hint = viewPathComponent.hint;
BOOL match = ((hint.length == 0 && pathComponent.hint.length == 0)
|| [hint isEqualToString:pathComponent.hint]
|| [[FBSDKUtility SHA256Hash:hint] isEqualToString:pathComponent.hint]);
if (!match) {
return NO;
}
}
}
return YES;
}
+ (NSObject *)findViewByPath:(NSArray *)path parent:(NSObject *)parent level:(int)level
{
if (level >= path.count) {
return nil;
}
FBSDKCodelessPathComponent *pathComponent = [FBSDKTypeUtility array:path objectAtIndex:level];
// If found parent, skip to next level
if ([pathComponent.className isEqualToString:CODELESS_MAPPING_PARENT_CLASS_NAME]) {
NSObject *nextParent = [FBSDKViewHierarchy getParent:parent];
return [FBSDKEventBinding findViewByPath:path parent:nextParent level:level + 1];
} else if ([pathComponent.className isEqualToString:CODELESS_MAPPING_CURRENT_CLASS_NAME]) {
return parent;
}
NSArray *children;
if (parent) {
children = [FBSDKViewHierarchy getChildren:parent];
} else {
UIWindow *window = [FBSDKInternalUtility.sharedUtility findWindow];
if (window) {
children = @[window];
} else {
return nil;
}
}
if (path.count - 1 == level) {
int index = pathComponent.index;
if (index >= 0) {
NSObject *child = index < children.count ? [FBSDKTypeUtility array:children objectAtIndex:index] : nil;
if ([self match:child pathComponent:pathComponent]) {
return child;
}
} else {
for (NSObject *child in children) {
if ([self match:child pathComponent:pathComponent]) {
return child;
}
}
}
} else {
for (NSObject *child in children) {
NSObject *result = [self findViewByPath:path parent:child level:level + 1];
if (result) {
return result;
}
}
}
return nil;
}
- (BOOL)isEqualToBinding:(FBSDKEventBinding *)binding
{
if (_path.count != binding.path.count
|| _parameters.count != binding.parameters.count) {
return NO;
}
NSString *current = [NSString stringWithFormat:@"%@|%@|%@|%@",
_eventName ?: @"",
_eventType ?: @"",
_appVersion ?: @"",
_pathType ?: @""];
NSString *compared = [NSString stringWithFormat:@"%@|%@|%@|%@",
binding.eventName ?: @"",
binding.eventType ?: @"",
binding.appVersion ?: @"",
binding.pathType ?: @""];
if (![current isEqualToString:compared]) {
return NO;
}
for (int i = 0; i < _path.count; i++) {
if (![[FBSDKTypeUtility array:_path objectAtIndex:i] isEqualToPath:[FBSDKTypeUtility array:binding.path objectAtIndex:i]]) {
return NO;
}
}
for (int i = 0; i < _parameters.count; i++) {
if (![[FBSDKTypeUtility array:_parameters objectAtIndex:i] isEqualToParameter:[FBSDKTypeUtility array:binding.parameters objectAtIndex:i]]) {
return NO;
}
}
return YES;
}
// MARK: - find event parameters via relative path
+ (NSString *)findParameterOfPath:(NSArray *)path
pathType:(NSString *)pathType
sourceView:(UIView *)sourceView
{
if (0 == path.count) {
return nil;
}
UIView *rootView = sourceView;
if (![pathType isEqualToString:CODELESS_PATH_TYPE_RELATIVE]) {
rootView = nil;
}
NSObject *foundObj = [self findViewByPath:path parent:rootView level:0];
return [FBSDKViewHierarchy getText:foundObj];
}
@end
#endif
@@ -0,0 +1,43 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
@protocol FBSDKSwizzling;
@protocol FBSDKEventLogging;
@class FBSDKEventBinding;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(EventBindingManager)
@interface FBSDKEventBindingManager : NSObject
- (instancetype)initWithSwizzler:(Class<FBSDKSwizzling>)swizzling
eventLogger:(id<FBSDKEventLogging>)eventLogger;
- (void)updateBindings:(NSArray *)bindings;
- (NSArray<FBSDKEventBinding *> *)parseArray:(NSArray *)array;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,498 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKEventBindingManager.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#import "FBSDKCodelessPathComponent.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKEventBinding.h"
#import "FBSDKEventLogging.h"
#import "FBSDKSwizzling.h"
#import "FBSDKViewHierarchy.h"
#import "FBSDKViewHierarchyMacros.h"
#define ReactNativeTargetKey @"target"
#define ReactNativeTouchEndEventName @"touchEnd"
#define ReactNativeClassRCTTextView "RCTTextView"
#define ReactNativeClassRCTImageView "RCTImageView"
#define ReactNativeClassRCTTouchEvent "RCTTouchEvent"
#define ReactNativeClassRCTTouchHandler "RCTTouchHandler"
@interface FBSDKEventBindingManager ()
@property (nonnull, nonatomic) id<FBSDKEventLogging> eventLogger;
@property (nonnull, nonatomic) Class<FBSDKSwizzling> swizzler;
@property (nonatomic) BOOL isStarted;
@property (nullable, nonatomic) NSMutableDictionary *reactBindings;
@property (nonnull, nonatomic) NSSet *validClasses;
@property (nonatomic) BOOL hasReactNative;
@property (nullable, nonatomic) NSArray *eventBindings;
@end
#if FBSDK_SWIFT_PACKAGE
NS_EXTENSION_UNAVAILABLE("The Facebook iOS SDK is not currently supported in extensions")
#endif
@implementation FBSDKEventBindingManager
- (instancetype)initWithSwizzler:(Class<FBSDKSwizzling>)swizzling
eventLogger:(id<FBSDKEventLogging>)eventLogger;
{
if ((self = [super init])) {
_swizzler = swizzling;
_eventLogger = eventLogger;
_hasReactNative = NO;
_isStarted = NO;
_reactBindings = [NSMutableDictionary dictionary];
NSMutableSet *classes = [NSMutableSet set];
[classes addObject:[UIControl class]];
[classes addObject:[UITableView class]];
[classes addObject:[UICollectionView class]];
// ReactNative
Class classRCTRootView = objc_lookUpClass(ReactNativeClassRCTRootView);
if (classRCTRootView != nil) {
_hasReactNative = YES;
Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView);
Class classRCTTextView = objc_lookUpClass(ReactNativeClassRCTTextView);
Class classRCTImageView = objc_lookUpClass(ReactNativeClassRCTImageView);
if (classRCTView) {
[classes addObject:classRCTView];
}
if (classRCTTextView) {
[classes addObject:classRCTTextView];
}
if (classRCTImageView) {
[classes addObject:classRCTImageView];
}
}
_validClasses = [NSSet setWithSet:classes];
}
return self;
}
- (instancetype)initWithJSON:(NSDictionary *)dict
swizzler:(Class<FBSDKSwizzling>)swizzler
eventLogger:(id<FBSDKEventLogging>)eventLogger
{
if ((self = [self initWithSwizzler:swizzler eventLogger:eventLogger])) {
NSArray *eventBindingsDict = [FBSDKTypeUtility arrayValue:dict[@"event_bindings"]];
NSMutableArray *bindings = [NSMutableArray array];
for (NSDictionary *d in eventBindingsDict) {
FBSDKEventBinding *e = [[FBSDKEventBinding alloc] initWithJSON:d eventLogger:eventLogger];
[FBSDKTypeUtility array:bindings addObject:e];
}
_eventBindings = [bindings copy];
}
return self;
}
- (NSArray *)parseArray:(NSArray *)array
{
NSMutableArray *result = [NSMutableArray array];
for (NSDictionary *json in array) {
FBSDKEventBinding *binding = [[FBSDKEventBinding alloc] initWithJSON:json
eventLogger:self.eventLogger];
[FBSDKTypeUtility array:result addObject:binding];
}
return [result copy];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
- (void)start
{
if (self.isStarted) {
return;
}
if (0 == self.eventBindings.count) {
return;
}
self.isStarted = YES;
void (^blockToWindow)(id view) = ^(id view) {
[self matchView:view delegate:nil];
};
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:[UIControl class]
withBlock:blockToWindow
named:@"map_control"];
// ReactNative
if (self.hasReactNative) { // If app is built via ReactNative
Class classRCTView = objc_lookUpClass(ReactNativeClassRCTView);
Class classRCTTextView = objc_lookUpClass(ReactNativeClassRCTTextView);
Class classRCTImageView = objc_lookUpClass(ReactNativeClassRCTImageView);
Class classRCTTouchHandler = objc_lookUpClass(ReactNativeClassRCTTouchHandler);
// All react-native views would be added tp RCTRootView, so no need to check didMoveToWindow
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:classRCTView
withBlock:blockToWindow
named:@"match_react_native"];
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:classRCTTextView
withBlock:blockToWindow
named:@"match_react_native"];
[self.swizzler swizzleSelector:@selector(didMoveToWindow)
onClass:classRCTImageView
withBlock:blockToWindow
named:@"match_react_native"];
// RCTTouchHandler handles with touch events, like touchEnd and uses RCTEventDispather to dispatch events, so we can check _updateAndDispatchTouches to fire events
[self.swizzler swizzleSelector:@selector(_updateAndDispatchTouches:eventName:)
onClass:classRCTTouchHandler
withBlock:^(id touchHandler, SEL command, id touches, id eventName) {
[self handleReactNativeTouchesWithHandler:touchHandler command:command touches:touches eventName:eventName];
}
named:@"dispatch_rn_event"];
}
// UITableView
void (^tableViewBlock)(UITableView *tableView,
SEL cmd,
id<UITableViewDelegate> delegate) =
^(UITableView *tableView, SEL cmd, id<UITableViewDelegate> delegate) {
if (!delegate) {
return;
}
[self matchView:tableView delegate:delegate];
};
[self.swizzler swizzleSelector:@selector(setDelegate:)
onClass:[UITableView class]
withBlock:tableViewBlock
named:@"match_table_view"];
// UICollectionView
void (^collectionViewBlock)(UICollectionView *collectionView,
SEL cmd,
id<UICollectionViewDelegate> delegate) =
^(UICollectionView *collectionView, SEL cmd, id<UICollectionViewDelegate> delegate) {
if (nil == delegate) {
return;
}
[self matchView:collectionView delegate:delegate];
};
[self.swizzler swizzleSelector:@selector(setDelegate:)
onClass:[UICollectionView class]
withBlock:collectionViewBlock
named:@"handle_collection_view"];
}
- (void)rematchBindings
{
if (0 == self.eventBindings.count) {
return;
}
NSArray *windows = [UIApplication sharedApplication].windows;
for (UIWindow *window in windows) {
[self matchSubviewsIn:window];
}
}
- (void)matchSubviewsIn:(UIView *)view
{
if (!view) {
return;
}
for (UIView *subview in view.subviews) {
BOOL isValidClass = NO;
for (Class cls in self.validClasses) {
if ([subview isKindOfClass:cls]) {
isValidClass = YES;
break;
}
}
if (isValidClass) {
if ([subview isKindOfClass:[UITableView class]]) {
UITableView *tableView = (UITableView *)subview;
if (tableView.delegate) {
[self matchView:subview delegate:tableView.delegate];
}
} else if ([subview isKindOfClass:[UICollectionView class]]) {
UICollectionView *collectionView = (UICollectionView *)subview;
if (collectionView.delegate) {
[self matchView:subview delegate:collectionView.delegate];
}
} else {
[self matchView:subview delegate:nil];
}
}
if (![subview isKindOfClass:[UIControl class]]) {
[self matchSubviewsIn:subview];
}
}
}
// check if the view is matched to any event
- (void)matchView:(UIView *)view delegate:(id)delegate
{
if (0 == self.eventBindings.count) {
return;
}
__weak Class<FBSDKSwizzling> weakSwizzler = self.swizzler;
__block BOOL hasReactNative = self.hasReactNative;
fb_dispatch_on_main_thread(^{
if (![view window]) {
return;
}
NSArray *path = [FBSDKViewHierarchy getPath:view];
void (^matchBlock)(void) = ^void () {
if ([view isKindOfClass:[UIControl class]]) {
UIControl *control = (UIControl *)view;
for (FBSDKEventBinding *binding in self->_eventBindings) {
if ([FBSDKEventBinding isPath:binding.path matchViewPath:path]) {
fb_dispatch_on_main_thread(^{
[control addTarget:binding
action:@selector(trackEvent:)
forControlEvents:UIControlEventTouchUpInside];
});
break;
}
}
} else if (hasReactNative
&& [view respondsToSelector:@selector(reactTag)]) {
for (FBSDKEventBinding *binding in self->_eventBindings) {
if ([FBSDKEventBinding isPath:binding.path matchViewPath:path]) {
fb_dispatch_on_main_thread(^{
if (view) {
NSNumber *reactTag = [FBSDKViewHierarchy getViewReactTag:view];
if (reactTag != nil) {
[FBSDKTypeUtility dictionary:self->_reactBindings setObject:binding forKey:reactTag];
}
}
});
break;
}
}
} else if ([view isKindOfClass:[UITableView class]]
&& [delegate conformsToProtocol:@protocol(UITableViewDelegate)]) {
void (^tableViewBlock)(void) = ^void () {
NSMutableSet *matchedBindings = [NSMutableSet set];
for (FBSDKEventBinding *binding in self->_eventBindings) {
if (binding.path.count > 1) {
NSArray *shortPath = [binding.path
subarrayWithRange:NSMakeRange(0, binding.path.count - 1)];
if ([FBSDKEventBinding isPath:shortPath matchViewPath:path]) {
[matchedBindings addObject:binding];
}
}
}
if (matchedBindings.count > 0) {
NSArray *bindings = matchedBindings.allObjects;
void (^block)(id, SEL, id, id) = ^(id target, SEL command, UITableView *tableView, NSIndexPath *indexPath) {
[self handleDidSelectRowWithBindings:bindings target:target command:command tableView:tableView indexPath:indexPath];
};
[weakSwizzler swizzleSelector:@selector(tableView:didSelectRowAtIndexPath:)
onClass:[delegate class]
withBlock:block
named:@"handle_table_view"];
}
};
#if FBTEST
tableViewBlock();
#else
fb_dispatch_on_default_thread(tableViewBlock);
#endif
} else if ([view isKindOfClass:[UICollectionView class]]
&& [delegate conformsToProtocol:@protocol(UICollectionViewDelegate)]) {
void (^collectionViewBlock)(void) = ^void () {
NSMutableSet *matchedBindings = [NSMutableSet set];
for (FBSDKEventBinding *binding in self->_eventBindings) {
if (binding.path.count > 1) {
NSArray *shortPath = [binding.path
subarrayWithRange:NSMakeRange(0, binding.path.count - 1)];
if ([FBSDKEventBinding isPath:shortPath matchViewPath:path]) {
[matchedBindings addObject:binding];
}
}
}
if (matchedBindings.count > 0) {
NSArray *bindings = matchedBindings.allObjects;
void (^block)(id, SEL, id, id) = ^(id target, SEL command, UICollectionView *collectionView, NSIndexPath *indexPath) {
[self handleDidSelectItemWithBindings:bindings target:target command:command collectionView:collectionView indexPath:indexPath];
};
[weakSwizzler swizzleSelector:@selector(collectionView:didSelectItemAtIndexPath:)
onClass:[delegate class]
withBlock:block
named:@"handle_collection_view"];
}
};
#if FBTEST
collectionViewBlock();
#else
fb_dispatch_on_default_thread(collectionViewBlock);
#endif
}
};
#if FBTEST
matchBlock();
#else
fb_dispatch_on_default_thread(matchBlock);
#endif
});
}
#pragma clang diagnostic pop
- (void)updateBindings:(NSArray *)bindings
{
if (self.eventBindings.count > 0 && self.eventBindings.count == bindings.count) {
// Check whether event bindings are the same
BOOL isSame = YES;
for (int i = 0; i < self.eventBindings.count; i++) {
if (![[FBSDKTypeUtility array:self.eventBindings objectAtIndex:i] isEqualToBinding:[FBSDKTypeUtility array:bindings objectAtIndex:i]]) {
isSame = NO;
break;
}
}
if (isSame) {
return;
}
}
self.eventBindings = bindings;
[self.reactBindings removeAllObjects];
if (!self.isStarted) {
[self start];
}
fb_dispatch_on_main_thread(^{
[self rematchBindings];
});
}
// MARK: Method Replacements
- (void)handleReactNativeTouchesWithHandler:(id)handler
command:(SEL)command
touches:(id)touches
eventName:(id)eventName
{
if ([touches isKindOfClass:[NSSet class]] && [eventName isKindOfClass:[NSString class]]) {
@try {
NSString *reactEventName = (NSString *)eventName;
NSSet<UITouch *> *reactTouches = (NSSet<UITouch *> *)touches;
if ([reactEventName isEqualToString:ReactNativeTouchEndEventName]) {
for (UITouch *touch in reactTouches) {
UIView *targetView = ((UITouch *)touch).view.superview;
NSNumber *reactTag = nil;
// Find the closest React-managed touchable view like RCTTouchHandler
while (targetView) {
reactTag = [FBSDKViewHierarchy getViewReactTag:targetView];
if (reactTag != nil && targetView.userInteractionEnabled) {
break;
}
targetView = targetView.superview;
}
FBSDKEventBinding *eventBinding = self->_reactBindings[reactTag];
if (reactTag != nil && eventBinding != nil) {
[eventBinding trackEvent:nil];
}
}
}
} @catch (NSException *exception) {
// Catch exception here to prevent LytroKit from crashing app
}
}
};
- (void)handleDidSelectRowWithBindings:(NSArray<FBSDKEventBinding *> *)bindings
target:(nullable id)target
command:(nullable SEL)command
tableView:(UITableView *)tableView
indexPath:(NSIndexPath *)indexPath
{
fb_dispatch_on_main_thread(^{
for (FBSDKEventBinding *binding in bindings) {
FBSDKCodelessPathComponent *component = binding.path.lastObject;
if ((component.section == -1 || component.section == indexPath.section)
&& (component.row == -1 || component.row == indexPath.row)) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell) {
[binding trackEvent:cell];
}
}
}
});
}
- (void)handleDidSelectItemWithBindings:(NSArray<FBSDKEventBinding *> *)bindings
target:(nullable id)target
command:(nullable SEL)command
collectionView:(UICollectionView *)collectionView
indexPath:(NSIndexPath *)indexPath
{
fb_dispatch_on_main_thread(^{
for (FBSDKEventBinding *binding in bindings) {
FBSDKCodelessPathComponent *component = binding.path.lastObject;
if ((component.section == -1 || component.section == indexPath.section)
&& (component.row == -1 || component.row == indexPath.row)) {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
[binding trackEvent:cell];
}
}
}
});
}
- (NSSet *)validClasses
{
return _validClasses;
}
#if DEBUG
#if FBTEST
- (void)setReactBindings:(NSMutableDictionary *)bindings
{
_reactBindings = bindings;
}
#endif
#endif
@end
#endif
@@ -0,0 +1,28 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsParameterProcessing.h"
#import "FBSDKEventDeactivationManager.h"
#import "FBSDKEventsProcessing.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKEventDeactivationManager (Protocols) <FBSDKAppEventsParameterProcessing, FBSDKEventsProcessing>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,33 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(EventDeactivationManager)
@interface FBSDKEventDeactivationManager : NSObject
- (void)enable;
- (void)processEvents:(NSMutableArray<NSDictionary<NSString *, id> *> *)events;
- (nullable NSDictionary<NSString *, id> *)processParameters:(nullable NSDictionary<NSString *, id> *)parameters
eventName:(NSString *)eventName;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,160 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKEventDeactivationManager.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKServerConfigurationManager+ServerConfigurationProviding.h"
static NSString *const DEPRECATED_PARAM_KEY = @"deprecated_param";
static NSString *const DEPRECATED_EVENT_KEY = @"is_deprecated_event";
@interface FBSDKDeactivatedEvent : NSObject
@property (nonatomic, readonly, copy) NSString *eventName;
@property (nullable, nonatomic, readonly, copy) NSSet<NSString *> *deactivatedParams;
- (instancetype)initWithEventName:(NSString *)eventName
deactivatedParams:(NSSet<NSString *> *)deactivatedParams;
@end
@implementation FBSDKDeactivatedEvent
- (instancetype)initWithEventName:(NSString *)eventName
deactivatedParams:(NSSet<NSString *> *)deactivatedParams
{
self = [super init];
if (self) {
_eventName = eventName;
_deactivatedParams = deactivatedParams;
}
return self;
}
@end
@interface FBSDKEventDeactivationManager ()
@property (nonatomic) BOOL isEventDeactivationEnabled;
@property (nonatomic, strong) NSMutableSet<NSString *> *deactivatedEvents;
@property (nonatomic, strong) NSMutableArray<FBSDKDeactivatedEvent *> *eventsWithDeactivatedParams;
@property (nonatomic) id<FBSDKServerConfigurationProviding> serverConfigurationProvider;
@end
@implementation FBSDKEventDeactivationManager
+ (instancetype)shared
{
static FBSDKEventDeactivationManager *instance;
static dispatch_once_t nonce;
dispatch_once(&nonce, ^{
instance = [[self alloc] initWithServerConfigurationProvider:FBSDKServerConfigurationManager.shared];
});
return instance;
}
- (instancetype)initWithServerConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider
{
self.isEventDeactivationEnabled = NO;
self.serverConfigurationProvider = serverConfigurationProvider;
return self;
}
- (void)enable
{
@try {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSDictionary<NSString *, id> *restrictiveParams = [self.serverConfigurationProvider cachedServerConfiguration].restrictiveParams;
if (restrictiveParams) {
[self _updateDeactivatedEvents:restrictiveParams];
self.isEventDeactivationEnabled = YES;
}
});
} @catch (NSException *exception) {}
}
- (void)processEvents:(NSMutableArray<NSDictionary<NSString *, id> *> *)events
{
@try {
if (!self.isEventDeactivationEnabled) {
return;
}
NSArray<NSDictionary<NSString *, id> *> *eventArray = [events copy];
for (NSDictionary<NSString *, NSDictionary<NSString *, id> *> *event in eventArray) {
if ([self.deactivatedEvents containsObject:event[@"event"][@"_eventName"]]) {
[events removeObject:event];
}
}
} @catch (NSException *exception) {}
}
- (nullable NSDictionary<NSString *, id> *)processParameters:(nullable NSDictionary<NSString *, id> *)parameters
eventName:(NSString *)eventName
{
@try {
if (!self.isEventDeactivationEnabled || parameters.count == 0 || self.eventsWithDeactivatedParams.count == 0) {
return parameters;
}
NSMutableDictionary<NSString *, id> *params = [NSMutableDictionary dictionaryWithDictionary:parameters];
for (NSString *key in [parameters keyEnumerator]) {
for (FBSDKDeactivatedEvent *event in self.eventsWithDeactivatedParams) {
if ([event.eventName isEqualToString:eventName] && [event.deactivatedParams containsObject:key]) {
[params removeObjectForKey:key];
}
}
}
return [params copy];
} @catch (NSException *exception) {
return parameters;
}
}
#pragma mark - Private Method
- (void)_updateDeactivatedEvents:(nullable NSDictionary<NSString *, id> *)events
{
events = [FBSDKTypeUtility dictionaryValue:events];
if (events.count == 0) {
return;
}
[self.deactivatedEvents removeAllObjects];
[self.eventsWithDeactivatedParams removeAllObjects];
NSMutableArray<FBSDKDeactivatedEvent *> *deactivatedParamsArray = [NSMutableArray array];
NSMutableSet<NSString *> *deactivatedEventSet = [NSMutableSet set];
for (NSString *eventName in events.allKeys) {
NSDictionary<NSString *, id> *eventInfo = [FBSDKTypeUtility dictionary:events objectForKey:eventName ofType:NSDictionary.class];
if (!eventInfo) {
continue;
}
if (eventInfo[DEPRECATED_EVENT_KEY]) {
[deactivatedEventSet addObject:eventName];
}
if (eventInfo[DEPRECATED_PARAM_KEY]) {
FBSDKDeactivatedEvent *eventWithDeactivatedParams = [[FBSDKDeactivatedEvent alloc] initWithEventName:eventName
deactivatedParams:[NSSet setWithArray:eventInfo[DEPRECATED_PARAM_KEY]]];
[FBSDKTypeUtility array:deactivatedParamsArray addObject:eventWithDeactivatedParams];
}
}
self.deactivatedEvents = deactivatedEventSet;
self.eventsWithDeactivatedParams = deactivatedParamsArray;
}
@end
@@ -0,0 +1,32 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#if SWIFT_PACKAGE
#import "FBSDKAppEvents.h"
#else
#import <FBSDKCoreKit/FBSDKAppEvents.h>
#endif
#import "FBSDKEventLogging.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKAppEvents (EventLogging) <FBSDKEventLogging>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,140 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#if defined FBSDK_SWIFT_PACKAGE
#import "FBSDKAppEvents.h"
#else
#import <FBSDKCoreKit/FBSDKAppEvents.h>
#endif
#import <UIKit/UIApplication.h>
#import "FBSDKAppEventsUtility.h"
// Internally known event names
/** Use to log that the share dialog was launched */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareSheetLaunch;
/** Use to log that the share dialog was dismissed */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareSheetDismiss;
/** Use to log that the permissions UI was launched */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNamePermissionsUILaunch;
/** Use to log that the permissions UI was dismissed */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNamePermissionsUIDismiss;
/** Use to log that the share tray launched. */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareTrayDidLaunch;
/** Use to log that the person selected a sharing target. */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameShareTrayDidSelectActivity;
// Internally known event parameters
/** Use to log the result of a call to FBDialogs presentShareDialogWithParams: */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentShareDialog;
/** Use to log the result of a call to FBDialogs presentLikeDialogWithLikeParams: */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentLikeDialogOG;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentShareDialogPhoto;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentMessageDialog;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBDialogsPresentMessageDialogPhoto;
/** Use to log the live streaming events from sdk */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingStart;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingStop;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingPause;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingResume;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingError;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingUpdateStatus;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingVideoID;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingMic;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingCamera;
/** Use to log the results of a share dialog */
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventAppInviteShareDialogResult;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKEventAppInviteShareDialogShow;
/** Use to log parameters for share tray use */
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterShareTrayActivityName;
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterShareTrayResult;
/** Use to log parameters for live streaming*/
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingPrevStatus;
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingStatus;
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingError;
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingVideoID;
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingMicEnabled;
FOUNDATION_EXPORT NSString *const FBSDKAppEventParameterLiveStreamingCameraEnabled;
// Internally known event parameter values
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogOutcomeValue_Completed;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsDialogOutcomeValue_Failed;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeButtonImpression;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingButtonImpression;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLikeButtonDidTap;
FOUNDATION_EXPORT NSString *const FBSDKAppEventNameFBSDKLiveStreamingButtonDidTap;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesHandlerKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesActionKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesEventKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesParamsKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackCustomKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackSingleKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelTrackSingleCustomKey;
FOUNDATION_EXPORT NSString *const FBSDKAppEventsWKWebViewMessagesPixelIDKey;
@interface FBSDKAppEvents (Internal)
@property (nonatomic) UIApplicationState applicationState;
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
isImplicitlyLogged:(BOOL)isImplicitlyLogged;
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
valueToSum:(double)valueToSum
isImplicitlyLogged:(BOOL)isImplicitlyLogged;
+ (void)logInternalEvent:(FBSDKAppEventName)eventName
valueToSum:(double)valueToSum
parameters:(NSDictionary *)parameters
isImplicitlyLogged:(BOOL)isImplicitlyLogged;
+ (void)logInternalEvent:(NSString *)eventName
valueToSum:(NSNumber *)valueToSum
parameters:(NSDictionary *)parameters
isImplicitlyLogged:(BOOL)isImplicitlyLogged
accessToken:(FBSDKAccessToken *)accessToken;
+ (void)logImplicitEvent:(NSString *)eventName
valueToSum:(NSNumber *)valueToSum
parameters:(NSDictionary *)parameters
accessToken:(FBSDKAccessToken *)accessToken;
- (void)flushForReason:(FBSDKAppEventsFlushReason)flushReason;
- (void)startObservingApplicationLifecycleNotifications;
@end
@@ -0,0 +1,45 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKAtePublishing.h"
@protocol FBSDKDataPersisting;
@protocol FBSDKGraphRequestProviding;
@protocol FBSDKSettings;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsAtePublisher)
@interface FBSDKAppEventsAtePublisher : NSObject <FBSDKAtePublishing>
@property (nonatomic, readonly) NSString *appIdentifier;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (nullable instancetype)initWithAppIdentifier:(NSString *)appIdentifier
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
settings:(id<FBSDKSettings>)settings
store:(id<FBSDKDataPersisting>)store;
- (void)publishATE;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,121 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsAtePublisher.h"
#if FBSDK_SWIFT_PACKAGE
#import "FBSDKGraphRequestFlags.h"
#import "FBSDKGraphRequestHTTPMethod.h"
#else
#import <FBSDKCoreKit/FBSDKGraphRequestFlags.h>
#import <FBSDKCoreKit/FBSDKGraphRequestHTTPMethod.h>
#endif
#import "FBSDKAppEventsDeviceInfo.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKDataPersisting.h"
#import "FBSDKGraphRequestConnecting.h"
#import "FBSDKGraphRequestProtocol.h"
#import "FBSDKGraphRequestProviding.h"
#import "FBSDKInternalUtility+Internal.h"
#import "FBSDKLogger.h"
#import "FBSDKSettingsProtocol.h"
@interface FBSDKAppEventsAtePublisher ()
@property (nullable, nonatomic) id<FBSDKGraphRequestProviding> graphRequestFactory;
@property (nullable, nonatomic) id<FBSDKSettings> settings;
@property (nullable, nonatomic) id<FBSDKDataPersisting> store;
@property (nonatomic) BOOL isProcessing;
@end
@implementation FBSDKAppEventsAtePublisher
- (nullable instancetype)initWithAppIdentifier:(NSString *)appIdentifier
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
settings:(id<FBSDKSettings>)settings
store:(id<FBSDKDataPersisting>)store
{
if ((self = [self init])) {
NSString *identifier = [FBSDKTypeUtility coercedToStringValue:appIdentifier];
if (identifier.length == 0) {
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorDeveloperErrors logEntry:@"Missing [FBSDKAppEvents appID] for [FBSDKAppEvents publishATE:]"];
return nil;
}
_appIdentifier = identifier;
_graphRequestFactory = graphRequestFactory;
_settings = settings;
_store = store;
}
return self;
}
- (void)publishATE
{
if (self.isProcessing) {
return;
}
self.isProcessing = YES;
NSString *lastATEPingString = [NSString stringWithFormat:@"com.facebook.sdk:lastATEPing%@", self.appIdentifier];
id lastPublishDate = [self.store objectForKey:lastATEPingString];
if ([lastPublishDate isKindOfClass:[NSDate class]] && [(NSDate *)lastPublishDate timeIntervalSinceNow] * -1 < 24 * 60 * 60) {
self.isProcessing = NO;
return;
}
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
[FBSDKTypeUtility dictionary:parameters setObject:@"CUSTOM_APP_EVENTS" forKey:@"event"];
NSOperatingSystemVersion operatingSystemVersion = [FBSDKInternalUtility.sharedUtility operatingSystemVersion];
NSString *osVersion = [NSString stringWithFormat:@"%ti.%ti.%ti",
operatingSystemVersion.majorVersion,
operatingSystemVersion.minorVersion,
operatingSystemVersion.patchVersion];
NSArray *event = @[
@{
@"_eventName" : @"fb_mobile_ate_status",
@"ate_status" : @(self.settings.advertisingTrackingStatus).stringValue,
@"os_version" : osVersion,
}
];
[FBSDKTypeUtility dictionary:parameters setObject:[FBSDKBasicUtility JSONStringForObject:event error:NULL invalidObjectHandler:NULL] forKey:@"custom_events"];
[FBSDKAppEventsDeviceInfo extendDictionaryWithDeviceInfo:parameters];
NSString *path = [NSString stringWithFormat:@"%@/activities", self.appIdentifier];
id<FBSDKGraphRequest> request = [self.graphRequestFactory createGraphRequestWithGraphPath:path
parameters:parameters
tokenString:nil
HTTPMethod:FBSDKHTTPMethodPOST
flags:FBSDKGraphRequestFlagDoNotInvalidateTokenOnError | FBSDKGraphRequestFlagDisableErrorRecovery];
__block id<FBSDKDataPersisting> weakStore = self.store;
[request startWithCompletion:^(id<FBSDKGraphRequestConnecting> connection, id result, NSError *error) {
if (!error) {
[weakStore setObject:[NSDate date] forKey:lastATEPingString];
}
self.isProcessing = NO;
}];
#if FBTEST
self.isProcessing = NO;
#endif
}
@end
@@ -0,0 +1,29 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKAppEventsConfiguration.h"
#import "FBSDKAppEventsConfigurationProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKAppEventsConfiguration (AppEventsConfigurationProtocol) <FBSDKAppEventsConfiguration>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,44 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#if SWIFT_PACKAGE
#import "FBSDKAdvertisingTrackingStatus.h"
#else
#import <FBSDKCoreKit/FBSDKAdvertisingTrackingStatus.h>
#endif
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsConfiguration)
@interface FBSDKAppEventsConfiguration : NSObject<NSCopying, NSObject, NSSecureCoding>
@property (nonatomic, readonly, assign) FBSDKAdvertisingTrackingStatus defaultATEStatus;
@property (nonatomic, readonly, assign) BOOL advertiserIDCollectionEnabled;
@property (nonatomic, readonly, assign) BOOL eventCollectionEnabled;
- (instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict;
+ (instancetype)defaultConfiguration;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,118 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsConfiguration.h"
#import "FBSDKCoreKitBasicsImport.h"
#define FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY @"default_ate_status"
#define FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY @"advertiser_id_collection_enabled"
#define FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY @"event_collection_enabled"
@implementation FBSDKAppEventsConfiguration
- (instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict
{
if ((self = [super init])) {
@try {
dict = [FBSDKTypeUtility dictionaryValue:dict];
if (!dict) {
return FBSDKAppEventsConfiguration.defaultConfiguration;
}
NSDictionary<NSString *, id> *configs = [FBSDKTypeUtility dictionary:dict objectForKey:@"app_events_config" ofType:NSDictionary.class];
if (!configs) {
return FBSDKAppEventsConfiguration.defaultConfiguration;
}
NSNumber *defaultATEStatus = [FBSDKTypeUtility numberValue:configs[FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY]] ?: @(FBSDKAdvertisingTrackingUnspecified);
NSNumber *advertiserIDCollectionEnabled = [FBSDKTypeUtility numberValue:configs[FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY]] ?: @(YES);
NSNumber *eventCollectionEnabled = [FBSDKTypeUtility numberValue:configs[FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY]] ?: @(NO);
_defaultATEStatus = [defaultATEStatus integerValue];
_advertiserIDCollectionEnabled = [advertiserIDCollectionEnabled boolValue];
_eventCollectionEnabled = [eventCollectionEnabled boolValue];
} @catch (NSException *exception) {
return FBSDKAppEventsConfiguration.defaultConfiguration;
}
}
return self;
}
- (instancetype)initWithDefaultATEStatus:(FBSDKAdvertisingTrackingStatus)defaultATEStatus
advertiserIDCollectionEnabled:(BOOL)advertiserIDCollectionEnabled
eventCollectionEnabled:(BOOL)eventCollectionEnabled
{
if ((self = [super init])) {
_defaultATEStatus = defaultATEStatus;
_advertiserIDCollectionEnabled = advertiserIDCollectionEnabled;
_eventCollectionEnabled = eventCollectionEnabled;
}
return self;
}
+ (instancetype)defaultConfiguration
{
FBSDKAppEventsConfiguration *config = [[FBSDKAppEventsConfiguration alloc] initWithDefaultATEStatus:FBSDKAdvertisingTrackingUnspecified
advertiserIDCollectionEnabled:YES
eventCollectionEnabled:NO];
return config;
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
FBSDKAdvertisingTrackingStatus defaultATEStatus = [decoder decodeIntegerForKey:FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY];
BOOL advertisingIDCollectionEnabled = [decoder decodeBoolForKey:FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY];
BOOL eventCollectionEnabled = [decoder decodeBoolForKey:FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY];
return [[FBSDKAppEventsConfiguration alloc] initWithDefaultATEStatus:defaultATEStatus
advertiserIDCollectionEnabled:advertisingIDCollectionEnabled
eventCollectionEnabled:eventCollectionEnabled];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInteger:_defaultATEStatus forKey:FBSDK_APP_EVENTS_CONFIGURATION_DEFAULT_ATE_STATUS_KEY];
[encoder encodeBool:_advertiserIDCollectionEnabled forKey:FBSDK_APP_EVENTS_CONFIGURATION_ADVERTISER_ID_TRACKING_ENABLED_KEY];
[encoder encodeBool:_eventCollectionEnabled forKey:FBSDK_APP_EVENTS_CONFIGURATION_EVENT_COLLECTION_ENABLED_KEY];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
#pragma mark - Testability
#if DEBUG
#if FBTEST
- (void)setDefaultATEStatus:(FBSDKAdvertisingTrackingStatus)status
{
_defaultATEStatus = status;
}
#endif
#endif
@end
@@ -0,0 +1,28 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKAppEventsConfigurationProviding.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKAppEventsConfigurationManager (AppEventsConfigurationProviding) <FBSDKAppEventsConfigurationProviding>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,46 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKAppEventsConfiguration.h"
typedef void (^FBSDKAppEventsConfigurationManagerBlock)(void);
@protocol FBSDKDataPersisting;
@protocol FBSDKSettings;
@protocol FBSDKGraphRequestProviding;
@protocol FBSDKGraphRequestConnectionProviding;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsConfigurationManager)
@interface FBSDKAppEventsConfigurationManager : NSObject
+ (void)configureWithStore:(id<FBSDKDataPersisting>)store
settings:(id<FBSDKSettings>)settings
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
graphRequestConnectionFactory:(id<FBSDKGraphRequestConnectionProviding>)graphRequestConnectionFactory
NS_SWIFT_NAME(configure(store:settings:graphRequestFactory:graphRequestConnectionFactory:));
+ (FBSDKAppEventsConfiguration *)cachedAppEventsConfiguration;
+ (void)loadAppEventsConfigurationWithBlock:(FBSDKAppEventsConfigurationManagerBlock)block;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,202 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsConfigurationManager.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKDataPersisting.h"
#import "FBSDKGraphRequestConnecting.h"
#import "FBSDKGraphRequestConnectionProviding.h"
#import "FBSDKGraphRequestProviding.h"
#import "FBSDKSettingsProtocol.h"
static NSString *const FBSDKAppEventsConfigurationKey = @"com.facebook.sdk:FBSDKAppEventsConfiguration";
static NSString *const FBSDKAppEventsConfigurationTimestampKey = @"com.facebook.sdk:FBSDKAppEventsConfigurationTimestamp";
static const NSTimeInterval kTimeout = 4.0;
@interface FBSDKAppEventsConfigurationManager ()
@property (nullable, nonatomic) id<FBSDKDataPersisting> store;
@property (nullable, nonatomic) id<FBSDKSettings> settings;
@property (nullable, nonatomic) id<FBSDKGraphRequestProviding> requestFactory;
@property (nullable, nonatomic) id<FBSDKGraphRequestConnectionProviding> connectionFactory;
@property (nonnull, nonatomic) FBSDKAppEventsConfiguration *configuration;
@property (nonatomic) BOOL isLoadingConfiguration;
@property (nonatomic) BOOL hasRequeryFinishedForAppStart;
@property (nullable, nonatomic) NSDate *timestamp;
@property (nullable, nonatomic) NSMutableArray *completionBlocks;
@end
@implementation FBSDKAppEventsConfigurationManager
static dispatch_once_t sharedConfigurationManagerNonce;
// Transitional singleton introduced as a way to change the usage semantics
// from a type-based interface to an instance-based interface.
// The goal of the refactor is to move callsites from:
// ClassWithoutUnderlyingInstance -> ClassRelyingOnUnderlyingInstance -> Instance
+ (FBSDKAppEventsConfigurationManager *)shared
{
static id instance;
dispatch_once(&sharedConfigurationManagerNonce, ^{
instance = [self new];
});
return instance;
}
+ (void) configureWithStore:(id<FBSDKDataPersisting>)store
settings:(id<FBSDKSettings>)settings
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
graphRequestConnectionFactory:(id<FBSDKGraphRequestConnectionProviding>)graphRequestConnectionFactory
{
[self.shared configureWithStore:store
settings:settings
graphRequestFactory:graphRequestFactory
graphRequestConnectionFactory:graphRequestConnectionFactory];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void) configureWithStore:(id<FBSDKDataPersisting>)store
settings:(id<FBSDKSettings>)settings
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
graphRequestConnectionFactory:(id<FBSDKGraphRequestConnectionProviding>)graphRequestConnectionFactory
{
self.store = store;
self.settings = settings;
self.requestFactory = graphRequestFactory;
self.connectionFactory = graphRequestConnectionFactory;
id data = [self.store objectForKey:FBSDKAppEventsConfigurationKey];
if ([data isKindOfClass:NSData.class]) {
if (@available(iOS 11.0, tvOS 11.0, *)) {
self.configuration = [NSKeyedUnarchiver unarchivedObjectOfClass:FBSDKAppEventsConfiguration.class fromData:data error:nil];
} else {
self.configuration = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
}
if (!self.configuration) {
self.configuration = [FBSDKAppEventsConfiguration defaultConfiguration];
}
self.completionBlocks = [NSMutableArray new];
self.timestamp = [self.store objectForKey:FBSDKAppEventsConfigurationTimestampKey];
}
#pragma clang diagnostic pop
+ (FBSDKAppEventsConfiguration *)cachedAppEventsConfiguration
{
return self.shared.cachedAppEventsConfiguration;
}
- (FBSDKAppEventsConfiguration *)cachedAppEventsConfiguration
{
return self.configuration;
}
+ (void)loadAppEventsConfigurationWithBlock:(FBSDKAppEventsConfigurationManagerBlock)block
{
[self.shared loadAppEventsConfigurationWithBlock:block];
}
- (void)loadAppEventsConfigurationWithBlock:(FBSDKAppEventsConfigurationManagerBlock)block
{
NSString *appID = self.settings.appID;
@synchronized(self) {
[FBSDKTypeUtility array:self.completionBlocks addObject:block];
if (!appID || (self.hasRequeryFinishedForAppStart && [self _isTimestampValid])) {
for (FBSDKAppEventsConfigurationManagerBlock completionBlock in self.completionBlocks) {
completionBlock();
}
[self.completionBlocks removeAllObjects];
return;
}
if (self.isLoadingConfiguration) {
return;
}
self.isLoadingConfiguration = true;
id<FBSDKGraphRequest> request = [self.requestFactory createGraphRequestWithGraphPath:appID
parameters:@{
@"fields" : [NSString stringWithFormat:@"app_events_config.os_version(%@)", [UIDevice currentDevice].systemVersion]
}];
id<FBSDKGraphRequestConnecting> requestConnection = [self.connectionFactory createGraphRequestConnection];
requestConnection.timeout = kTimeout;
[requestConnection addRequest:request completion:^(id<FBSDKGraphRequestConnecting> connection, id result, NSError *error) {
[self _processResponse:result error:error];
}];
[requestConnection start];
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ (void)_processResponse:(id)response
error:(NSError *)error
{
[self.shared _processResponse:response error:error];
}
- (void)_processResponse:(id)response
error:(NSError *)error
{
NSDate *date = [NSDate date];
@synchronized(self) {
self.isLoadingConfiguration = NO;
self.hasRequeryFinishedForAppStart = YES;
if (error) {
return;
}
self.configuration = [[FBSDKAppEventsConfiguration alloc] initWithJSON:response];
self.timestamp = date;
for (FBSDKAppEventsConfigurationManagerBlock completionBlock in self.completionBlocks) {
completionBlock();
}
[self.completionBlocks removeAllObjects];
}
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.configuration];
[self.store setObject:data forKey:FBSDKAppEventsConfigurationKey];
[self.store setObject:date forKey:FBSDKAppEventsConfigurationTimestampKey];
}
#pragma clang diagnostic pop
- (BOOL)_isTimestampValid
{
return self.timestamp && [[NSDate date] timeIntervalSinceDate:self.timestamp] < 3600;
}
#if DEBUG
#if FBTEST
+ (void)reset
{
[self.shared reset];
}
- (void)reset
{
// Reset the nonce so that a new instance will be created.
if (sharedConfigurationManagerNonce) {
sharedConfigurationManagerNonce = 0;
}
}
#endif
#endif
@end
@@ -0,0 +1,36 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
//typedef NS_ENUM(NSUInteger, FBSDKAdvertisingTrackingStatus);
NS_SWIFT_NAME(AppEventsConfigurationProtocol)
@protocol FBSDKAppEventsConfiguration
@property (nonatomic, readonly, assign) FBSDKAdvertisingTrackingStatus defaultATEStatus;
@property (nonatomic, readonly, assign) BOOL advertiserIDCollectionEnabled;
@property (nonatomic, readonly, assign) BOOL eventCollectionEnabled;
- (instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict;
+ (instancetype)defaultConfiguration;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,37 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_SWIFT_NAME(AppEventsConfigurationProvidingBlock)
typedef void (^FBSDKAppEventsConfigurationProvidingBlock)(void);
NS_ASSUME_NONNULL_BEGIN
@protocol FBSDKAppEventsConfiguration;
NS_SWIFT_NAME(AppEventsConfigurationProviding)
@protocol FBSDKAppEventsConfigurationProviding
+ (id<FBSDKAppEventsConfiguration>)cachedAppEventsConfiguration;
+ (void)loadAppEventsConfigurationWithBlock:(FBSDKAppEventsConfigurationProvidingBlock)block;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,30 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsDeviceInfo)
@interface FBSDKAppEventsDeviceInfo : NSObject
+ (void)extendDictionaryWithDeviceInfo:(NSMutableDictionary *)dictionary;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,292 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsDeviceInfo.h"
#import <sys/sysctl.h>
#import <sys/utsname.h>
#if !TARGET_OS_TV
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#endif
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "FBSDKAppEventsUtility.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKDynamicFrameworkLoader.h"
#import "FBSDKInternalUtility+Internal.h"
#import "FBSDKSettings+Internal.h"
#define FB_ARRAY_COUNT(x) sizeof(x) / sizeof(x[0])
static const u_int FB_GROUP1_RECHECK_DURATION = 30 * 60; // seconds
// Apple reports storage in binary gigabytes (1024^3) in their About menus, etc.
static const u_int FB_GIGABYTE = 1024 * 1024 * 1024; // bytes
@implementation FBSDKAppEventsDeviceInfo
{
// Ephemeral data, may change during the lifetime of an app. We collect them in different
// 'group' frequencies - group1 may gets collected once every 30 minutes.
// group1
NSString *_carrierName;
NSString *_timeZoneAbbrev;
unsigned long long _remainingDiskSpaceGB;
NSString *_timeZoneName;
// Persistent data, but we maintain it to make rebuilding the device info as fast as possible.
NSString *_bundleIdentifier;
NSString *_longVersion;
NSString *_shortVersion;
NSString *_sysVersion;
NSString *_machine;
NSString *_language;
unsigned long long _totalDiskSpaceGB;
unsigned long long _coreCount;
CGFloat _width;
CGFloat _height;
CGFloat _density;
// Other state
long _lastGroup1CheckTime;
BOOL _isEncodingDirty;
NSString *_encodedDeviceInfo;
}
#pragma mark - Public Methods
+ (void)extendDictionaryWithDeviceInfo:(NSMutableDictionary *)dictionary
{
[FBSDKTypeUtility dictionary:dictionary setObject:[[self sharedDeviceInfo] encodedDeviceInfo] forKey:@"extinfo"];
}
#pragma mark - Internal Methods
+ (void)initialize
{
if (self == [FBSDKAppEventsDeviceInfo class]) {
[[self sharedDeviceInfo] _collectPersistentData];
}
}
+ (instancetype)sharedDeviceInfo
{
static FBSDKAppEventsDeviceInfo *_sharedDeviceInfo = nil;
if (_sharedDeviceInfo == nil) {
_sharedDeviceInfo = [FBSDKAppEventsDeviceInfo new];
}
return _sharedDeviceInfo;
}
- (instancetype)init
{
if ((self = [super init])) {
_isEncodingDirty = YES;
}
return self;
}
- (NSString *)encodedDeviceInfo
{
@synchronized(self) {
BOOL isGroup1Expired = [self _isGroup1Expired];
BOOL isEncodingExpired = isGroup1Expired; // Can || other groups in if we add them
// As long as group1 hasn't expired, we can just return the last generated value
if (_encodedDeviceInfo && !isEncodingExpired) {
return _encodedDeviceInfo;
}
if (isGroup1Expired) {
[self _collectGroup1Data];
}
if (_isEncodingDirty) {
self.encodedDeviceInfo = [self _generateEncoding];
_isEncodingDirty = NO;
}
return _encodedDeviceInfo;
}
}
- (void)setEncodedDeviceInfo:(NSString *)encodedDeviceInfo
{
@synchronized(self) {
if (![_encodedDeviceInfo isEqualToString:encodedDeviceInfo]) {
_encodedDeviceInfo = [encodedDeviceInfo copy];
}
}
}
// This data need only be collected once.
- (void)_collectPersistentData
{
// Bundle stuff
NSBundle *mainBundle = [NSBundle mainBundle];
_bundleIdentifier = mainBundle.bundleIdentifier;
_longVersion = [mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"];
_shortVersion = [mainBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
// Locale stuff
_language = [NSLocale currentLocale].localeIdentifier;
// Device stuff
UIDevice *device = [UIDevice currentDevice];
_sysVersion = device.systemVersion;
_coreCount = [FBSDKAppEventsDeviceInfo _coreCount];
UIScreen *sc = [UIScreen mainScreen];
CGRect sr = sc.bounds;
_width = sr.size.width;
_height = sr.size.height;
_density = sc.scale;
struct utsname systemInfo;
uname(&systemInfo);
_machine = @(systemInfo.machine);
// Disk space stuff
float totalDiskSpace = [FBSDKAppEventsDeviceInfo _getTotalDiskSpace].floatValue;
_totalDiskSpaceGB = (unsigned long long)round(totalDiskSpace / FB_GIGABYTE);
}
- (BOOL)_isGroup1Expired
{
return ([self unixTimeNow] - _lastGroup1CheckTime) > FB_GROUP1_RECHECK_DURATION;
}
// This data is collected only once every GROUP1_RECHECK_DURATION.
- (void)_collectGroup1Data
{
const BOOL shouldUseCachedValues = [FBSDKSettings shouldUseCachedValuesForExpensiveMetadata];
if (!_carrierName || !shouldUseCachedValues) {
NSString *newCarrierName = [FBSDKAppEventsDeviceInfo _getCarrier];
if (!_carrierName || ![newCarrierName isEqualToString:_carrierName]) {
_carrierName = newCarrierName;
_isEncodingDirty = YES;
}
}
if (!_timeZoneName || !_timeZoneAbbrev || !shouldUseCachedValues) {
NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
NSString *timeZoneName = timeZone.name;
if (!_timeZoneName || ![timeZoneName isEqualToString:_timeZoneName]) {
_timeZoneName = timeZoneName;
_timeZoneAbbrev = timeZone.abbreviation;
_isEncodingDirty = YES;
}
}
// Remaining disk space
float remainingDiskSpace = [FBSDKAppEventsDeviceInfo _getRemainingDiskSpace].floatValue;
unsigned long long newRemainingDiskSpaceGB = (unsigned long long)round(remainingDiskSpace / FB_GIGABYTE);
if (_remainingDiskSpaceGB != newRemainingDiskSpaceGB) {
_remainingDiskSpaceGB = newRemainingDiskSpaceGB;
_isEncodingDirty = YES;
}
_lastGroup1CheckTime = [self unixTimeNow];
}
- (NSString *)_generateEncoding
{
// Keep a bit of precision on density as it's the most likely to become non-integer.
NSString *densityString = _density ? [NSString stringWithFormat:@"%.02f", _density] : @"";
NSArray *arr = @[
@"i2", // version - starts with 'i' for iOS, we'll use 'a' for Android
_bundleIdentifier ?: @"",
_longVersion ?: @"",
_shortVersion ?: @"",
_sysVersion ?: @"",
_machine ?: @"",
_language ?: @"",
_timeZoneAbbrev ?: @"",
_carrierName ?: @"",
_width ? @((unsigned long)_width) : @"",
_height ? @((unsigned long)_height) : @"",
densityString,
@(_coreCount) ?: @"",
@(_totalDiskSpaceGB) ?: @"",
@(_remainingDiskSpaceGB) ?: @"",
_timeZoneName ?: @""
];
return [FBSDKBasicUtility JSONStringForObject:arr error:NULL invalidObjectHandler:NULL];
}
#pragma mark - Helper Methods
- (NSTimeInterval)unixTimeNow
{
return round([NSDate date].timeIntervalSince1970);
}
+ (NSNumber *)_getTotalDiskSpace
{
NSDictionary *attrs = [[NSFileManager new] attributesOfFileSystemForPath:NSHomeDirectory()
error:nil];
return attrs[NSFileSystemSize];
}
+ (NSNumber *)_getRemainingDiskSpace
{
NSDictionary *attrs = [[NSFileManager new] attributesOfFileSystemForPath:NSHomeDirectory()
error:nil];
return attrs[NSFileSystemFreeSize];
}
+ (uint)_coreCount
{
return [FBSDKAppEventsDeviceInfo _readSysCtlUInt:CTL_HW type:HW_AVAILCPU];
}
+ (uint)_readSysCtlUInt:(int)ctl type:(int)type
{
int mib[2] = {ctl, type};
uint value;
size_t size = sizeof value;
if (0 != sysctl(mib, FB_ARRAY_COUNT(mib), &value, &size, NULL, 0)) {
return 0;
}
return value;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ (NSString *)_getCarrier
{
#if TARGET_OS_TV || TARGET_OS_SIMULATOR
return @"NoCarrier";
#else
// Dynamically load class for this so calling app doesn't need to link framework in.
CTTelephonyNetworkInfo *networkInfo = [[fbsdkdfl_CTTelephonyNetworkInfoClass() alloc] init];
CTCarrier *carrier = networkInfo.subscriberCellularProvider;
return carrier.carrierName ?: @"NoCarrier";
#endif
}
#pragma clang diagnostic pop
@end
@@ -0,0 +1,38 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FBSDKNumberParsing <NSObject>
- (NSNumber *)parseNumberFrom:(NSString *)string;
@end
NS_SWIFT_NAME(AppEventsNumberParser)
@interface FBSDKAppEventsNumberParser : NSObject <FBSDKNumberParsing>
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithLocale:(NSLocale *)locale;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,64 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsNumberParser.h"
@implementation FBSDKAppEventsNumberParser
{
NSLocale *_locale;
}
- (instancetype)initWithLocale:(NSLocale *)locale
{
if ((self = [self init])) {
_locale = locale;
}
return self;
}
- (NSNumber *)parseNumberFrom:(NSString *)string
{
NSNumber *value = @0;
NSString *ds = [_locale objectForKey:NSLocaleDecimalSeparator] ?: @".";
NSString *gs = [_locale objectForKey:NSLocaleGroupingSeparator] ?: @",";
NSString *separators = [ds stringByAppendingString:gs];
NSString *regex = [NSString stringWithFormat:@"[+-]?([0-9]+[%1$@]?)?[%1$@]?([0-9]+[%1$@]?)+", separators];
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:regex
options:0
error:nil];
NSTextCheckingResult *match = [re firstMatchInString:string
options:0
range:NSMakeRange(0, string.length)];
if (match) {
NSString *validText = [string substringWithRange:match.range];
NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.locale = _locale;
formatter.numberStyle = NSNumberFormatterDecimalStyle;
value = [formatter numberFromString:validText];
if (nil == value) {
value = @(validText.floatValue);
}
}
return value;
}
@end
@@ -0,0 +1,32 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsParameterProcessing)
@protocol FBSDKAppEventsParameterProcessing
- (void)enable;
- (nullable NSDictionary<NSString *, id> *)processParameters:(nullable NSDictionary<NSString *, id> *)parameters
eventName:(NSString *)eventName;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,35 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsReporter)
@protocol FBSDKAppEventsReporter
- (void)enable;
- (void)recordAndUpdateEvent:(NSString *)event
currency:(nullable NSString *)currency
value:(nullable NSNumber *)value
parameters:(nullable NSDictionary *)parameters
NS_SWIFT_NAME(recordAndUpdate(event:currency:value:parameters:));
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,45 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKEventsProcessing.h"
// this type is not thread safe.
NS_SWIFT_NAME(AppEventsState)
@interface FBSDKAppEventsState : NSObject<NSCopying, NSSecureCoding>
@property (nonatomic, readonly, copy) NSArray *events;
@property (nonatomic, readonly, assign) NSUInteger numSkipped;
@property (nonatomic, readonly, copy) NSString *tokenString;
@property (nonatomic, readonly, copy) NSString *appID;
@property (nonatomic, readonly, getter=areAllEventsImplicit) BOOL allEventsImplicit;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)initWithToken:(NSString *)tokenString appID:(NSString *)appID NS_DESIGNATED_INITIALIZER;
- (void)addEvent:(NSDictionary *)eventDictionary isImplicit:(BOOL)isImplicit;
- (void)addEventsFromAppEventState:(FBSDKAppEventsState *)appEventsState;
- (BOOL)isCompatibleWithAppEventsState:(FBSDKAppEventsState *)appEventsState;
- (BOOL)isCompatibleWithTokenString:(NSString *)tokenString appID:(NSString *)appID;
- (NSString *)JSONStringForEventsIncludingImplicitEvents:(BOOL)includeImplicitEvents;
- (NSString *)extractReceiptData;
+ (void)configureWithEventProcessors:(NSArray<id<FBSDKEventsProcessing>> *)eventProcessors;
@end
@@ -0,0 +1,208 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsState.h"
#import "FBSDKCoreKitBasicsImport.h"
#define FBSDK_APPEVENTSTATE_ISIMPLICIT_KEY @"isImplicit"
#define FBSDK_APPEVENTSSTATE_MAX_EVENTS 1000
#define FBSDK_APPEVENTSSTATE_APPID_KEY @"appID"
#define FBSDK_APPEVENTSSTATE_EVENTS_KEY @"events"
#define FBSDK_APPEVENTSSTATE_NUMSKIPPED_KEY @"numSkipped"
#define FBSDK_APPEVENTSSTATE_TOKENSTRING_KEY @"tokenString"
#define FBSDK_APPEVENTSTATE_RECEIPTDATA_KEY @"receipt_data"
#define FBSDK_APPEVENTSTATE_RECEIPTID_KEY @"receipt_id"
static NSArray<id<FBSDKEventsProcessing>> *_eventProcessors;
@implementation FBSDKAppEventsState
{
NSMutableArray *_mutableEvents;
}
+ (void)configureWithEventProcessors:(nonnull NSArray<id<FBSDKEventsProcessing>> *)eventProcessors
{
_eventProcessors = eventProcessors;
}
- (instancetype)initWithToken:(NSString *)tokenString appID:(NSString *)appID
{
if ((self = [super init])) {
_tokenString = [tokenString copy];
_appID = [appID copy];
_mutableEvents = [NSMutableArray array];
}
return self;
}
- (instancetype)copyWithZone:(NSZone *)zone
{
FBSDKAppEventsState *copy = [[FBSDKAppEventsState allocWithZone:zone] initWithToken:_tokenString appID:_appID];
if (copy) {
[copy->_mutableEvents addObjectsFromArray:_mutableEvents];
copy->_numSkipped = _numSkipped;
}
return copy;
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
NSString *appID = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDK_APPEVENTSSTATE_APPID_KEY];
NSString *tokenString = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDK_APPEVENTSSTATE_TOKENSTRING_KEY];
NSArray *events = [FBSDKTypeUtility arrayValue:[decoder decodeObjectOfClasses:
[NSSet setWithArray:@[NSArray.class, NSDictionary.class]]
forKey:FBSDK_APPEVENTSSTATE_EVENTS_KEY]];
NSUInteger numSkipped = [[decoder decodeObjectOfClass:[NSNumber class] forKey:FBSDK_APPEVENTSSTATE_NUMSKIPPED_KEY] unsignedIntegerValue];
if ((self = [self initWithToken:tokenString appID:appID])) {
_mutableEvents = [NSMutableArray arrayWithArray:events];
_numSkipped = numSkipped;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:_appID forKey:FBSDK_APPEVENTSSTATE_APPID_KEY];
[encoder encodeObject:_tokenString forKey:FBSDK_APPEVENTSSTATE_TOKENSTRING_KEY];
[encoder encodeObject:@(_numSkipped) forKey:FBSDK_APPEVENTSSTATE_NUMSKIPPED_KEY];
[encoder encodeObject:_mutableEvents forKey:FBSDK_APPEVENTSSTATE_EVENTS_KEY];
}
#pragma mark - Implementation
- (NSArray *)events
{
return [_mutableEvents copy];
}
- (void)addEventsFromAppEventState:(FBSDKAppEventsState *)appEventsState
{
NSArray *toAdd = appEventsState->_mutableEvents;
NSInteger excess = _mutableEvents.count + toAdd.count - FBSDK_APPEVENTSSTATE_MAX_EVENTS;
if (excess > 0) {
NSInteger range = FBSDK_APPEVENTSSTATE_MAX_EVENTS - _mutableEvents.count;
toAdd = [toAdd subarrayWithRange:NSMakeRange(0, range)];
_numSkipped += excess;
}
[_mutableEvents addObjectsFromArray:toAdd];
}
- (void)addEvent:(NSDictionary *)eventDictionary
isImplicit:(BOOL)isImplicit
{
if (_mutableEvents.count >= FBSDK_APPEVENTSSTATE_MAX_EVENTS) {
_numSkipped++;
} else {
[FBSDKTypeUtility array:_mutableEvents addObject:@{
@"event" : [eventDictionary mutableCopy],
FBSDK_APPEVENTSTATE_ISIMPLICIT_KEY : @(isImplicit)
}];
}
}
- (NSString *)extractReceiptData
{
NSMutableString *receipts_string = [NSMutableString string];
NSInteger transactionId = 1;
for (NSMutableDictionary *events in _mutableEvents) {
NSMutableDictionary *event = events[@"event"];
NSString *receipt = event[@"receipt_data"];
// Add receipt id as the identifier for receipt data in event parameter.
// Receipt data will be sent as post parameter rather than the event parameter
if (receipt) {
NSString *idKey = [NSString stringWithFormat:@"receipt_%ld", (long)transactionId];
[FBSDKTypeUtility dictionary:event setObject:idKey forKey:FBSDK_APPEVENTSTATE_RECEIPTID_KEY];
NSString *receiptWithId = [NSString stringWithFormat:@"%@::%@;;;", idKey, receipt];
[receipts_string appendString:receiptWithId];
transactionId++;
}
}
return receipts_string;
}
- (BOOL)areAllEventsImplicit
{
for (NSDictionary *event in _mutableEvents) {
if (![[event valueForKey:FBSDK_APPEVENTSTATE_ISIMPLICIT_KEY] boolValue]) {
return NO;
}
}
return YES;
}
- (BOOL)isCompatibleWithAppEventsState:(FBSDKAppEventsState *)appEventsState
{
return ([self isCompatibleWithTokenString:appEventsState.tokenString appID:appEventsState.appID]);
}
- (BOOL)isCompatibleWithTokenString:(NSString *)tokenString appID:(NSString *)appID
{
// token strings can be nil (e.g., no user token) but appIDs should not.
BOOL tokenCompatible = ([self.tokenString isEqualToString:tokenString]
|| (self.tokenString == nil && tokenString == nil));
return (tokenCompatible
&& [self.appID isEqualToString:appID]);
}
- (NSString *)JSONStringForEventsIncludingImplicitEvents:(BOOL)includeImplicitEvents
{
if (_eventProcessors != nil) {
for (id<FBSDKEventsProcessing> processor in _eventProcessors) {
[processor processEvents:_mutableEvents];
}
}
NSMutableArray *events = [[NSMutableArray alloc] initWithCapacity:_mutableEvents.count];
for (NSDictionary *eventAndImplicitFlag in _mutableEvents) {
const BOOL isImplicitEvent = [eventAndImplicitFlag[FBSDK_APPEVENTSTATE_ISIMPLICIT_KEY] boolValue];
if (!includeImplicitEvents && isImplicitEvent) {
continue;
}
NSMutableDictionary *event = eventAndImplicitFlag[@"event"];
NSAssert(event != nil, @"event cannot be nil");
[event removeObjectForKey:FBSDK_APPEVENTSTATE_RECEIPTDATA_KEY];
[FBSDKTypeUtility array:events addObject:event];
}
return [FBSDKBasicUtility JSONStringForObject:events error:NULL invalidObjectHandler:NULL];
}
#ifdef DEBUG
#if FBTEST
+ (NSArray<id<FBSDKEventsProcessing>> *)eventProcessors
{
return _eventProcessors;
}
#endif
#endif
@end
@@ -0,0 +1,28 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKAppEventsStateProviding.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsStateFactory)
@interface FBSDKAppEventsStateFactory : NSObject<FBSDKAppEventsStateProviding>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,32 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsStateFactory.h"
#import <Foundation/Foundation.h>
#import "FBSDKAppEventsState.h"
@implementation FBSDKAppEventsStateFactory
- (FBSDKAppEventsState *)createStateWithToken:(NSString *)tokenString appID:(NSString *)appID
{
return [[FBSDKAppEventsState alloc] initWithToken:tokenString appID:appID];
}
@end
@@ -0,0 +1,29 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKAppEventsStateManager.h"
#import "FBSDKAppEventsStatePersisting.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKAppEventsStateManager (AppEventsStatePersisting) <FBSDKAppEventsStatePersisting>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,38 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@class FBSDKAppEventsState;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsStateManager)
@interface FBSDKAppEventsStateManager : NSObject
@property (class, readonly, nonatomic) FBSDKAppEventsStateManager* shared;
- (void)clearPersistedAppEventsStates;
// reads all saved event states, appends the param, and writes them all.
- (void)persistAppEventsData:(FBSDKAppEventsState *)appEventsState;
// returns the array of saved app event states and deletes them.
- (NSArray *)retrievePersistedAppEventsStates;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,128 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsStateManager.h"
#import <Foundation/Foundation.h>
#import "FBSDKAppEventsState.h"
#import "FBSDKAppEventsUtility.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKLogger.h"
#import "FBSDKSettings.h"
#import "FBSDKUnarchiverProvider.h"
@interface FBSDKAppEventsStateManager (Internal)
// A quick optimization to allow returning empty array if we know there are no persisted events.
@property (nonatomic, readwrite, assign) BOOL canSkipDiskCheck;
@end
@implementation FBSDKAppEventsStateManager
{
BOOL _canSkipDiskCheck;
}
- (instancetype)init
{
self.canSkipDiskCheck = NO;
return self;
}
- (void)setCanSkipDiskCheck:(BOOL)canSkipDiskCheck
{
_canSkipDiskCheck = canSkipDiskCheck;
}
- (BOOL)canSkipDiskCheck
{
return _canSkipDiskCheck;
}
+ (FBSDKAppEventsStateManager *)shared
{
static dispatch_once_t nonce;
static FBSDKAppEventsStateManager *instance = nil;
dispatch_once(&nonce, ^{
instance = [FBSDKAppEventsStateManager new];
});
return instance;
}
- (void)clearPersistedAppEventsStates
{
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorAppEvents
logEntry:@"FBSDKAppEvents Persist: Clearing"];
[[NSFileManager defaultManager] removeItemAtPath:[self filePath]
error:NULL];
self.canSkipDiskCheck = YES;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)persistAppEventsData:(FBSDKAppEventsState *)appEventsState
{
NSString *msg = [NSString stringWithFormat:@"FBSDKAppEvents Persist: Writing %lu events", (unsigned long)appEventsState.events.count];
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorAppEvents
logEntry:msg];
if (!appEventsState.events.count) {
return;
}
NSMutableArray *existingEvents = [NSMutableArray arrayWithArray:[self retrievePersistedAppEventsStates]];
[FBSDKTypeUtility array:existingEvents addObject:appEventsState];
[NSKeyedArchiver archiveRootObject:existingEvents toFile:[self filePath]];
self.canSkipDiskCheck = NO;
}
- (NSArray *)retrievePersistedAppEventsStates
{
NSMutableArray *eventsStates = [NSMutableArray array];
if (!self.canSkipDiskCheck) {
NSData *data = [[NSData alloc] initWithContentsOfFile:[self filePath] options:NSDataReadingMappedIfSafe error:NULL];
id<FBSDKObjectDecoding> unarchiver = [FBSDKUnarchiverProvider createSecureUnarchiverFor:data];
@try {
NSArray<FBSDKAppEventsState *> *retrievedEvents = [unarchiver decodeObjectOfClasses:
[NSSet setWithObjects:NSArray.class, FBSDKAppEventsState.class, NSDictionary.class, nil]
forKey:NSKeyedArchiveRootObjectKey];
[eventsStates addObjectsFromArray:[FBSDKTypeUtility arrayValue:retrievedEvents]];
} @catch (NSException *ex) {
// ignore decoding exceptions from previous versions of the archive, etc
}
NSString *msg = [NSString stringWithFormat:@"FBSDKAppEvents Persist: Read %lu event states. First state has %lu events",
(unsigned long)eventsStates.count,
(unsigned long)(eventsStates.count > 0 ? ((FBSDKAppEventsState *)[FBSDKTypeUtility array:eventsStates objectAtIndex:0]).events.count : 0)];
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorAppEvents
logEntry:msg];
[self clearPersistedAppEventsStates];
}
return eventsStates;
}
#pragma clang diagnostic pop
#pragma mark - Private Helpers
- (NSString *)filePath
{
return [FBSDKBasicUtility persistenceFilePath:@"com-facebook-sdk-AppEventsPersistedEvents.json"];
}
@end
@@ -0,0 +1,32 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@class FBSDKAppEventsState;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppEventsStatePersisting)
@protocol FBSDKAppEventsStatePersisting
- (void)clearPersistedAppEventsStates;
- (void)persistAppEventsData:(FBSDKAppEventsState *)appEventsState;
- (NSArray *)retrievePersistedAppEventsStates;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,28 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@class FBSDKAppEventsState;
NS_SWIFT_NAME(AppEventsStateProviding)
@protocol FBSDKAppEventsStateProviding
- (FBSDKAppEventsState*) createStateWithToken:(NSString *)tokenString appID:(NSString *)appID NS_SWIFT_NAME(createState(tokenString:appID:));
@end
@@ -0,0 +1,490 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsUtility.h"
#import <AdSupport/AdSupport.h>
#import <objc/runtime.h>
#import "FBSDKAccessToken.h"
#import "FBSDKAppEvents.h"
#import "FBSDKAppEventsConfiguration.h"
#import "FBSDKAppEventsConfigurationManager.h"
#import "FBSDKAppEventsDeviceInfo.h"
#import "FBSDKConstants.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKDynamicFrameworkLoader.h"
#import "FBSDKError+Internal.h"
#import "FBSDKInternalUtility+Internal.h"
#import "FBSDKLogger.h"
#import "FBSDKSettings.h"
#import "FBSDKSettings+Internal.h"
#define FBSDK_APPEVENTSUTILITY_ANONYMOUSIDFILENAME @"com-facebook-sdk-PersistedAnonymousID.json"
#define FBSDK_APPEVENTSUTILITY_ANONYMOUSID_KEY @"anon_id"
#define FBSDK_APPEVENTSUTILITY_MAX_IDENTIFIER_LENGTH 40
static NSArray<NSString *> *standardEvents;
static ASIdentifierManager *_cachedAdvertiserIdentifierManager;
@implementation FBSDKAppEventsUtility
+ (void)initialize
{
standardEvents = @[
FBSDKAppEventNameCompletedRegistration,
FBSDKAppEventNameViewedContent,
FBSDKAppEventNameSearched,
FBSDKAppEventNameRated,
FBSDKAppEventNameCompletedTutorial,
FBSDKAppEventNameAddedToCart,
FBSDKAppEventNameAddedToWishlist,
FBSDKAppEventNameInitiatedCheckout,
FBSDKAppEventNameAddedPaymentInfo,
FBSDKAppEventNamePurchased,
FBSDKAppEventNameAchievedLevel,
FBSDKAppEventNameUnlockedAchievement,
FBSDKAppEventNameSpentCredits,
FBSDKAppEventNameContact,
FBSDKAppEventNameCustomizeProduct,
FBSDKAppEventNameDonate,
FBSDKAppEventNameFindLocation,
FBSDKAppEventNameSchedule,
FBSDKAppEventNameStartTrial,
FBSDKAppEventNameSubmitApplication,
FBSDKAppEventNameSubscribe,
FBSDKAppEventNameAdImpression,
FBSDKAppEventNameAdClick
];
}
// Transitional singleton introduced as a way to change the usage semantics
// from a type-based interface to an instance-based interface.
// The goal is to move from:
// ClassWithoutUnderlyingInstance -> ClassRelyingOnUnderlyingInstance -> Instance
+ (instancetype)shared
{
static dispatch_once_t nonce;
static id instance;
dispatch_once(&nonce, ^{
instance = [self new];
});
return instance;
}
+ (NSMutableDictionary *)activityParametersDictionaryForEvent:(NSString *)eventCategory
shouldAccessAdvertisingID:(BOOL)shouldAccessAdvertisingID
{
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
[FBSDKTypeUtility dictionary:parameters setObject:eventCategory forKey:@"event"];
if (shouldAccessAdvertisingID) {
NSString *advertiserID = [self.shared advertiserID];
[FBSDKTypeUtility dictionary:parameters setObject:advertiserID forKey:@"advertiser_id"];
}
[FBSDKTypeUtility dictionary:parameters setObject:[FBSDKBasicUtility anonymousID] forKey:FBSDK_APPEVENTSUTILITY_ANONYMOUSID_KEY];
FBSDKAdvertisingTrackingStatus advertisingTrackingStatus = [FBSDKSettings advertisingTrackingStatus];
if (advertisingTrackingStatus != FBSDKAdvertisingTrackingUnspecified) {
[FBSDKTypeUtility dictionary:parameters setObject:@([FBSDKSettings isAdvertiserTrackingEnabled]).stringValue forKey:@"advertiser_tracking_enabled"];
}
NSString *userData = [FBSDKAppEvents getUserData];
if (userData) {
[FBSDKTypeUtility dictionary:parameters setObject:userData forKey:@"ud"];
}
[FBSDKTypeUtility dictionary:parameters setObject:@(!FBSDKSettings.limitEventAndDataUsage).stringValue forKey:@"application_tracking_enabled"];
[FBSDKTypeUtility dictionary:parameters setObject:@(FBSDKSettings.advertiserIDCollectionEnabled).stringValue forKey:@"advertiser_id_collection_enabled"];
NSString *userID = [FBSDKAppEvents userID];
if (userID) {
[FBSDKTypeUtility dictionary:parameters setObject:userID forKey:@"app_user_id"];
}
NSDictionary<NSString *, id> *dataProcessingOptions = [FBSDKSettings dataProcessingOptions];
if (dataProcessingOptions) {
NSArray<NSString *> *options = (NSArray<NSString *> *)dataProcessingOptions[DATA_PROCESSING_OPTIONS];
if (options && [options isKindOfClass:NSArray.class]) {
NSString *optionsString = [FBSDKBasicUtility JSONStringForObject:options error:nil invalidObjectHandler:nil];
[FBSDKTypeUtility dictionary:parameters
setObject:optionsString
forKey:DATA_PROCESSING_OPTIONS];
}
[FBSDKTypeUtility dictionary:parameters
setObject:dataProcessingOptions[DATA_PROCESSING_OPTIONS_COUNTRY]
forKey:DATA_PROCESSING_OPTIONS_COUNTRY];
[FBSDKTypeUtility dictionary:parameters
setObject:dataProcessingOptions[DATA_PROCESSING_OPTIONS_STATE]
forKey:DATA_PROCESSING_OPTIONS_STATE];
}
[FBSDKAppEventsDeviceInfo extendDictionaryWithDeviceInfo:parameters];
static dispatch_once_t fetchBundleOnce;
static NSMutableArray *urlSchemes;
dispatch_once(&fetchBundleOnce, ^{
NSBundle *mainBundle = [NSBundle mainBundle];
urlSchemes = [NSMutableArray new];
for (NSDictionary<NSString *, id> *fields in [mainBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]) {
NSArray<NSString *> *schemesForType = fields[@"CFBundleURLSchemes"];
if (schemesForType) {
[urlSchemes addObjectsFromArray:schemesForType];
}
}
});
if (urlSchemes.count > 0) {
[FBSDKTypeUtility dictionary:parameters setObject:[FBSDKBasicUtility JSONStringForObject:urlSchemes error:NULL invalidObjectHandler:NULL] forKey:@"url_schemes"];
}
return parameters;
}
- (NSString *)advertiserID
{
BOOL shouldUseCachedManagerIfAvailable = [FBSDKSettings shouldUseCachedValuesForExpensiveMetadata];
id<FBSDKDynamicFrameworkResolving> dynamicFrameworkResolver = FBSDKDynamicFrameworkLoader.shared;
return [self _advertiserIDFromDynamicFrameworkResolver:dynamicFrameworkResolver
shouldUseCachedManager:shouldUseCachedManagerIfAvailable];
}
- (NSString *)_advertiserIDFromDynamicFrameworkResolver:(id<FBSDKDynamicFrameworkResolving>)dynamicFrameworkResolver
shouldUseCachedManager:(BOOL)shouldUseCachedManager
{
if (!FBSDKSettings.isAdvertiserIDCollectionEnabled) {
return nil;
}
if (@available(iOS 14.0, *)) {
if (![FBSDKAppEventsConfigurationManager cachedAppEventsConfiguration].advertiserIDCollectionEnabled) {
return nil;
}
}
ASIdentifierManager *manager = [self _asIdentifierManagerWithShouldUseCachedManager:shouldUseCachedManager
dynamicFrameworkResolver:dynamicFrameworkResolver];
return manager.advertisingIdentifier.UUIDString;
}
- (ASIdentifierManager *)_asIdentifierManagerWithShouldUseCachedManager:(BOOL)shouldUseCachedManager
dynamicFrameworkResolver:(id<FBSDKDynamicFrameworkResolving>)dynamicFrameworkResolver
{
if (shouldUseCachedManager && _cachedAdvertiserIdentifierManager) {
return _cachedAdvertiserIdentifierManager;
}
Class ASIdentifierManagerClass = [dynamicFrameworkResolver asIdentifierManagerClass];
ASIdentifierManager *manager = [ASIdentifierManagerClass sharedManager];
if (shouldUseCachedManager) {
_cachedAdvertiserIdentifierManager = manager;
} else {
_cachedAdvertiserIdentifierManager = nil;
}
return manager;
}
+ (BOOL)isStandardEvent:(nullable NSString *)event
{
if (!event) {
return NO;
}
return [standardEvents containsObject:event];
}
#pragma mark - Internal, for testing
+ (void)clearLibraryFiles
{
[[NSFileManager defaultManager] removeItemAtPath:[[self class] persistenceFilePath:FBSDK_APPEVENTSUTILITY_ANONYMOUSIDFILENAME]
error:NULL];
[[NSFileManager defaultManager] removeItemAtPath:[[self class] persistenceFilePath:@"com-facebook-sdk-AppEventsTimeSpent.json"]
error:NULL];
}
+ (void)ensureOnMainThread:(NSString *)methodName className:(NSString *)className
{
FBSDKConditionalLog(
[NSThread isMainThread],
FBSDKLoggingBehaviorDeveloperErrors,
@"*** <%@, %@> is not called on the main thread. This can lead to errors.",
methodName,
className
);
}
+ (NSString *)flushReasonToString:(FBSDKAppEventsFlushReason)flushReason
{
NSString *result = @"Unknown";
switch (flushReason) {
case FBSDKAppEventsFlushReasonExplicit:
result = @"Explicit";
break;
case FBSDKAppEventsFlushReasonTimer:
result = @"Timer";
break;
case FBSDKAppEventsFlushReasonSessionChange:
result = @"SessionChange";
break;
case FBSDKAppEventsFlushReasonPersistedEvents:
result = @"PersistedEvents";
break;
case FBSDKAppEventsFlushReasonEventThreshold:
result = @"EventCountThreshold";
break;
case FBSDKAppEventsFlushReasonEagerlyFlushingEvent:
result = @"EagerlyFlushingEvent";
break;
}
return result;
}
+ (void)logAndNotify:(NSString *)msg
{
[[self class] logAndNotify:msg allowLogAsDeveloperError:YES];
}
+ (void)logAndNotify:(NSString *)msg allowLogAsDeveloperError:(BOOL)allowLogAsDeveloperError
{
NSString *behaviorToLog = FBSDKLoggingBehaviorAppEvents;
if (allowLogAsDeveloperError) {
if ([FBSDKSettings.loggingBehaviors containsObject:FBSDKLoggingBehaviorDeveloperErrors]) {
// Rather than log twice, prefer 'DeveloperErrors' if it's set over AppEvents.
behaviorToLog = FBSDKLoggingBehaviorDeveloperErrors;
}
}
[FBSDKLogger singleShotLogEntry:behaviorToLog logEntry:msg];
NSError *error = [FBSDKError errorWithCode:FBSDKErrorAppEventsFlush message:msg];
[[NSNotificationCenter defaultCenter] postNotificationName:FBSDKAppEventsLoggingResultNotification object:error];
}
+ (BOOL) matchString:(NSString *)string
firstCharacterSet:(NSCharacterSet *)firstCharacterSet
restOfStringCharacterSet:(NSCharacterSet *)restOfStringCharacterSet
{
if (string.length == 0) {
return NO;
}
for (NSUInteger i = 0; i < string.length; i++) {
const unichar c = [string characterAtIndex:i];
if (i == 0) {
if (![firstCharacterSet characterIsMember:c]) {
return NO;
}
} else {
if (![restOfStringCharacterSet characterIsMember:c]) {
return NO;
}
}
}
return YES;
}
+ (BOOL)regexValidateIdentifier:(NSString *)identifier
{
static NSCharacterSet *firstCharacterSet;
static NSCharacterSet *restOfStringCharacterSet;
static dispatch_once_t onceToken;
static NSMutableSet *cachedIdentifiers;
dispatch_once(&onceToken, ^{
NSMutableCharacterSet *mutableSet = [NSMutableCharacterSet alphanumericCharacterSet];
[mutableSet addCharactersInString:@"_"];
firstCharacterSet = [mutableSet copy];
[mutableSet addCharactersInString:@"- "];
restOfStringCharacterSet = [mutableSet copy];
cachedIdentifiers = [NSMutableSet new];
});
@synchronized(self) {
if (![cachedIdentifiers containsObject:identifier]) {
if ([self matchString:identifier
firstCharacterSet:firstCharacterSet
restOfStringCharacterSet:restOfStringCharacterSet]) {
[cachedIdentifiers addObject:identifier];
} else {
return NO;
}
}
}
return YES;
}
+ (BOOL)validateIdentifier:(NSString *)identifier
{
if (identifier == nil || identifier.length == 0 || identifier.length > FBSDK_APPEVENTSUTILITY_MAX_IDENTIFIER_LENGTH || ![[self class] regexValidateIdentifier:identifier]) {
[[self class] logAndNotify:[NSString stringWithFormat:@"Invalid identifier: '%@'. Must be between 1 and %d characters, and must be contain only alphanumerics, _, - or spaces, starting with alphanumeric or _.",
identifier, FBSDK_APPEVENTSUTILITY_MAX_IDENTIFIER_LENGTH]];
return NO;
}
return YES;
}
// Given a candidate token (which may be nil), find the real token to string to use.
// Precedence: 1) provided token, 2) current token, 3) app | client token, 4) fully anonymous session.
+ (NSString *)tokenStringToUseFor:(FBSDKAccessToken *)token
{
if (!token) {
token = [FBSDKAccessToken currentAccessToken];
}
NSString *loggingOverrideAppID = [FBSDKAppEvents loggingOverrideAppID];
NSString *appID = loggingOverrideAppID ?: token.appID ?: [FBSDKSettings appID];
NSString *tokenString = token.tokenString;
NSString *clientTokenString = [FBSDKSettings clientToken];
if (![appID isEqualToString:token.appID]) {
// If there's a logging override app id present
// then we don't want to use the client token since the client token
// is intended to match up with the primary app id
// and AppEvents doesn't require a client token.
if (clientTokenString && loggingOverrideAppID) {
tokenString = nil;
} else if (clientTokenString && appID && ([appID isEqualToString:token.appID] || token == nil)) {
tokenString = [NSString stringWithFormat:@"%@|%@", appID, clientTokenString];
} else if (appID) {
tokenString = nil;
}
}
return tokenString;
}
+ (NSTimeInterval)unixTimeNow
{
return round([NSDate date].timeIntervalSince1970);
}
+ (NSTimeInterval)convertToUnixTime:(NSDate *)date
{
return round([date timeIntervalSince1970]);
}
+ (BOOL)isDebugBuild
{
#if TARGET_OS_SIMULATOR
return YES;
#else
BOOL isDevelopment = NO;
// There is no provisioning profile in AppStore Apps.
@try {
NSData *data = [NSData dataWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"embedded" ofType:@"mobileprovision"]];
if (data) {
const char *bytes = [data bytes];
NSMutableString *profile = [[NSMutableString alloc] initWithCapacity:data.length];
for (NSUInteger i = 0; i < data.length; i++) {
[profile appendFormat:@"%c", bytes[i]];
}
// Look for debug value, if detected we're in a development build.
NSString *cleared = [[profile componentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet] componentsJoinedByString:@""];
isDevelopment = ([cleared rangeOfString:@"<key>get-task-allow</key><true/>"].length > 0);
}
return isDevelopment;
} @catch (NSException *exception) {}
return NO;
#endif
}
+ (BOOL)shouldDropAppEvent
{
if (@available(iOS 14.0, *)) {
if ([FBSDKSettings advertisingTrackingStatus] == FBSDKAdvertisingTrackingDisallowed && ![FBSDKAppEventsConfigurationManager cachedAppEventsConfiguration].eventCollectionEnabled) {
return YES;
}
}
return NO;
}
+ (BOOL)isSensitiveUserData:(NSString *)text
{
if (0 == text.length) {
return NO;
}
return [self isEmailAddress:text] || [self isCreditCardNumber:text];
}
+ (BOOL)isCreditCardNumber:(NSString *)text
{
text = [[text componentsSeparatedByCharactersInSet:[NSCharacterSet.decimalDigitCharacterSet invertedSet]] componentsJoinedByString:@""];
if (text.doubleValue == 0) {
return NO;
}
if (text.length < 9 || text.length > 21) {
return NO;
}
const char *chars = [text cStringUsingEncoding:NSUTF8StringEncoding];
if (NULL == chars) {
return NO;
}
BOOL isOdd = YES;
int oddSum = 0;
int evenSum = 0;
for (int i = (int)text.length - 1; i >= 0; i--) {
int digit = chars[i] - '0';
if (isOdd) {
oddSum += digit;
} else {
evenSum += digit / 5 + (2 * digit) % 10;
}
isOdd = !isOdd;
}
return ((oddSum + evenSum) % 10 == 0);
}
+ (BOOL)isEmailAddress:(NSString *)text
{
NSString *pattern = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
NSUInteger matches = [regex numberOfMatchesInString:text options:0 range:NSMakeRange(0, [text length])];
return matches > 0;
}
#if DEBUG
#if FBTEST
+ (ASIdentifierManager *)cachedAdvertiserIdentifierManager
{
return _cachedAdvertiserIdentifierManager;
}
+ (void)setCachedAdvertiserIdentifierManager:(ASIdentifierManager *)manager
{
_cachedAdvertiserIdentifierManager = manager;
}
#endif
#endif
@end
@@ -0,0 +1,34 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AppStoreReceiptProviding)
@protocol FBSDKAppStoreReceiptProviding
@property (nullable, readonly, copy) NSURL *appStoreReceiptURL;
@end
// Default conformance to the AppStoreReceiptProvider protocol
@interface NSBundle () <FBSDKAppStoreReceiptProviding>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,33 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@protocol FBSDKAtePublishing;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AtePublisherCreating)
@protocol FBSDKAtePublisherCreating
- (nullable id<FBSDKAtePublishing>)createPublisherWithAppID:(NSString *)appID
NS_SWIFT_NAME(createPublisher(appID:));
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,41 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKAtePublisherCreating.h"
@protocol FBSDKDataPersisting;
@protocol FBSDKGraphRequestProviding;
@protocol FBSDKSettings;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AtePublisherFactory)
@interface FBSDKAtePublisherFactory : NSObject<FBSDKAtePublisherCreating>
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithStore:(id<FBSDKDataPersisting>)store
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
settings:(id<FBSDKSettings>)settings;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,58 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAtePublisherFactory.h"
#import "FBSDKAppEventsAtePublisher.h"
#import "FBSDKDataPersisting.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKAtePublisherFactory ()
@property (nonnull, nonatomic, readonly) id<FBSDKGraphRequestProviding> graphRequestFactory;
@property (nonnull, nonatomic, readonly) id<FBSDKSettings> settings;
@property (nonnull, nonatomic, readonly) id<FBSDKDataPersisting> store;
@end
@implementation FBSDKAtePublisherFactory
- (instancetype)initWithStore:(id<FBSDKDataPersisting>)store
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
settings:(id<FBSDKSettings>)settings
{
if ((self = [super init])) {
_store = store;
_graphRequestFactory = graphRequestFactory;
_settings = settings;
}
return self;
}
- (nullable id<FBSDKAtePublishing>)createPublisherWithAppID:(NSString *)appID
{
return [[FBSDKAppEventsAtePublisher alloc] initWithAppIdentifier:appID
graphRequestFactory:self.graphRequestFactory
settings:self.settings
store:self.store];
}
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,30 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AtePublishing)
@protocol FBSDKAtePublishing <NSObject>
- (void)publishATE;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,25 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_SWIFT_NAME(EventsProcessing)
@protocol FBSDKEventsProcessing
- (void)processEvents:(NSMutableArray<NSDictionary<NSString *, id> *> *)events;
@end
@@ -0,0 +1,30 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(FeatureExtracting)
@protocol FBSDKFeatureExtracting
+ (nullable float *)getDenseFeatures:(NSDictionary *)viewHierarchy;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,34 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(HybridAppEventsScriptMessageHandler)
@interface FBSDKHybridAppEventsScriptMessageHandler : NSObject <WKScriptMessageHandler>
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,94 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKHybridAppEventsScriptMessageHandler.h"
#import "FBSDKAppEvents+EventLogging.h"
#import "FBSDKAppEvents+Internal.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKEventLogging.h"
NSString *const FBSDKAppEventsWKWebViewMessagesPixelReferralParamKey = @"_fb_pixel_referral_id";
@protocol FBSDKEventLogging;
@class WKUserContentController;
@interface FBSDKHybridAppEventsScriptMessageHandler ()
@property (nonatomic) id<FBSDKEventLogging> eventLogger;
@end
@implementation FBSDKHybridAppEventsScriptMessageHandler
- (instancetype)init
{
return [self initWithEventLogger:FBSDKAppEvents.singleton];
}
- (instancetype)initWithEventLogger:(id<FBSDKEventLogging>)eventLogger
{
if ((self = [super init])) {
_eventLogger = eventLogger;
}
return self;
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([message.name isEqualToString:FBSDKAppEventsWKWebViewMessagesHandlerKey]) {
NSDictionary *body = [FBSDKTypeUtility dictionaryValue:message.body];
if (!body) {
return;
}
NSString *event = body[FBSDKAppEventsWKWebViewMessagesEventKey];
if ([event isKindOfClass:NSString.class] && (event.length > 0)) {
NSString *stringedParams = [FBSDKTypeUtility stringValueOrNil:body[FBSDKAppEventsWKWebViewMessagesParamsKey]];
NSMutableDictionary<NSString *, id> *params = nil;
NSError *jsonParseError = nil;
if (stringedParams) {
params = [FBSDKTypeUtility JSONObjectWithData:[stringedParams dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingMutableContainers
error:&jsonParseError
];
}
NSString *pixelID = body[FBSDKAppEventsWKWebViewMessagesPixelIDKey];
if (pixelID == nil) {
[FBSDKAppEventsUtility logAndNotify:@"Can't bridge an event without a referral Pixel ID. Check your webview Pixel configuration."];
return;
}
if (jsonParseError != nil || ![params isKindOfClass:[NSDictionary class]] || params == nil) {
[FBSDKAppEventsUtility logAndNotify:@"Could not find parameters for your Pixel request. Check your webview Pixel configuration."];
params = [[NSMutableDictionary alloc] initWithObjectsAndKeys:pixelID, FBSDKAppEventsWKWebViewMessagesPixelReferralParamKey, nil];
} else {
[FBSDKTypeUtility dictionary:params setObject:pixelID forKey:FBSDKAppEventsWKWebViewMessagesPixelReferralParamKey];
}
[self.eventLogger logInternalEvent:event
parameters:params
isImplicitlyLogged:NO];
}
}
}
@end
#endif
@@ -0,0 +1,29 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKPaymentObserver.h"
#import "FBSDKPaymentObserving.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKPaymentObserver (PaymentObservingProtocol) <FBSDKPaymentObserving>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,37 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// Class to encapsulate implicit logging of purchase events
NS_SWIFT_NAME(PaymentObserver)
@interface FBSDKPaymentObserver : NSObject
@property (class, readonly) FBSDKPaymentObserver *shared;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (void)startObservingTransactions;
- (void)stopObservingTransactions;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,105 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKPaymentObserver.h"
#import <StoreKit/StoreKit.h>
#import "FBSDKCoreKit+Internal.h"
#import "FBSDKPaymentProductRequestor.h"
#import "FBSDKPaymentProductRequestorCreating.h"
#import "FBSDKPaymentProductRequestorFactory.h"
@interface FBSDKPaymentObserver () <SKPaymentTransactionObserver>
@property (nonatomic, readonly) SKPaymentQueue *paymentQueue;
@property (nonatomic, readonly) id<FBSDKPaymentProductRequestorCreating> requestorFactory;
@property (nonatomic) BOOL isObservingTransactions;
@end
@implementation FBSDKPaymentObserver
- (instancetype)initWithPaymentQueue:(SKPaymentQueue *)paymentQueue
paymentProductRequestorFactory:(id<FBSDKPaymentProductRequestorCreating>)paymentProductRequestorFactory
{
if ((self = [super init])) {
_paymentQueue = paymentQueue;
_requestorFactory = paymentProductRequestorFactory;
}
return self;
}
#pragma mark - Internal Methods
+ (FBSDKPaymentObserver *)shared
{
static FBSDKPaymentObserver *shared = nil;
static dispatch_once_t nonce;
dispatch_once(&nonce, ^{
shared = [[FBSDKPaymentObserver alloc] initWithPaymentQueue:SKPaymentQueue.defaultQueue
paymentProductRequestorFactory:[FBSDKPaymentProductRequestorFactory new]];
});
return shared;
}
- (void)startObservingTransactions
{
@synchronized(self) {
if (!self.isObservingTransactions) {
[self.paymentQueue addTransactionObserver:self];
self.isObservingTransactions = YES;
}
}
}
- (void)stopObservingTransactions
{
@synchronized(self) {
if (self.isObservingTransactions) {
[self.paymentQueue removeTransactionObserver:self];
self.isObservingTransactions = NO;
}
}
}
- (void) paymentQueue:(SKPaymentQueue *)queue
updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
case SKPaymentTransactionStatePurchased:
case SKPaymentTransactionStateFailed:
case SKPaymentTransactionStateRestored:
[self handleTransaction:transaction];
break;
case SKPaymentTransactionStateDeferred:
break;
}
}
}
- (void)handleTransaction:(SKPaymentTransaction *)transaction
{
FBSDKPaymentProductRequestor *productRequestor = [self.requestorFactory createRequestorWithTransaction:transaction];
[productRequestor resolveProducts];
}
@end
@@ -0,0 +1,26 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
// Protocol of the class to encapsulate implicit logging of purchase events
NS_SWIFT_NAME(PaymentObserving)
@protocol FBSDKPaymentObserving
- (void)startObservingTransactions;
- (void)stopObservingTransactions;
@end
@@ -0,0 +1,54 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <StoreKit/StoreKit.h>
@protocol FBSDKSettings;
@protocol FBSDKEventLogging;
@protocol FBSDKGateKeeperManaging;
@protocol FBSDKDataPersisting;
@protocol FBSDKLoggingCreating;
@protocol FBSDKProductsRequestCreating;
@protocol FBSDKAppStoreReceiptProviding;
NS_ASSUME_NONNULL_BEGIN
/**
Used for requesting information about purchase events from StoreKit to use when
logging AppEvents
*/
NS_SWIFT_NAME(PaymentProductRequestor)
@interface FBSDKPaymentProductRequestor : NSObject <SKProductsRequestDelegate>
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithTransaction:(SKPaymentTransaction *)transaction
settings:(id<FBSDKSettings>)settings
eventLogger:(id<FBSDKEventLogging>)eventLogger
gateKeeperManager:(Class<FBSDKGateKeeperManaging>)gateKeeperManager
store:(id<FBSDKDataPersisting>)store
loggerFactory:(id<FBSDKLoggingCreating>)loggerFactory
productsRequestFactory:(id<FBSDKProductsRequestCreating>)productRequestFactory
appStoreReceiptProvider:(id<FBSDKAppStoreReceiptProviding>)receiptProvider;
- (void)resolveProducts;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,463 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKPaymentProductRequestor.h"
#import <StoreKit/StoreKit.h>
#import "FBSDKAppEventName.h"
#import "FBSDKAppEventParameterName.h"
#import "FBSDKAppEventsFlushReason.h"
#import "FBSDKAppStoreReceiptProviding.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKDataPersisting.h"
#import "FBSDKEventLogging.h"
#import "FBSDKGateKeeperManaging.h"
#import "FBSDKLoggingCreating.h"
#import "FBSDKProductsRequestProtocols.h"
#import "FBSDKSettingsProtocol.h"
static NSString *const FBSDKPaymentObserverOriginalTransactionKey = @"com.facebook.appevents.PaymentObserver.originalTransaction";
static NSString *const FBSDKPaymentObserverDelimiter = @",";
static NSString *const FBSDKAppEventParameterImplicitlyLoggedPurchase = @"_implicitlyLogged";
static NSString *const FBSDKAppEventNamePurchaseFailed = @"fb_mobile_purchase_failed";
static NSString *const FBSDKAppEventNamePurchaseRestored = @"fb_mobile_purchase_restored";
static NSString *const FBSDKAppEventParameterNameInAppPurchaseType = @"fb_iap_product_type";
static NSString *const FBSDKAppEventParameterNameProductTitle = @"fb_content_title";
static NSString *const FBSDKAppEventParameterNameOriginalTransactionID = @"fb_original_transaction_id";
static NSString *const FBSDKAppEventParameterNameTransactionID = @"fb_transaction_id";
static NSString *const FBSDKAppEventParameterNameTransactionDate = @"fb_transaction_date";
static NSString *const FBSDKAppEventParameterNameSubscriptionPeriod = @"fb_iap_subs_period";
static NSString *const FBSDKAppEventParameterNameIsStartTrial = @"fb_iap_is_start_trial";
static NSString *const FBSDKAppEventParameterNameHasFreeTrial = @"fb_iap_has_free_trial";
static NSString *const FBSDKAppEventParameterNameTrialPeriod = @"fb_iap_trial_period";
static NSString *const FBSDKAppEventParameterNameTrialPrice = @"fb_iap_trial_price";
static NSString *const FBSDKGateKeeperAppEventsIfAutoLogSubs = @"app_events_if_auto_log_subs";
static int const FBSDKMaxParameterValueLength = 100;
@interface FBSDKPaymentProductRequestor ()
@property (class, nonatomic, readonly) NSMutableArray *pendingRequestors;
@property (nonatomic, retain) SKPaymentTransaction *transaction;
@property (nonatomic, readonly) id<FBSDKAppStoreReceiptProviding> appStoreReceiptProvider;
@property (nonatomic, retain) id<FBSDKProductsRequest> productsRequest;
@property (nonatomic, readonly) id<FBSDKProductsRequestCreating> productRequestFactory;
@property (nonatomic, readonly) id<FBSDKSettings> settings;
@property (nonatomic, readonly) id<FBSDKEventLogging> eventLogger;
@property (nonatomic, readonly) Class<FBSDKGateKeeperManaging> gateKeeperManager;
@property (nonatomic, readonly) id<FBSDKDataPersisting> store;
@property (nonatomic, readonly) id<FBSDKLoggingCreating> loggerFactory;
@property (nonatomic) NSMutableSet<NSString *> *originalTransactionSet;
@property (nonatomic) NSSet<NSString *> *eventsWithReceipt;
@property (nonatomic, readonly) NSDateFormatter *formatter;
@end
@implementation FBSDKPaymentProductRequestor
static NSMutableArray *_pendingRequestors;
+ (void)initialize
{
if ([self class] == [FBSDKPaymentProductRequestor class]) {
_pendingRequestors = [NSMutableArray new];
}
}
- (instancetype)initWithTransaction:(SKPaymentTransaction *)transaction
settings:(id<FBSDKSettings>)settings
eventLogger:(id<FBSDKEventLogging>)eventLogger
gateKeeperManager:(Class<FBSDKGateKeeperManaging>)gateKeeperManager
store:(id<FBSDKDataPersisting>)store
loggerFactory:(id<FBSDKLoggingCreating>)loggerFactory
productsRequestFactory:(id<FBSDKProductsRequestCreating>)productRequestFactory
appStoreReceiptProvider:(id<FBSDKAppStoreReceiptProviding>)receiptProvider
{
if ((self = [super init])) {
_settings = settings;
_eventLogger = eventLogger;
_gateKeeperManager = gateKeeperManager;
_store = store;
_loggerFactory = loggerFactory;
_productRequestFactory = productRequestFactory;
_appStoreReceiptProvider = receiptProvider;
_transaction = transaction;
_formatter = [NSDateFormatter new];
_formatter.dateFormat = @"yyyy-MM-dd HH:mm:ssZ";
NSString *data = [_store stringForKey:FBSDKPaymentObserverOriginalTransactionKey];
_eventsWithReceipt = [NSSet setWithArray:@[FBSDKAppEventNamePurchased, FBSDKAppEventNameSubscribe,
FBSDKAppEventNameStartTrial]];
if (data) {
_originalTransactionSet = [NSMutableSet setWithArray:[data componentsSeparatedByString:FBSDKPaymentObserverDelimiter]];
} else {
_originalTransactionSet = [NSMutableSet new];
}
}
return self;
}
+ (NSMutableArray *)pendingRequestors
{
return _pendingRequestors;
}
- (void)setProductsRequest:(id<FBSDKProductsRequest>)productsRequest
{
if (productsRequest != _productsRequest) {
if (_productsRequest) {
_productsRequest.delegate = nil;
}
_productsRequest = productsRequest;
}
}
- (void)resolveProducts
{
NSString *productId = self.transaction.payment.productIdentifier;
NSSet *productIdentifiers = [NSSet setWithObjects:productId, nil];
self.productsRequest = [self.productRequestFactory createWithProductIdentifiers:productIdentifiers];
self.productsRequest.delegate = self;
@synchronized(self.class.pendingRequestors) {
[FBSDKTypeUtility array:self.class.pendingRequestors addObject:self];
}
[self.productsRequest start];
}
- (NSString *)getTruncatedString:(NSString *)inputString
{
if (!inputString) {
return @"";
}
return inputString.length <= FBSDKMaxParameterValueLength ? inputString : [inputString substringToIndex:FBSDKMaxParameterValueLength];
}
- (void)logTransactionEvent:(SKProduct *)product
{
if ([self isSubscription:product]
&& [self.gateKeeperManager boolForKey:FBSDKGateKeeperAppEventsIfAutoLogSubs
defaultValue:NO]) {
[self logImplicitSubscribeTransaction:self.transaction ofProduct:product];
} else {
[self logImplicitPurchaseTransaction:self.transaction ofProduct:product];
}
}
- (BOOL)isSubscription:(SKProduct *)product
{
#if !TARGET_OS_TV
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_11_1
if (@available(iOS 11.2, *)) {
return (product.subscriptionPeriod != nil) && ((unsigned long)product.subscriptionPeriod.numberOfUnits > 0);
}
#endif
#endif
return NO;
}
- (NSMutableDictionary<NSString *, id> *)getEventParametersOfProduct:(SKProduct *)product
withTransaction:(SKPaymentTransaction *)transaction
{
NSString *transactionID = nil;
NSString *transactionDate = nil;
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStatePurchased:
transactionID = transaction.transactionIdentifier;
transactionDate = [_formatter stringFromDate:transaction.transactionDate];
break;
case SKPaymentTransactionStateFailed:
break;
case SKPaymentTransactionStateRestored:
transactionDate = [_formatter stringFromDate:transaction.transactionDate];
break;
default: break;
}
SKPayment *payment = transaction.payment;
NSMutableDictionary *eventParameters = [NSMutableDictionary dictionaryWithDictionary:@{
FBSDKAppEventParameterNameContentID : payment.productIdentifier ?: @"",
FBSDKAppEventParameterNameNumItems : @(payment.quantity),
FBSDKAppEventParameterNameTransactionDate : transactionDate ?: @"",
}];
if (product) {
[eventParameters addEntriesFromDictionary:@{
FBSDKAppEventParameterNameNumItems : @(payment.quantity),
FBSDKAppEventParameterNameProductTitle : [self getTruncatedString:product.localizedTitle],
FBSDKAppEventParameterNameDescription : [self getTruncatedString:product.localizedDescription],
}];
if (@available(iOS 10.0, *)) {
[FBSDKTypeUtility dictionary:eventParameters
setObject:product.priceLocale.currencyCode
forKey:FBSDKAppEventParameterNameCurrency];
}
if (transactionID) {
[FBSDKTypeUtility dictionary:eventParameters setObject:transactionID forKey:FBSDKAppEventParameterNameTransactionID];
}
}
#if !TARGET_OS_TV
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_11_1
if (@available(iOS 11.2, *)) {
if ([self isSubscription:product]) {
// subs inapp
[FBSDKTypeUtility dictionary:eventParameters setObject:[self durationOfSubscriptionPeriod:product.subscriptionPeriod] forKey:FBSDKAppEventParameterNameSubscriptionPeriod];
[FBSDKTypeUtility dictionary:eventParameters setObject:@"subs" forKey:FBSDKAppEventParameterNameInAppPurchaseType];
[FBSDKTypeUtility dictionary:eventParameters setObject:[self isStartTrial:transaction ofProduct:product] ? @"1" : @"0" forKey:FBSDKAppEventParameterNameIsStartTrial];
// trial information for subs
SKProductDiscount *discount = product.introductoryPrice;
if (discount) {
if (discount.paymentMode == SKProductDiscountPaymentModeFreeTrial) {
[FBSDKTypeUtility dictionary:eventParameters setObject:@"1" forKey:FBSDKAppEventParameterNameHasFreeTrial];
} else {
[FBSDKTypeUtility dictionary:eventParameters setObject:@"0" forKey:FBSDKAppEventParameterNameHasFreeTrial];
}
[FBSDKTypeUtility dictionary:eventParameters setObject:[self durationOfSubscriptionPeriod:discount.subscriptionPeriod] forKey:FBSDKAppEventParameterNameTrialPeriod];
[FBSDKTypeUtility dictionary:eventParameters setObject:discount.price forKey:FBSDKAppEventParameterNameTrialPrice];
}
} else {
[FBSDKTypeUtility dictionary:eventParameters setObject:@"inapp" forKey:FBSDKAppEventParameterNameInAppPurchaseType];
}
}
#endif
#endif
return eventParameters;
}
- (void)appendOriginalTransactionID:(NSString *)transactionID
{
if (!transactionID) {
return;
}
[self.originalTransactionSet addObject:transactionID];
[self.store setObject:[[self.originalTransactionSet allObjects] componentsJoinedByString:FBSDKPaymentObserverDelimiter]
forKey:FBSDKPaymentObserverOriginalTransactionKey];
}
- (void)clearOriginalTransactionID:(NSString *)transactionID
{
if (!transactionID) {
return;
}
[self.originalTransactionSet removeObject:transactionID];
[self.store setObject:[[self.originalTransactionSet allObjects] componentsJoinedByString:FBSDKPaymentObserverDelimiter]
forKey:FBSDKPaymentObserverOriginalTransactionKey];
}
- (BOOL)isStartTrial:(SKPaymentTransaction *)transaction
ofProduct:(SKProduct *)product
{
#if !TARGET_OS_TV
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_11_1
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_11_4
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_12_1
// promotional offer starting from iOS 12.2
if (@available(iOS 12.2, *)) {
SKPaymentDiscount *paymentDiscount = transaction.payment.paymentDiscount;
if (paymentDiscount) {
NSArray<SKProductDiscount *> *discounts = product.discounts;
for (SKProductDiscount *discount in discounts) {
if (discount.paymentMode == SKProductDiscountPaymentModeFreeTrial
&& [paymentDiscount.identifier isEqualToString:discount.identifier]) {
return YES;
}
}
}
}
#endif
#endif
// introductory offer starting from iOS 11.2
if (@available(iOS 11.2, *)) {
if (product.introductoryPrice
&& product.introductoryPrice.paymentMode == SKProductDiscountPaymentModeFreeTrial) {
NSString *originalTransactionID = transaction.originalTransaction.transactionIdentifier;
// only consider the very first trial transaction as start trial
if (!originalTransactionID) {
return YES;
}
}
}
#endif
#endif
return NO;
}
- (NSString *)durationOfSubscriptionPeriod:(id)subcriptionPeriod
{
#if !TARGET_OS_TV
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_11_1
if (@available(iOS 11.2, *)) {
if (subcriptionPeriod && [subcriptionPeriod isKindOfClass:[SKProductSubscriptionPeriod class]]) {
SKProductSubscriptionPeriod *period = (SKProductSubscriptionPeriod *)subcriptionPeriod;
NSString *unit = nil;
switch (period.unit) {
case SKProductPeriodUnitDay: unit = @"D"; break;
case SKProductPeriodUnitWeek: unit = @"W"; break;
case SKProductPeriodUnitMonth: unit = @"M"; break;
case SKProductPeriodUnitYear: unit = @"Y"; break;
}
return [NSString stringWithFormat:@"P%lu%@", (unsigned long)period.numberOfUnits, unit];
}
}
#endif
#endif
return nil;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
NSArray *invalidProductIdentifiers = response.invalidProductIdentifiers;
if (products.count + invalidProductIdentifiers.count != 1) {
id<FBSDKLogging> logger = [self.loggerFactory createLoggerWithLoggingBehavior:FBSDKLoggingBehaviorAppEvents];
[logger logEntry:@"FBSDKPaymentObserver: Expect to resolve one product per request"];
}
SKProduct *product = nil;
if (products.count) {
product = [FBSDKTypeUtility array:products objectAtIndex:0];
}
[self logTransactionEvent:product];
}
- (void)requestDidFinish:(SKRequest *)request
{
[self cleanUp];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
[self logTransactionEvent:nil];
[self cleanUp];
}
- (void)cleanUp
{
@synchronized(self.class.pendingRequestors) {
[self.class.pendingRequestors removeObject:self];
}
}
- (void)logImplicitSubscribeTransaction:(SKPaymentTransaction *)transaction
ofProduct:(SKProduct *)product
{
NSString *eventName = nil;
NSString *originalTransactionID = transaction.originalTransaction.transactionIdentifier;
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
eventName = @"SubscriptionInitiatedCheckout";
break;
case SKPaymentTransactionStatePurchased:
if ([self isStartTrial:transaction ofProduct:product]) {
eventName = FBSDKAppEventNameStartTrial;
[self clearOriginalTransactionID:originalTransactionID];
} else {
if (originalTransactionID && [self.originalTransactionSet containsObject:originalTransactionID]) {
return;
}
eventName = FBSDKAppEventNameSubscribe;
[self appendOriginalTransactionID:(originalTransactionID ?: transaction.transactionIdentifier)];
}
break;
case SKPaymentTransactionStateFailed:
eventName = @"SubscriptionFailed";
break;
case SKPaymentTransactionStateRestored:
eventName = @"SubscriptionRestore";
break;
case SKPaymentTransactionStateDeferred:
return;
}
double totalAmount = 0;
if (product) {
totalAmount = transaction.payment.quantity * product.price.doubleValue;
}
[self logImplicitTransactionEvent:eventName
valueToSum:totalAmount
parameters:[self getEventParametersOfProduct:product withTransaction:transaction]];
}
- (void)logImplicitPurchaseTransaction:(SKPaymentTransaction *)transaction
ofProduct:(SKProduct *)product
{
NSString *eventName = nil;
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
eventName = FBSDKAppEventNameInitiatedCheckout;
break;
case SKPaymentTransactionStatePurchased:
eventName = FBSDKAppEventNamePurchased;
break;
case SKPaymentTransactionStateFailed:
eventName = FBSDKAppEventNamePurchaseFailed;
break;
case SKPaymentTransactionStateRestored:
eventName = FBSDKAppEventNamePurchaseRestored;
break;
case SKPaymentTransactionStateDeferred:
return;
}
double totalAmount = 0;
if (product) {
totalAmount = transaction.payment.quantity * product.price.doubleValue;
}
[self logImplicitTransactionEvent:eventName
valueToSum:totalAmount
parameters:[self getEventParametersOfProduct:product withTransaction:transaction]];
}
- (void)logImplicitTransactionEvent:(NSString *)eventName
valueToSum:(double)valueToSum
parameters:(NSDictionary<NSString *, id> *)parameters
{
NSMutableDictionary *eventParameters = [NSMutableDictionary dictionaryWithDictionary:parameters];
if ([_eventsWithReceipt containsObject:eventName]) {
NSData *receipt = [self fetchDeviceReceipt];
if (receipt) {
NSString *base64encodedReceipt = [receipt base64EncodedStringWithOptions:0];
[FBSDKTypeUtility dictionary:eventParameters setObject:base64encodedReceipt forKey:@"receipt_data"];
}
}
[FBSDKTypeUtility dictionary:eventParameters setObject:@"1" forKey:FBSDKAppEventParameterImplicitlyLoggedPurchase];
[self.eventLogger logEvent:eventName
valueToSum:valueToSum
parameters:eventParameters];
// Unless the behavior is set to only allow explicit flushing, we go ahead and flush, since purchase events
// are relatively rare and relatively high value and worth getting across on wire right away.
if ([self.eventLogger flushBehavior] != FBSDKAppEventsFlushBehaviorExplicitOnly) {
[self.eventLogger flushForReason:FBSDKAppEventsFlushReasonEagerlyFlushingEvent];
}
}
// Fetch the current receipt for this application.
- (NSData *)fetchDeviceReceipt
{
NSURL *receiptURL = self.appStoreReceiptProvider.appStoreReceiptURL;
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
return receipt;
}
@end
@@ -0,0 +1,34 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@class FBSDKPaymentProductRequestor;
@class SKPaymentTransaction;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(PaymentProductRequestorCreating)
@protocol FBSDKPaymentProductRequestorCreating
- (nonnull FBSDKPaymentProductRequestor *)createRequestorWithTransaction:(SKPaymentTransaction *)transaction
NS_SWIFT_NAME(createRequestor(transaction:));
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,48 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKPaymentProductRequestorCreating.h"
@protocol FBSDKSettings;
@protocol FBSDKEventLogging;
@protocol FBSDKGateKeeperManaging;
@protocol FBSDKDataPersisting;
@protocol FBSDKLoggingCreating;
@protocol FBSDKProductsRequestCreating;
@protocol FBSDKAppStoreReceiptProviding;
NS_ASSUME_NONNULL_BEGIN
/// Factory used to create `FBSDKPaymentProductRequestor` instances with dependencies.
NS_SWIFT_NAME(PaymentProductRequestorFactory)
@interface FBSDKPaymentProductRequestorFactory : NSObject<FBSDKPaymentProductRequestorCreating>
- (instancetype)initWithSettings:(id<FBSDKSettings>)settings
eventLogger:(id<FBSDKEventLogging>)eventLogger
gateKeeperManager:(Class<FBSDKGateKeeperManaging>)gateKeeperManager
store:(id<FBSDKDataPersisting>)store
loggerFactory:(id<FBSDKLoggingCreating>)logger
productsRequestFactory:(id<FBSDKProductsRequestCreating>)productsRequestFactory
appStoreReceiptProvider:(id<FBSDKAppStoreReceiptProviding>)receiptProvider
NS_SWIFT_NAME(init(settings:eventLogger:gateKeeperManager:store:loggerFactory:productsRequestFactory:receiptProvider:));
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,90 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKPaymentProductRequestorFactory.h"
#import "FBSDKAppEvents+EventLogging.h"
#import "FBSDKAppStoreReceiptProviding.h"
#import "FBSDKGateKeeperManager.h"
#import "FBSDKGateKeeperManaging.h"
#import "FBSDKLoggerFactory.h"
#import "FBSDKPaymentProductRequestor.h"
#import "FBSDKProductRequestFactory.h"
#import "FBSDKProductsRequestProtocols.h"
#import "FBSDKSettings+Internal.h"
#import "NSUserDefaults+FBSDKDataPersisting.h"
@interface FBSDKPaymentProductRequestorFactory ()
@property (nonatomic, readonly) id<FBSDKSettings> settings;
@property (nonatomic, readonly) id<FBSDKEventLogging> eventLogger;
@property (nullable, nonatomic) Class<FBSDKGateKeeperManaging> gateKeeperManager;
@property (nullable, nonatomic) id<FBSDKDataPersisting> store;
@property (nullable, nonatomic) id<FBSDKLoggingCreating> loggerFactory;
@property (nonatomic, readonly) id<FBSDKProductsRequestCreating> productsRequestFactory;
@property (nonatomic, readonly) id<FBSDKAppStoreReceiptProviding> appStoreReceiptProvider;
@end
@implementation FBSDKPaymentProductRequestorFactory
- (instancetype)init
{
return [self initWithSettings:FBSDKSettings.sharedSettings
eventLogger:FBSDKAppEvents.singleton
gateKeeperManager:FBSDKGateKeeperManager.class
store:NSUserDefaults.standardUserDefaults
loggerFactory:[FBSDKLoggerFactory new]
productsRequestFactory:[FBSDKProductRequestFactory new]
appStoreReceiptProvider:[NSBundle bundleForClass:self.class]];
}
- (instancetype)initWithSettings:(id<FBSDKSettings>)settings
eventLogger:(id<FBSDKEventLogging>)eventLogger
gateKeeperManager:(Class<FBSDKGateKeeperManaging>)gateKeeperManager
store:(id<FBSDKDataPersisting>)store
loggerFactory:(id<FBSDKLoggingCreating>)loggerFactory
productsRequestFactory:(id<FBSDKProductsRequestCreating>)productsRequestFactory
appStoreReceiptProvider:(id<FBSDKAppStoreReceiptProviding>)receiptProvider
{
if ((self = [super init])) {
_settings = settings;
_eventLogger = eventLogger;
_gateKeeperManager = gateKeeperManager;
_store = store;
_loggerFactory = loggerFactory;
_productsRequestFactory = productsRequestFactory;
_appStoreReceiptProvider = receiptProvider;
}
return self;
}
- (nonnull FBSDKPaymentProductRequestor *)createRequestorWithTransaction:(SKPaymentTransaction *)transaction
{
return [[FBSDKPaymentProductRequestor alloc] initWithTransaction:transaction
settings:self.settings
eventLogger:self.eventLogger
gateKeeperManager:self.gateKeeperManager
store:self.store
loggerFactory:self.loggerFactory
productsRequestFactory:self.productsRequestFactory
appStoreReceiptProvider:self.appStoreReceiptProvider];
}
@end
@@ -0,0 +1,30 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKProductsRequestProtocols.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(ProductRequestFactory)
@interface FBSDKProductRequestFactory : NSObject<FBSDKProductsRequestCreating>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,30 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKProductRequestFactory.h"
#import "SKProductsRequest+FBSDKProductsRequest.h"
@implementation FBSDKProductRequestFactory
- (nonnull id<FBSDKProductsRequest>)createWithProductIdentifiers:(nonnull NSSet<NSString *> *)identifiers
{
return [[SKProductsRequest alloc] initWithProductIdentifiers:identifiers];
}
@end
@@ -0,0 +1,42 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@protocol SKProductsRequestDelegate;
NS_ASSUME_NONNULL_BEGIN
/// An abstraction for an `SKProductsRequest` instance
NS_SWIFT_NAME(ProductsRequest)
@protocol FBSDKProductsRequest
@property(nonatomic, weak, nullable) id <SKProductsRequestDelegate> delegate;
- (void)cancel;
- (void)start;
@end
/// An abstraction for any object that can create a `ProductsRequest`
NS_SWIFT_NAME(ProductsRequestCreating)
@protocol FBSDKProductsRequestCreating
- (id<FBSDKProductsRequest>)createWithProductIdentifiers:(NSSet<NSString *> *)identifiers;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,32 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(SourceApplicationTracking)
@protocol FBSDKSourceApplicationTracking
- (void)setSourceApplication:(nullable NSString *)sourceApplication openURL:(nullable NSURL *)url;
- (void)setSourceApplication:(nullable NSString *)sourceApplication isFromAppLink:(BOOL)isFromAppLink;
- (void)registerAutoResetSourceApplication;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,28 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKSourceApplicationTracking.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKTimeSpentData (SourceApplicationTracking) <FBSDKSourceApplicationTracking>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,29 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKTimeSpentData.h"
#import "FBSDKTimeSpentRecording.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKTimeSpentData (TimeSpentRecording) <FBSDKTimeSpentRecording>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,45 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
@protocol FBSDKEventLogging;
@protocol FBSDKServerConfigurationProviding;
NS_ASSUME_NONNULL_BEGIN
// Class to encapsulate persisting of time spent data collected by [FBSDKAppEvents activateApp]. The activate app App Event is
// logged when restore: is called with sufficient time since the last deactivation.
NS_SWIFT_NAME(TimeSpentData)
@interface FBSDKTimeSpentData : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithEventLogger:(id<FBSDKEventLogging>)eventLogger
serverConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider;
- (void)setSourceApplication:(nullable NSString *)sourceApplication openURL:(nullable NSURL *)url;
- (void)setSourceApplication:(nullable NSString *)sourceApplication isFromAppLink:(BOOL)isFromAppLink;
- (void)registerAutoResetSourceApplication;
- (void)suspend;
- (void)restore:(BOOL)calledFromActivateApp;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,307 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKTimeSpentData.h"
#import "FBSDKAppEventParameterName.h"
#import "FBSDKAppEventsFlushReason.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKEventLogging.h"
#import "FBSDKInternalUtility+Internal.h"
#import "FBSDKLogger.h"
#import "FBSDKServerConfiguration.h"
#import "FBSDKServerConfigurationProviding.h"
// Filename and keys for session length
NSString *const FBSDKTimeSpentFilename = @"com-facebook-sdk-AppEventsTimeSpent.json";
static NSString *const FBSDKTimeSpentPersistKeySessionSecondsSpent = @"secondsSpentInCurrentSession";
static NSString *const FBSDKTimeSpentPersistKeySessionNumInterruptions = @"numInterruptions";
static NSString *const FBSDKTimeSpentPersistKeyLastSuspendTime = @"lastSuspendTime";
static NSString *const FBSDKTimeSpentPersistKeySessionID = @"sessionID";
static NSString *const FBSDKAppEventNameActivatedApp = @"fb_mobile_activate_app";
static NSString *const FBSDKAppEventNameDeactivatedApp = @"fb_mobile_deactivate_app";
static NSString *const FBSDKAppEventParameterNameSessionInterruptions = @"fb_mobile_app_interruptions";
static NSString *const FBSDKAppEventParameterNameTimeBetweenSessions = @"fb_mobile_time_between_sessions";
static NSString *const FBSDKAppEventParameterNameSessionID = @"_session_id";
FBSDKAppEventParameterName FBSDKAppEventParameterLaunchSource = @"fb_mobile_launch_source";
static const int SECS_PER_MIN = 60;
static const int SECS_PER_HOUR = 60 * SECS_PER_MIN;
static const int SECS_PER_DAY = 24 * SECS_PER_HOUR;
// Will be translated and displayed in App Insights. Need to maintain same number and value of quanta on the server.
static const long INACTIVE_SECONDS_QUANTA[] =
{
5 * SECS_PER_MIN,
15 * SECS_PER_MIN,
30 * SECS_PER_MIN,
1 * SECS_PER_HOUR,
6 * SECS_PER_HOUR,
12 * SECS_PER_HOUR,
1 * SECS_PER_DAY,
2 * SECS_PER_DAY,
3 * SECS_PER_DAY,
7 * SECS_PER_DAY,
14 * SECS_PER_DAY,
21 * SECS_PER_DAY,
28 * SECS_PER_DAY,
60 * SECS_PER_DAY,
90 * SECS_PER_DAY,
120 * SECS_PER_DAY,
150 * SECS_PER_DAY,
180 * SECS_PER_DAY,
365 * SECS_PER_DAY,
LONG_MAX, // keep as LONG_MAX to guarantee loop will terminate
};
@interface FBSDKTimeSpentData ()
@property (nonatomic, weak) id<FBSDKEventLogging> eventLogger;
@property (nonnull, nonatomic) id<FBSDKServerConfigurationProviding> serverConfigurationProvider;
@property (nonatomic) NSString *sourceApplication;
@property (nonatomic) BOOL isOpenedFromAppLink;
@property (nonatomic) BOOL isCurrentlyLoaded;
@property (nonatomic) NSTimeInterval lastRestoreTime;
@property (nonatomic) NSTimeInterval secondsSpentInCurrentSession;
@property (nonatomic) NSTimeInterval timeSinceLastSuspend;
@property (nonatomic) int numInterruptionsInCurrentSession;
@property (nonatomic) NSString *sessionID;
@property (nonatomic) NSTimeInterval lastSuspendTime;
@property (nonatomic) BOOL shouldLogActivateEvent;
@property (nonatomic) BOOL shouldLogDeactivateEvent;
@end
/**
* This class encapsulates the notion of an app 'session' - the length of time that the user has
* spent in the app that can be considered a single usage of the app. Apps may be frequently interrupted
* do to other device activity, like a text message, so this class allows those interruptions to be smoothed
* out and the time actually spent in the app excluding this interruption time to be accumulated. Also,
* once a certain amount of time has gone by where the app is not in the foreground, we consider the
* session to be complete, and a new session beginning. When this occurs, we log a 'deactivate app' event
* with the duration of the previous session as the 'value' of this event, along with the number of
* interruptions from that previous session as an event parameter.
*/
@implementation FBSDKTimeSpentData
- (instancetype)initWithEventLogger:(id<FBSDKEventLogging>)eventLogger
serverConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider
{
if ((self = [super init])) {
_eventLogger = eventLogger;
_serverConfigurationProvider = serverConfigurationProvider;
}
return self;
}
// Calculate and persist time spent data for this instance of the app activation.
- (void)suspend
{
dispatch_async(dispatch_get_main_queue(), ^{
[self suspendTimeSpentData];
});
}
- (void)suspendTimeSpentData
{
if (!self.isCurrentlyLoaded) {
FBSDKConditionalLog(YES, FBSDKLoggingBehaviorInformational, @"[FBSDKTimeSpentData suspend] invoked without corresponding restore");
return;
}
NSTimeInterval now = round([NSDate date].timeIntervalSince1970);
NSTimeInterval timeSinceRestore = now - self.lastRestoreTime;
// Can happen if the clock on the device is changed
if (timeSinceRestore < 0) {
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorAppEvents
logEntry:@"Clock skew detected"];
timeSinceRestore = 0;
}
self.secondsSpentInCurrentSession += timeSinceRestore;
NSDictionary *timeSpentData =
@{
FBSDKTimeSpentPersistKeySessionSecondsSpent : @(self.secondsSpentInCurrentSession),
FBSDKTimeSpentPersistKeySessionNumInterruptions : @(self.numInterruptionsInCurrentSession),
FBSDKTimeSpentPersistKeyLastSuspendTime : @(now),
FBSDKTimeSpentPersistKeySessionID : self.sessionID,
};
NSString *content = [FBSDKBasicUtility JSONStringForObject:timeSpentData error:NULL invalidObjectHandler:NULL];
[content writeToFile:[FBSDKBasicUtility persistenceFilePath:FBSDKTimeSpentFilename]
atomically:YES
encoding:NSASCIIStringEncoding
error:nil];
NSString *msg = [NSString stringWithFormat:@"FBSDKTimeSpentData Persist: %@", content];
[FBSDKLogger singleShotLogEntry:FBSDKLoggingBehaviorAppEvents
logEntry:msg];
self.isCurrentlyLoaded = NO;
}
// Called during activation - either through an explicit 'activateApp' call or implicitly when the app is foregrounded.
// In both cases, we restore the persisted event data. In the case of the activateApp, we log an 'app activated'
// event if there's been enough time between the last deactivation and now.
- (void)restore:(BOOL)calledFromActivateApp
{
dispatch_async(dispatch_get_main_queue(), ^{
[self restoreTimeSpendDataWithCalledFromActivateApp:calledFromActivateApp];
});
}
- (void)restoreTimeSpendDataWithCalledFromActivateApp:(BOOL)isCalledFromActivateApp
{
// It's possible to call this multiple times during the time the app is in the foreground. If this is the case,
// just restore persisted data the first time.
if (!self.isCurrentlyLoaded) {
NSTimeInterval now = round([NSDate date].timeIntervalSince1970);
NSString *content =
[[NSString alloc] initWithContentsOfFile:[FBSDKBasicUtility persistenceFilePath:FBSDKTimeSpentFilename]
usedEncoding:nil
error:nil];
if (!content) {
// Nothing persisted, so this is the first launch.
self.sessionID = [NSUUID UUID].UUIDString;
self.secondsSpentInCurrentSession = 0;
self.numInterruptionsInCurrentSession = 0;
self.lastSuspendTime = 0;
// We want to log the app activation event on the first launch, but not the deactivate event
self.shouldLogActivateEvent = YES;
self.shouldLogDeactivateEvent = NO;
} else {
NSDictionary<id, id> *results = [FBSDKBasicUtility objectForJSONString:content error:NULL];
self.lastSuspendTime = [results[FBSDKTimeSpentPersistKeyLastSuspendTime] longValue];
self.timeSinceLastSuspend = now - self.lastSuspendTime;
self.secondsSpentInCurrentSession = [results[FBSDKTimeSpentPersistKeySessionSecondsSpent] intValue];
self.sessionID = results[FBSDKTimeSpentPersistKeySessionID] ?: [NSUUID UUID].UUIDString;
self.numInterruptionsInCurrentSession = [results[FBSDKTimeSpentPersistKeySessionNumInterruptions] intValue];
self.shouldLogActivateEvent = (self.timeSinceLastSuspend > [[self.serverConfigurationProvider cachedServerConfiguration] sessionTimoutInterval]);
// Other than the first launch, we always log the last session's deactivate with this session's activate.
self.shouldLogDeactivateEvent = self.shouldLogActivateEvent;
if (!self.shouldLogDeactivateEvent) {
// If we're not logging, then the time we spent deactivated is considered another interruption. But cap it
// so errant or test uses doesn't blow out the cardinality on the backend processing
self.numInterruptionsInCurrentSession = MIN(self.numInterruptionsInCurrentSession + 1, 200);
}
}
self.lastRestoreTime = now;
self.isCurrentlyLoaded = YES;
if (isCalledFromActivateApp) {
// It's important to log deactivate first to reset sessionID
if (self.shouldLogDeactivateEvent) {
[self.eventLogger logEvent:FBSDKAppEventNameDeactivatedApp
valueToSum:self.secondsSpentInCurrentSession
parameters:[self appEventsParametersForDeactivate]];
// We've logged the session stats, now reset.
self.secondsSpentInCurrentSession = 0;
self.numInterruptionsInCurrentSession = 0;
self.sessionID = [NSUUID UUID].UUIDString;
}
if (self.shouldLogActivateEvent) {
[self.eventLogger logEvent:FBSDKAppEventNameActivatedApp
parameters:[self appEventsParametersForActivate]];
// Unless the behavior is set to only allow explicit flushing, we go ahead and flush. App launch
// events are critical to Analytics so we don't want to lose them.
if (self.eventLogger.flushBehavior != FBSDKAppEventsFlushBehaviorExplicitOnly) {
[self.eventLogger flushForReason:FBSDKAppEventsFlushReasonEagerlyFlushingEvent];
}
}
}
}
}
- (NSDictionary *)appEventsParametersForActivate
{
return @{
FBSDKAppEventParameterLaunchSource : [self getSourceApplication],
FBSDKAppEventParameterNameSessionID : self.sessionID,
};
}
- (NSDictionary *)appEventsParametersForDeactivate
{
int quantaIndex = 0;
while (_timeSinceLastSuspend > INACTIVE_SECONDS_QUANTA[quantaIndex]) {
quantaIndex++;
}
NSMutableDictionary *params = [@{ FBSDKAppEventParameterNameSessionInterruptions : @(self.numInterruptionsInCurrentSession),
FBSDKAppEventParameterNameTimeBetweenSessions : [NSString stringWithFormat:@"session_quanta_%d", quantaIndex],
FBSDKAppEventParameterLaunchSource : [self getSourceApplication],
FBSDKAppEventParameterNameSessionID : self.sessionID ?: @"", } mutableCopy];
if (_lastSuspendTime) {
[FBSDKTypeUtility dictionary:params setObject:@(_lastSuspendTime) forKey:@"_logTime"];
}
return [params copy];
}
- (void)setSourceApplication:(nullable NSString *)sourceApplication openURL:(NSURL *)url
{
[self setSourceApplication:sourceApplication
isFromAppLink:[FBSDKInternalUtility.sharedUtility parametersFromFBURL:url][@"al_applink_data"] != nil];
}
- (void)setSourceApplication:(nullable NSString *)sourceApplication isFromAppLink:(BOOL)isFromAppLink
{
self.isOpenedFromAppLink = isFromAppLink;
self.sourceApplication = sourceApplication;
}
- (NSString *)getSourceApplication
{
NSString *openType = @"Unclassified";
if (self.isOpenedFromAppLink) {
openType = @"AppLink";
}
return (self.sourceApplication
? [NSString stringWithFormat:@"%@(%@)", openType, self.sourceApplication]
: openType);
}
- (void)resetSourceApplication
{
self.sourceApplication = nil;
self.isOpenedFromAppLink = NO;
}
- (void)registerAutoResetSourceApplication
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(resetSourceApplication)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
@end
@@ -0,0 +1,31 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(TimeSpentRecording)
@protocol FBSDKTimeSpentRecording
- (void)suspend;
- (void)restore:(BOOL)calledFromActivateApp;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,33 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKTimeSpentRecording.h"
#import "FBSDKSourceApplicationTracking.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(TimeSpentRecordingCreating)
@protocol FBSDKTimeSpentRecordingCreating
- (id<FBSDKTimeSpentRecording, FBSDKSourceApplicationTracking>)createTimeSpentRecorder;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,39 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "FBSDKTimeSpentRecordingCreating.h"
@protocol FBSDKEventLogging;
@protocol FBSDKServerConfigurationProviding;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(TimeSpentRecordingFactory)
@interface FBSDKTimeSpentRecordingFactory : NSObject<FBSDKTimeSpentRecordingCreating>
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithEventLogger:(id<FBSDKEventLogging>)eventLogger
serverConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,56 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKTimeSpentRecordingFactory.h"
#import "FBSDKEventLogging.h"
#import "FBSDKServerConfigurationProviding.h"
#import "FBSDKTimeSpentData.h"
#import "FBSDKTimeSpentData+SourceApplicationTracking.h"
#import "FBSDKTimeSpentData+TimeSpentRecording.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKTimeSpentRecordingFactory ()
@property (nonnull, nonatomic, readonly) id<FBSDKServerConfigurationProviding> serverConfigurationProvider;
@property (nonnull, nonatomic, readonly) id<FBSDKEventLogging> eventLogger;
@end
@implementation FBSDKTimeSpentRecordingFactory
- (instancetype)initWithEventLogger:(id<FBSDKEventLogging>)eventLogger
serverConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider
{
if ((self = [super init])) {
_eventLogger = eventLogger;
_serverConfigurationProvider = serverConfigurationProvider;
}
return self;
}
- (id<FBSDKSourceApplicationTracking, FBSDKTimeSpentRecording>)createTimeSpentRecorder
{
return [[FBSDKTimeSpentData alloc] initWithEventLogger:self.eventLogger
serverConfigurationProvider:self.serverConfigurationProvider];
}
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,33 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKAppEventsParameterProcessing.h"
#import "FBSDKIntegrityManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKIntegrityManager (AppEventsParameterProcessing) <FBSDKAppEventsParameterProcessing>
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,45 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
@protocol FBSDKGateKeeperManaging;
@protocol FBSDKIntegrityProcessing;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(IntegrityManager)
@interface FBSDKIntegrityManager : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithGateKeeperManager:(Class<FBSDKGateKeeperManaging>)gateKeeperManager
integrityProcessor:(id<FBSDKIntegrityProcessing>)integrityProcessor;
- (void)enable;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,85 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKIntegrityManager.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKGateKeeperManaging.h"
#import "FBSDKIntegrityProcessing.h"
@interface FBSDKIntegrityManager ()
@property (nonatomic) Class<FBSDKGateKeeperManaging> gateKeeperManager;
@property (nonatomic, weak) id<FBSDKIntegrityProcessing> integrityProcessor;
@property (nonatomic) BOOL isIntegrityEnabled;
@property (nonatomic) BOOL isSampleEnabled;
@end
@implementation FBSDKIntegrityManager
- (instancetype)initWithGateKeeperManager:(Class<FBSDKGateKeeperManaging>)gateKeeperManager
integrityProcessor:(id<FBSDKIntegrityProcessing>)integrityProcessor
{
if ((self = [super init])) {
_gateKeeperManager = gateKeeperManager;
_integrityProcessor = integrityProcessor;
}
return self;
}
- (void)enable
{
self.isIntegrityEnabled = YES;
self.isSampleEnabled = [self.gateKeeperManager boolForKey:@"FBSDKFeatureIntegritySample" defaultValue:false];
}
// Unused parameter eventName is required for conformance to shared protocol for processing app events.
- (nullable NSDictionary<NSString *, id> *)processParameters:(nullable NSDictionary<NSString *, id> *)parameters
eventName:(NSString *)eventName
{
if (!self.isIntegrityEnabled || parameters.count == 0) {
return parameters;
}
NSMutableDictionary<NSString *, id> *params = [NSMutableDictionary dictionaryWithDictionary:parameters];
NSMutableDictionary<NSString *, id> *restrictiveParams = [NSMutableDictionary dictionary];
for (NSString *key in [parameters keyEnumerator]) {
NSString *valueString = [FBSDKTypeUtility coercedToStringValue:parameters[key]];
BOOL shouldFilter = [self.integrityProcessor processIntegrity:key] || [self.integrityProcessor processIntegrity:valueString];
if (shouldFilter) {
[FBSDKTypeUtility dictionary:restrictiveParams setObject:self.isSampleEnabled ? valueString : @"" forKey:key];
[params removeObjectForKey:key];
}
}
if ([restrictiveParams count] > 0) {
NSString *restrictiveParamsJSONString = [FBSDKBasicUtility JSONStringForObject:restrictiveParams
error:NULL
invalidObjectHandler:NULL];
[FBSDKTypeUtility dictionary:params setObject:restrictiveParamsJSONString forKey:@"_onDeviceParams"];
}
return [params copy];
}
@end
#endif
@@ -0,0 +1,35 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(RestrictiveData)
@interface FBSDKRestrictiveData : NSObject
- (instancetype)initWithEventName:(NSString *)eventName params:(id)params;
@property (nonatomic, readonly, copy) NSString *eventName;
@property (nullable, nonatomic, readonly, copy) NSDictionary<NSString *, NSString *> *restrictiveParams;
@property (nullable, nonatomic, readonly, copy) NSArray<NSString *> *deprecatedParams;
@property (nonatomic, readonly, assign) BOOL deprecatedEvent;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,47 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKRestrictiveData.h"
#import <Foundation/Foundation.h>
#import "FBSDKCoreKitBasicsImport.h"
#define RESTRICTIVE_PARAM @"restrictive_param"
#define DEPRECATED_PARAM @"deprecated_param"
#define IS_DEPRECATED_EVENT @"is_deprecated_event"
@implementation FBSDKRestrictiveData
- (instancetype)initWithEventName:(NSString *)eventName params:(id)params
{
self = [super init];
if (self) {
NSDictionary<NSString *, id> *paramDict = [FBSDKTypeUtility dictionaryValue:params];
if (!paramDict) {
return nil;
}
_eventName = eventName;
_restrictiveParams = paramDict[RESTRICTIVE_PARAM] ? [FBSDKTypeUtility dictionaryValue:paramDict[RESTRICTIVE_PARAM]] : nil;
_deprecatedParams = paramDict[DEPRECATED_PARAM] ? [FBSDKTypeUtility arrayValue:paramDict[DEPRECATED_PARAM]] : nil;
_deprecatedEvent = (paramDict[IS_DEPRECATED_EVENT] && [paramDict[IS_DEPRECATED_EVENT] respondsToSelector:@selector(boolValue)]) ? [paramDict[IS_DEPRECATED_EVENT] boolValue] : NO;
}
return self;
}
@end
@@ -0,0 +1,28 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKAppEventsParameterProcessing.h"
#import "FBSDKEventsProcessing.h"
#import "FBSDKRestrictiveDataFilterManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKRestrictiveDataFilterManager (AppEventsParameterProcessing) <FBSDKAppEventsParameterProcessing, FBSDKEventsProcessing>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,38 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FBSDKServerConfigurationProviding;
NS_SWIFT_NAME(RestrictiveDataFilterManager)
@interface FBSDKRestrictiveDataFilterManager : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)initWithServerConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider NS_DESIGNATED_INITIALIZER;
- (void)enable;
- (void)processEvents:(NSArray<NSDictionary<NSString *, id> *> *)events;
- (nullable NSDictionary<NSString *, id> *)processParameters:(nullable NSDictionary<NSString *, id> *)parameters
eventName:(NSString *)eventName;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,196 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FBSDKRestrictiveDataFilterManager.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKServerConfigurationManager.h"
#import "FBSDKServerConfigurationProviding.h"
@interface FBSDKRestrictiveEventFilter : NSObject
@property (nonatomic, readonly, copy) NSString *eventName;
@property (nonatomic, readonly, copy) NSDictionary<NSString *, id> *restrictiveParams;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)initWithEventName:(NSString *)eventName
restrictiveParams:(NSDictionary<NSString *, id> *)restrictiveParams;
@end
@implementation FBSDKRestrictiveEventFilter
- (instancetype)initWithEventName:(NSString *)eventName
restrictiveParams:(NSDictionary<NSString *, id> *)restrictiveParams
{
self = [super init];
if (self) {
_eventName = [eventName copy];
_restrictiveParams = [restrictiveParams copy];
}
return self;
}
@end
static FBSDKRestrictiveDataFilterManager *_instance;
@interface FBSDKRestrictiveDataFilterManager ()
@property (nonatomic) BOOL isRestrictiveEventFilterEnabled;
@property (nonatomic) NSMutableArray<FBSDKRestrictiveEventFilter *> *params;
@property (nonatomic) NSMutableSet<NSString *> *restrictedEvents;
@property (nonatomic) id<FBSDKServerConfigurationProviding> serverConfigurationProvider;
@end
@implementation FBSDKRestrictiveDataFilterManager
- (instancetype)initWithServerConfigurationProvider:(id<FBSDKServerConfigurationProviding>)serverConfigurationProvider
{
self.serverConfigurationProvider = serverConfigurationProvider;
return self;
}
- (void)enable
{
@synchronized(self) {
@try {
if (!self.isRestrictiveEventFilterEnabled) {
NSDictionary<NSString *, id> *restrictiveParams = [self.serverConfigurationProvider cachedServerConfiguration].restrictiveParams;
if (restrictiveParams) {
[self updateFilters:restrictiveParams];
self.isRestrictiveEventFilterEnabled = YES;
}
}
} @catch (NSException *exception) {}
}
}
- (NSDictionary<NSString *, id> *)processParameters:(NSDictionary<NSString *, id> *)parameters
eventName:(NSString *)eventName
{
if (!self.isRestrictiveEventFilterEnabled) {
return parameters;
}
if (parameters) {
@try {
NSMutableDictionary<NSString *, id> *params = [NSMutableDictionary dictionaryWithDictionary:parameters];
NSMutableDictionary<NSString *, NSString *> *restrictedParams = [NSMutableDictionary dictionary];
for (NSString *key in [parameters keyEnumerator]) {
NSString *type = [self getMatchedDataTypeWithEventName:eventName paramKey:key];
if (type) {
[FBSDKTypeUtility dictionary:restrictedParams setObject:type forKey:key];
[params removeObjectForKey:key];
}
}
if ([[restrictedParams allKeys] count] > 0) {
NSString *restrictedParamsJSONString = [FBSDKBasicUtility JSONStringForObject:restrictedParams
error:NULL
invalidObjectHandler:NULL];
[FBSDKTypeUtility dictionary:params setObject:restrictedParamsJSONString forKey:@"_restrictedParams"];
}
return [params copy];
} @catch (NSException *exception) {
return parameters;
}
}
return nil;
}
- (void)processEvents:(NSArray<NSMutableDictionary<NSString *, id> *> *)events
{
@try {
if (!self.isRestrictiveEventFilterEnabled) {
return;
}
static NSString *const REPLACEMENT_STRING = @"_removed_";
for (NSDictionary<NSString *, NSMutableDictionary<NSString *, id> *> *event in events) {
if ([self isRestrictedEvent:event[@"event"][@"_eventName"]]) {
[FBSDKTypeUtility dictionary:event[@"event"] setObject:REPLACEMENT_STRING forKey:@"_eventName"];
}
}
} @catch (NSException *exception) {}
}
#pragma mark - Private Methods
- (BOOL)isRestrictedEvent:(NSString *)eventName
{
@synchronized(self) {
return [self.restrictedEvents containsObject:eventName];
}
}
- (nullable NSString *)getMatchedDataTypeWithEventName:(NSString *)eventName
paramKey:(NSString *)paramKey
{
// match by params in custom events with event name
for (FBSDKRestrictiveEventFilter *filter in self.params) {
if ([filter.eventName isEqualToString:eventName]) {
NSString *type = [FBSDKTypeUtility coercedToStringValue:filter.restrictiveParams[paramKey]];
if (type) {
return type;
}
}
}
return nil;
}
- (void)updateFilters:(nullable NSDictionary<NSString *, id> *)restrictiveParams
{
static NSString *const RESTRICTIVE_PARAM_KEY = @"restrictive_param";
static NSString *const PROCESS_EVENT_NAME_KEY = @"process_event_name";
restrictiveParams = [FBSDKTypeUtility dictionaryValue:restrictiveParams];
if (restrictiveParams.count > 0) {
@synchronized(self) {
[self.params removeAllObjects];
[self.restrictedEvents removeAllObjects];
NSMutableArray<FBSDKRestrictiveEventFilter *> *eventFilterArray = [NSMutableArray array];
NSMutableSet<NSString *> *restrictedEventSet = [NSMutableSet set];
for (NSString *eventName in restrictiveParams.allKeys) {
NSDictionary<NSString *, id> *eventInfo = restrictiveParams[eventName];
if (!eventInfo) {
continue;
}
if (eventInfo[RESTRICTIVE_PARAM_KEY]) {
FBSDKRestrictiveEventFilter *restrictiveEventFilter = [[FBSDKRestrictiveEventFilter alloc] initWithEventName:eventName
restrictiveParams:eventInfo[RESTRICTIVE_PARAM_KEY]];
[FBSDKTypeUtility array:eventFilterArray addObject:restrictiveEventFilter];
}
if (restrictiveParams[eventName][PROCESS_EVENT_NAME_KEY]) {
[restrictedEventSet addObject:eventName];
}
}
self.params = eventFilterArray;
self.restrictedEvents = restrictedEventSet;
}
}
}
@end
@@ -0,0 +1,44 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef FBSDKMLMacros_h
#define FBSDKMLMacros_h
// keys for ML
#define MODEL_REQUEST_INTERVAL (60 * 60 * 24 * 3)
#define MODEL_REQUEST_TIMESTAMP_KEY @"com.facebook.sdk:FBSDKModelRequestTimestamp"
#define FBSDK_ML_MODEL_PATH @"models"
#define MODEL_INFO_KEY @"com.facebook.sdk:FBSDKModelInfo"
#define ASSET_URI_KEY @"asset_uri"
#define RULES_URI_KEY @"rules_uri"
#define THRESHOLDS_KEY @"thresholds"
#define USE_CASE_KEY @"use_case"
#define VERSION_ID_KEY @"version_id"
#define MODEL_DATA_KEY @"data"
#define MTMLKey @"MTML"
#define MTMLTaskAppEventPredKey @"MTML_APP_EVENT_PRED"
#define MTMLTaskIntegrityDetectKey @"MTML_INTEGRITY_DETECT"
// keys for Suggested Event
#define SUGGEST_EVENT_KEY @"SUGGEST_EVENT"
#define DENSE_FEATURE_KEY @"DENSE_FEATURE"
#define SUGGESTED_EVENT_OTHER @"other"
#endif /* FBSDKMLMacros_h */
@@ -0,0 +1,33 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKIntegrityParametersProcessorProvider.h"
#import "FBSDKModelManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKModelManager (IntegrityParametersProcessorProvider) <FBSDKIntegrityParametersProcessorProvider>
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,34 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKIntegrityProcessing.h"
#import "FBSDKModelManager.h"
NS_ASSUME_NONNULL_BEGIN
// Default conformance to the integrity processing protocol
@interface FBSDKModelManager (IntegrityProcessing) <FBSDKIntegrityProcessing>
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,33 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKModelManager.h"
#import "FBSDKRulesFromKeyProvider.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKModelManager (RulesFromKeyProvider) <FBSDKRulesFromKeyProvider>
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,57 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
#import "FBSDKEventProcessing.h"
@protocol FBSDKDataPersisting;
@protocol FBSDKFeatureChecking;
@protocol FBSDKFileManaging;
@protocol FBSDKGraphRequestProviding;
@protocol FBSDKSettings;
@protocol FBSDKFileDataExtracting;
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(ModelManager)
@interface FBSDKModelManager : NSObject<FBSDKEventProcessing>
@property (class, nonnull, readonly) FBSDKModelManager *shared;
- (void)enable;
- (nullable NSData *)getWeightsForKey:(NSString *)useCase;
- (nullable NSArray *)getThresholdsForKey:(NSString *)useCase;
- (BOOL)processIntegrity:(nullable NSString *)param;
- (NSString *)processSuggestedEvents:(NSString *)textFeature denseData:(nullable float *)denseData;
- (void)configureWithFeatureChecker:(id<FBSDKFeatureChecking>)featureChecker
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
fileManager:(id<FBSDKFileManaging>)fileManager
store:(id<FBSDKDataPersisting>)store
settings:(id<FBSDKSettings>)settings
dataExtractor:(Class<FBSDKFileDataExtracting>)dataExtractor;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,504 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKModelManager.h"
#import "FBSDKModelManager+IntegrityProcessing.h"
#import "FBSDKAppEvents+Internal.h"
#import "FBSDKAppEventsParameterProcessing.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKDataPersisting.h"
#import "FBSDKFeatureChecking.h"
#import "FBSDKFeatureExtractor.h"
#import "FBSDKGateKeeperManager.h"
#import "FBSDKGraphRequestProviding.h"
#import "FBSDKIntegrityManager+AppEventsParametersProcessing.h"
#import "FBSDKMLMacros.h"
#import "FBSDKModelParser.h"
#import "FBSDKModelRuntime.hpp"
#import "FBSDKModelUtility.h"
#import "FBSDKSettingsProtocol.h"
#import "FBSDKSuggestedEventsIndexer.h"
static NSString *const INTEGRITY_NONE = @"none";
static NSString *const INTEGRITY_ADDRESS = @"address";
static NSString *const INTEGRITY_HEALTH = @"health";
extern FBSDKAppEventName FBSDKAppEventNameCompletedRegistration;
extern FBSDKAppEventName FBSDKAppEventNameAddedToCart;
extern FBSDKAppEventName FBSDKAppEventNamePurchased;
extern FBSDKAppEventName FBSDKAppEventNameInitiatedCheckout;
static NSString *_directoryPath;
static NSMutableDictionary<NSString *, id> *_modelInfo;
static std::unordered_map<std::string, fbsdk::MTensor> _MTMLWeights;
NS_ASSUME_NONNULL_BEGIN
@interface FBSDKModelManager ()
@property (nonatomic) id<FBSDKAppEventsParameterProcessing> integrityParametersProcessor;
@property (nullable, nonatomic) id<FBSDKFeatureChecking> featureChecker;
@property (nullable, nonatomic) id<FBSDKGraphRequestProviding> graphRequestFactory;
@property (nullable, nonatomic) id<FBSDKFileManaging> fileManager;
@property (nullable, nonatomic) id<FBSDKDataPersisting> store;
@property (nullable, nonatomic) id<FBSDKSettings> settings;
@property (nullable, nonatomic) Class<FBSDKFileDataExtracting> dataExtractor;
@end
@implementation FBSDKModelManager
typedef void (^FBSDKDownloadCompletionBlock)(void);
// Transitional singleton introduced as a way to change the usage semantics
// from a type-based interface to an instance-based interface.
+ (instancetype)shared
{
static dispatch_once_t nonce;
static id instance;
dispatch_once(&nonce, ^{
instance = [self new];
});
return instance;
}
#pragma mark - Dependency Management
- (void)configureWithFeatureChecker:(id<FBSDKFeatureChecking>)featureChecker
graphRequestFactory:(id<FBSDKGraphRequestProviding>)graphRequestFactory
fileManager:(id<FBSDKFileManaging>)fileManager
store:(id<FBSDKDataPersisting>)store
settings:(id<FBSDKSettings>)settings
dataExtractor:(Class<FBSDKFileDataExtracting>)dataExtractor
{
_featureChecker = featureChecker;
_graphRequestFactory = graphRequestFactory;
_fileManager = fileManager;
_store = store;
_settings = settings;
_dataExtractor = dataExtractor;
}
#pragma mark - Public methods
static dispatch_once_t enableNonce;
- (void)enable
{
@try {
dispatch_once(&enableNonce, ^{
NSString *languageCode = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode];
// If the languageCode could not be fetched successfully, it's regarded as "en" by default.
if (languageCode && ![languageCode isEqualToString:@"en"]) {
return;
}
_directoryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:FBSDK_ML_MODEL_PATH];
if (![self.fileManager fileExistsAtPath:_directoryPath]) {
[self.fileManager createDirectoryAtPath:_directoryPath withIntermediateDirectories:YES attributes:NULL error:NULL];
}
_modelInfo = [self.store objectForKey:MODEL_INFO_KEY];
NSDate *timestamp = [self.store objectForKey:MODEL_REQUEST_TIMESTAMP_KEY];
if ([_modelInfo count] == 0 || ![self.featureChecker isEnabled:FBSDKFeatureModelRequest] || ![self.class isValidTimestamp:timestamp]) {
// fetch api
NSString *graphPath = [NSString stringWithFormat:@"%@/model_asset", self.settings.appID];
id<FBSDKGraphRequest> request = [self.graphRequestFactory createGraphRequestWithGraphPath:graphPath];
__weak FBSDKModelManager *weakSelf = self;
[request startWithCompletion:^(id<FBSDKGraphRequestConnecting> connection, id result, NSError *error) {
if (!error) {
NSDictionary<NSString *, id> *resultDictionary = [FBSDKTypeUtility dictionaryValue:result];
NSArray *rawModels = resultDictionary[MODEL_DATA_KEY];
if ([rawModels isKindOfClass:NSArray.class]) {
NSDictionary<NSString *, id> *modelInfo = [weakSelf.class convertToDictionary:rawModels];
if (modelInfo) {
_modelInfo = [modelInfo mutableCopy];
[weakSelf.class processMTML];
// update cache for model info and timestamp
[weakSelf.store setObject:_modelInfo forKey:MODEL_INFO_KEY];
[weakSelf.store setObject:[NSDate date] forKey:MODEL_REQUEST_TIMESTAMP_KEY];
}
}
}
[self checkFeaturesAndExecuteForMTML];
}];
} else {
[self checkFeaturesAndExecuteForMTML];
}
});
} @catch (NSException *exception) {
NSLog(@"Fail to enable model manager, exception reason: %@", exception.reason);
}
}
- (nullable NSDictionary *)getRulesForKey:(NSString *)useCase
{
@try {
NSDictionary<NSString *, id> *model = [FBSDKTypeUtility dictionary:_modelInfo objectForKey:useCase ofType:NSObject.class];
if (model && model[VERSION_ID_KEY]) {
NSString *filePath = [_directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@.rules", useCase, model[VERSION_ID_KEY]]];
if (filePath) {
NSData *rulesData = [self.dataExtractor dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:nil];
NSDictionary *rules = [FBSDKTypeUtility JSONObjectWithData:rulesData options:0 error:nil];
return rules;
}
}
} @catch (NSException *exception) {
NSLog(@"Fail to get rules for usecase %@ from ml model, exception reason: %@", useCase, exception.reason);
}
return nil;
}
- (nullable NSData *)getWeightsForKey:(NSString *)useCase
{
if (!_modelInfo || !_directoryPath) {
return nil;
}
if ([useCase hasPrefix:MTMLKey]) {
useCase = MTMLKey;
}
NSDictionary<NSString *, id> *model = [FBSDKTypeUtility dictionary:_modelInfo objectForKey:useCase ofType:NSObject.class];
if (model && model[VERSION_ID_KEY]) {
NSString *path = [_directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@.weights", useCase, model[VERSION_ID_KEY]]];
if (!path) {
return nil;
}
return [NSData dataWithContentsOfFile:path
options:NSDataReadingMappedIfSafe
error:nil];
}
return nil;
}
- (nullable NSArray *)getThresholdsForKey:(NSString *)useCase
{
if (!_modelInfo) {
return nil;
}
NSDictionary<NSString *, id> *modelInfo = _modelInfo[useCase];
if (!modelInfo) {
return nil;
}
return modelInfo[THRESHOLDS_KEY];
}
#pragma mark - Integrity Inferencer method
// Used by the `integrityParametersProcessor` which holds a weak reference to this instance
- (BOOL)processIntegrity:(nullable NSString *)param
{
NSString *integrityType = INTEGRITY_NONE;
@try {
if (param.length == 0 || _MTMLWeights.size() == 0) {
return false;
}
NSArray<NSString *> *integrityMapping = [self.class getIntegrityMapping];
NSString *text = [FBSDKModelUtility normalizedText:param];
const char *bytes = [text UTF8String];
if ((int)strlen(bytes) == 0) {
return false;
}
NSArray *thresholds = [FBSDKModelManager.shared getThresholdsForKey:MTMLTaskIntegrityDetectKey];
if (thresholds.count != integrityMapping.count) {
return false;
}
const fbsdk::MTensor &res = fbsdk::predictOnMTML("integrity_detect", bytes, _MTMLWeights, nullptr);
const float *res_data = res.data();
for (int i = 0; i < thresholds.count; i++) {
if ((float)res_data[i] >= (float)[[FBSDKTypeUtility array:thresholds objectAtIndex:i] floatValue]) {
integrityType = [FBSDKTypeUtility array:integrityMapping objectAtIndex:i];
break;
}
}
} @catch (NSException *exception) {
NSLog(@"Fail to process parameter for integrity usecase, exception reason: %@", exception.reason);
}
return ![integrityType isEqualToString:INTEGRITY_NONE];
}
#pragma mark - SuggestedEvents Inferencer method
- (NSString *)processSuggestedEvents:(NSString *)textFeature denseData:(nullable float *)denseData
{
@try {
NSArray<NSString *> *eventMapping = [FBSDKModelManager getSuggestedEventsMapping];
if (textFeature.length == 0 || _MTMLWeights.size() == 0 || !denseData) {
return SUGGESTED_EVENT_OTHER;
}
const char *bytes = [textFeature UTF8String];
if ((int)strlen(bytes) == 0) {
return SUGGESTED_EVENT_OTHER;
}
NSArray *thresholds = [FBSDKModelManager.shared getThresholdsForKey:MTMLTaskAppEventPredKey];
if (thresholds.count != eventMapping.count) {
return SUGGESTED_EVENT_OTHER;
}
const fbsdk::MTensor &res = fbsdk::predictOnMTML("app_event_pred", bytes, _MTMLWeights, denseData);
const float *res_data = res.data();
for (int i = 0; i < thresholds.count; i++) {
if ((float)res_data[i] >= (float)[[FBSDKTypeUtility array:thresholds objectAtIndex:i] floatValue]) {
return [FBSDKTypeUtility array:eventMapping objectAtIndex:i];
}
}
} @catch (NSException *exception) {
NSLog(@"Fail to process suggested events, exception reason: %@", exception.reason);
}
return SUGGESTED_EVENT_OTHER;
}
#pragma mark - Private methods
+ (BOOL)isValidTimestamp:(NSDate *)timestamp
{
if (!timestamp) {
return NO;
}
return ([[NSDate date] timeIntervalSinceDate:timestamp] < MODEL_REQUEST_INTERVAL);
}
+ (void)processMTML
{
NSString *mtmlAssetUri = nil;
long mtmlVersionId = 0;
for (NSString *useCase in _modelInfo) {
if (![useCase isKindOfClass:NSString.class]) {
continue;
}
NSDictionary<NSString *, id> *model = _modelInfo[useCase];
if ([useCase hasPrefix:MTMLKey]) {
if (![model[ASSET_URI_KEY] isKindOfClass:NSString.class]
|| ![model[VERSION_ID_KEY] isKindOfClass:NSNumber.class]) {
continue;
}
mtmlAssetUri = model[ASSET_URI_KEY];
long thisVersionId = [model[VERSION_ID_KEY] longValue];
mtmlVersionId = thisVersionId > mtmlVersionId ? thisVersionId : mtmlVersionId;
}
}
if (mtmlAssetUri && mtmlVersionId > 0) {
[FBSDKTypeUtility dictionary:_modelInfo setObject:@{
USE_CASE_KEY : MTMLKey,
ASSET_URI_KEY : mtmlAssetUri,
VERSION_ID_KEY : [NSNumber numberWithLong:mtmlVersionId],
} forKey:MTMLKey];
}
}
- (void)checkFeaturesAndExecuteForMTML
{
[self getModelAndRules:MTMLKey onSuccess:^() {
NSData *data = [FBSDKModelManager.shared getWeightsForKey:MTMLKey];
_MTMLWeights = [FBSDKModelParser parseWeightsData:data];
if (![FBSDKModelParser validateWeights:_MTMLWeights forKey:MTMLKey]) {
return;
}
if ([self.featureChecker isEnabled:FBSDKFeatureSuggestedEvents]) {
[self getModelAndRules:MTMLTaskAppEventPredKey onSuccess:^() {
[FBSDKFeatureExtractor loadRulesForKey:MTMLTaskAppEventPredKey];
[FBSDKSuggestedEventsIndexer.shared enable];
}];
}
if ([self.featureChecker isEnabled:FBSDKFeatureIntelligentIntegrity]) {
[self getModelAndRules:MTMLTaskIntegrityDetectKey onSuccess:^() {
[self setIntegrityParametersProcessor:[[FBSDKIntegrityManager alloc] initWithGateKeeperManager:FBSDKGateKeeperManager.class
integrityProcessor:self]];
[[self integrityParametersProcessor] enable];
}];
}
}];
}
- (void)getModelAndRules:(NSString *)useCaseKey
onSuccess:(FBSDKDownloadCompletionBlock)handler
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSDictionary<NSString *, id> *model = [FBSDKTypeUtility dictionary:_modelInfo objectForKey:useCaseKey ofType:NSObject.class];
if (!model || !_directoryPath) {
return;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
// download model asset only if not exist before
NSString *assetUrlString = [FBSDKTypeUtility dictionary:model objectForKey:ASSET_URI_KEY ofType:NSObject.class];
NSString *assetFilePath;
if (assetUrlString.length > 0) {
[self clearCacheForModel:model suffix:@".weights"];
NSString *fileName = useCaseKey;
if ([useCaseKey hasPrefix:MTMLKey]) {
// all mtml tasks share the same weights file
fileName = MTMLKey;
}
assetFilePath = [_directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@.weights", fileName, model[VERSION_ID_KEY]]];
[self download:assetUrlString filePath:assetFilePath queue:queue group:group];
}
// download rules
NSString *rulesUrlString = [FBSDKTypeUtility dictionary:model objectForKey:RULES_URI_KEY ofType:NSObject.class];
NSString *rulesFilePath = nil;
// rules are optional and rulesUrlString may be empty
if (rulesUrlString.length > 0) {
[self clearCacheForModel:model suffix:@".rules"];
rulesFilePath = [_directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@.rules", useCaseKey, model[VERSION_ID_KEY]]];
[self download:rulesUrlString filePath:rulesFilePath queue:queue group:group];
}
dispatch_group_notify(group,
dispatch_get_main_queue(), ^{
if (handler) {
if ([fileManager fileExistsAtPath:assetFilePath] && (!rulesFilePath || [fileManager fileExistsAtPath:rulesFilePath])) {
handler();
}
}
});
}
- (void)clearCacheForModel:(NSDictionary<NSString *, id> *)model
suffix:(NSString *)suffix
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *useCase = model[USE_CASE_KEY];
NSString *version = model[VERSION_ID_KEY];
NSArray<NSString *> *files = [fileManager contentsOfDirectoryAtPath:_directoryPath error:nil];
NSString *prefixWithVersion = [NSString stringWithFormat:@"%@_%@", useCase, version];
for (NSString *file in files) {
if ([file hasSuffix:suffix] && [file hasPrefix:useCase] && ![file hasPrefix:prefixWithVersion]) {
[fileManager removeItemAtPath:[_directoryPath stringByAppendingPathComponent:file] error:nil];
}
}
}
- (void)download:(NSString *)urlString
filePath:(NSString *)filePath
queue:(dispatch_queue_t)queue
group:(dispatch_group_t)group
{
if (!filePath || [[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
return;
}
dispatch_group_async(group,
queue, ^{
NSURL *url = [NSURL URLWithString:urlString];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if (urlData) {
[urlData writeToFile:filePath atomically:YES];
}
});
}
+ (nullable NSMutableDictionary<NSString *, id> *)convertToDictionary:(NSArray<NSDictionary<NSString *, id> *> *)models
{
if ([models count] == 0) {
return nil;
}
NSMutableDictionary<NSString *, id> *modelInfo = [NSMutableDictionary dictionary];
for (NSDictionary<NSString *, id> *model in models) {
if ([model isKindOfClass:NSDictionary.class]
&& [model[USE_CASE_KEY] isKindOfClass:NSString.class]
&& [self isPlistFormatDictionary:model]) {
[modelInfo addEntriesFromDictionary:@{model[USE_CASE_KEY] : model}];
}
}
if (modelInfo.allKeys.count > 0) {
return modelInfo;
} else {
return nil;
}
}
+ (BOOL)isPlistFormatDictionary:(NSDictionary *)dictionary
{
__block BOOL isPlistFormat = YES;
[dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
if (![key isKindOfClass:NSString.class]) {
isPlistFormat = NO;
*stop = YES;
}
if (![obj isKindOfClass:NSArray.class]
&& ![obj isKindOfClass:NSDictionary.class]
&& ![obj isKindOfClass:NSData.class]
&& ![obj isKindOfClass:NSDate.class]
&& ![obj isKindOfClass:NSNumber.class]
&& ![obj isKindOfClass:NSString.class]) {
isPlistFormat = NO;
*stop = YES;
}
}];
return isPlistFormat;
}
+ (NSArray<NSString *> *)getIntegrityMapping
{
return @[INTEGRITY_NONE, INTEGRITY_ADDRESS, INTEGRITY_HEALTH];
}
+ (NSArray<NSString *> *)getSuggestedEventsMapping
{
return
@[SUGGESTED_EVENT_OTHER,
FBSDKAppEventNameCompletedRegistration,
FBSDKAppEventNameAddedToCart,
FBSDKAppEventNamePurchased,
FBSDKAppEventNameInitiatedCheckout];
}
#if DEBUG && FBTEST
+ (void)reset
{
if (enableNonce) {
enableNonce = 0;
}
_directoryPath = nil;
_modelInfo = nil;
self.shared.featureChecker = nil;
self.shared.graphRequestFactory = nil;
self.shared.fileManager = nil;
self.shared.store = nil;
self.shared.settings = nil;
self.shared.dataExtractor = nil;
}
+ (void)setModelInfo:(NSDictionary<NSString *, id> *)modelInfo
{
_modelInfo = [NSMutableDictionary dictionaryWithDictionary:modelInfo];
}
+ (void)setDirectoryPath:(NSString *)directoryPath
{
_directoryPath = directoryPath;
}
#endif
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,39 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
#import "FBSDKTensor.hpp"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(ModelParser)
@interface FBSDKModelParser : NSObject
+ (std::unordered_map<std::string, fbsdk::MTensor>)parseWeightsData:(NSData *)weightsData;
+ (bool)validateWeights:(std::unordered_map<std::string, fbsdk::MTensor>)weights forKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,177 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKModelParser.h"
#import "FBSDKCoreKitBasicsImport.h"
#import "FBSDKMLMacros.h"
NS_ASSUME_NONNULL_BEGIN
@implementation FBSDKModelParser
+ (std::unordered_map<std::string, fbsdk::MTensor>)parseWeightsData:(NSData *)weightsData
{
std::unordered_map<std::string, fbsdk::MTensor> weights;
if (!weightsData) {
return weights;
}
const void *data = weightsData.bytes;
NSUInteger totalLength = weightsData.length;
if (totalLength < 4) {
// Make sure data length is valid
return weights;
}
try {
int length;
memcpy(&length, data, 4);
if (length + 4 > totalLength) {
// Make sure data length is valid
return weights;
}
char *json = (char *)data + 4;
NSDictionary<NSString *, id> *info = [FBSDKTypeUtility JSONObjectWithData:[NSData dataWithBytes:json length:length]
options:0
error:nil];
NSArray<NSString *> *keys = [[info allKeys] sortedArrayUsingComparator:^NSComparisonResult (NSString *key1, NSString *key2) {
return [key1 compare:key2];
}];
int totalFloats = 0;
float *floats = (float *)(json + length);
NSDictionary<NSString *, NSString *> *keysMapping = [self getKeysMapping];
for (NSString *key in keys) {
NSString *finalKey = key;
NSString *mapping = [FBSDKTypeUtility dictionary:keysMapping objectForKey:key ofType:NSObject.class];
if (mapping) {
finalKey = mapping;
}
std::string s_name([finalKey UTF8String]);
std::vector<int> v_shape;
NSArray<NSString *> *shape = [FBSDKTypeUtility dictionary:info objectForKey:key ofType:NSObject.class];
int count = 1;
for (NSNumber *_s in shape) {
int i = [_s intValue];
v_shape.push_back(i);
count *= i;
}
totalFloats += count;
if ((4 + length + totalFloats * 4) > totalLength) {
// Make sure data length is valid
break;
}
fbsdk::MTensor tensor(v_shape);
memcpy(tensor.mutable_data(), floats, sizeof(float) * count);
floats += count;
weights[s_name] = tensor;
}
} catch (const std::exception &e) {}
return weights;
}
+ (bool)validateWeights:(std::unordered_map<std::string, fbsdk::MTensor>)weights forKey:(NSString *)key
{
NSMutableDictionary<NSString *, NSArray *> *weightsInfoDict = [NSMutableDictionary new];
if ([key hasPrefix:MTMLKey]) {
[weightsInfoDict addEntriesFromDictionary:[self getMTMLWeightsInfo]];
}
return [self checkWeights:weights withExpectedInfo:weightsInfoDict];
}
#pragma mark - private methods
+ (NSDictionary<NSString *, NSString *> *)getKeysMapping
{
return @{
@"embedding.weight" : @"embed.weight",
@"dense1.weight" : @"fc1.weight",
@"dense2.weight" : @"fc2.weight",
@"dense3.weight" : @"fc3.weight",
@"dense1.bias" : @"fc1.bias",
@"dense2.bias" : @"fc2.bias",
@"dense3.bias" : @"fc3.bias"
};
}
+ (NSDictionary<NSString *, NSArray *> *)getMTMLWeightsInfo
{
return @{
@"embed.weight" : @[@256, @32],
@"convs.0.weight" : @[@32, @32, @3],
@"convs.0.bias" : @[@32],
@"convs.1.weight" : @[@64, @32, @3],
@"convs.1.bias" : @[@64],
@"convs.2.weight" : @[@64, @64, @3],
@"convs.2.bias" : @[@64],
@"fc1.weight" : @[@128, @190],
@"fc1.bias" : @[@128],
@"fc2.weight" : @[@64, @128],
@"fc2.bias" : @[@64],
@"integrity_detect.weight" : @[@3, @64],
@"integrity_detect.bias" : @[@3],
@"app_event_pred.weight" : @[@5, @64],
@"app_event_pred.bias" : @[@5]
};
}
+ (bool)checkWeights:(std::unordered_map<std::string, fbsdk::MTensor>)weights
withExpectedInfo:(NSDictionary<NSString *, NSArray *> *)weightsInfoDict
{
if (weightsInfoDict.count != weights.size()) {
return false;
}
try {
for (NSString *key in weightsInfoDict) {
if (weights.count(std::string([key UTF8String])) == 0) {
return false;
}
fbsdk::MTensor tensor = weights[std::string([key UTF8String])];
const std::vector<int> &actualSize = tensor.sizes();
NSArray *expectedSize = weightsInfoDict[key];
if (actualSize.size() != expectedSize.count) {
return false;
}
for (int i = 0; i < expectedSize.count; i++) {
if ((int)actualSize[i] != (int)[[FBSDKTypeUtility array:expectedSize objectAtIndex:i] intValue]) {
return false;
}
}
}
} catch (const std::exception &e) {
return false;
}
return true;
}
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,347 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#include <unordered_map>
#include <float.h>
#include <math.h>
#include <stdint.h>
#import <Accelerate/Accelerate.h>
#include "FBSDKTensor.hpp"
#define SEQ_LEN 128
#define DENSE_FEATURE_LEN 30
namespace fbsdk {
static void relu(MTensor &x)
{
float min = 0;
float max = FLT_MAX;
float *x_data = x.mutable_data();
vDSP_vclip(x_data, 1, &min, &max, x_data, 1, x.count());
}
static void flatten(MTensor &x, int start_dim)
{
const std::vector<int> &shape = x.sizes();
std::vector<int> new_shape;
for (int i = 0; i < start_dim; i++) {
new_shape.push_back(shape[i]);
}
int count = 1;
for (int i = start_dim; i < shape.size(); i++) {
count *= shape[i];
}
new_shape.push_back(count);
x.Reshape(new_shape);
}
static MTensor concatenate(std::vector<MTensor *> &tensors)
{
int n_examples = tensors[0]->size(0);
int count = 0;
for (int i = 0; i < tensors.size(); i++) {
count += tensors[i]->size(1);
}
MTensor y({n_examples, count});
float *y_data = y.mutable_data();
for (int i = 0; i < tensors.size(); i++) {
int this_count = (int)tensors[i]->size(1);
const float *this_data = tensors[i]->data();
for (int n = 0; n < n_examples; n++) {
memcpy(y_data + n * count, this_data + n * this_count, this_count * sizeof(float));
}
y_data += this_count;
}
return y;
}
static void softmax(MTensor &x)
{
int n_examples = x.size(0);
int n_channel = x.size(1);
float *x_data = x.mutable_data();
float max;
float sum;
for (int n = 0; n < n_examples; n++) {
vDSP_maxv(x_data, 1, &max, n_channel);
max = -max;
vDSP_vsadd(x_data, 1, &max, x_data, 1, n_channel);
vvexpf(x_data, x_data, &n_channel);
vDSP_sve(x_data, 1, &sum, n_channel);
vDSP_vsdiv(x_data, 1, &sum, x_data, 1, n_channel);
x_data += n_channel;
}
}
static std::vector<int> vectorize(const char *texts, const int seq_length)
{
int str_len = (int)strlen(texts);
std::vector<int> vec(seq_length, 0);
for (int i = 0; i < seq_length; i++) {
if (i < str_len) {
vec[i] = static_cast<unsigned char>(texts[i]);
}
}
return vec;
}
static MTensor embedding(const char *texts, const int seq_length, const MTensor &w)
{
// TODO: T65152708 support batch prediction
const std::vector<int> &vec = vectorize(texts, seq_length);
int n_examples = 1;
int embedding_size = w.size(1);
MTensor y({n_examples, seq_length, embedding_size});
const float *w_data = w.data();
float *y_data = y.mutable_data();
for (int i = 0; i < n_examples; i++) {
for (int j = 0; j < seq_length; j++) {
memcpy(y_data, w_data + vec[i * seq_length + j] * embedding_size, (size_t)(embedding_size * sizeof(float)));
y_data += embedding_size;
}
}
return y;
}
/*
x shape: n_examples, in_vector_size
w shape: in_vector_size, out_vector_size
b shape: out_vector_size
return shape: n_examples, out_vector_size
*/
static MTensor dense(const MTensor &x, const MTensor &w, const MTensor &b)
{
int n_examples = x.size(0);
int in_vector_size = x.size(1);
int out_vector_size = w.size(1);
MTensor y({n_examples, out_vector_size});
float *y_data = y.mutable_data();
const float *b_data = b.data();
vDSP_mmul(x.data(), 1, w.data(), 1, y_data, 1, n_examples, out_vector_size, in_vector_size);
for (int i = 0; i < out_vector_size; i++) {
vDSP_vsadd(y_data + i, out_vector_size, b_data + i, y_data + i, out_vector_size, n_examples);
}
return y;
}
/*
x shape: n_examples, seq_len, input_size
w shape: kernel_size, input_size, output_size
return shape: n_examples, seq_len - kernel_size + 1, output_size
*/
static MTensor conv1D(const MTensor &x, const MTensor &w)
{
int n_examples = x.size(0);
int seq_len = x.size(1);
int input_size = x.size(2);
int kernel_size = w.size(0);
int output_size = w.size(2);
MTensor y({n_examples, seq_len - kernel_size + 1, output_size});
MTensor temp_x({kernel_size, input_size});
MTensor temp_w({kernel_size, input_size});
const float *x_data = x.data();
const float *w_data = w.data();
float *y_data = y.mutable_data();
float *temp_x_data = temp_x.mutable_data();
float *temp_w_data = temp_w.mutable_data();
float sum;
for (int n = 0; n < n_examples; n++) {
for (int o = 0; o < output_size; o++) {
for (int i = 0; i < seq_len - kernel_size + 1; i++) {
for (int m = 0; m < kernel_size; m++) {
for (int k = 0; k < input_size; k++) {
temp_x_data[m * input_size + k] = x_data[n * (seq_len * input_size) + (m + i) * input_size + k];
temp_w_data[m * input_size + k] = w_data[(m * input_size + k) * output_size + o];
}
}
vDSP_dotpr(temp_x_data, 1, temp_w_data, 1, &sum, (size_t)(kernel_size * input_size));
y_data[(n * (output_size * (seq_len - kernel_size + 1)) + i * output_size + o)] = sum;
}
}
}
return y;
}
/*
input shape: n_examples, len, n_channel
return shape: n_examples, len - pool_size + 1, n_channel
*/
static MTensor maxPool1D(const MTensor &x, const int pool_size)
{
int n_examples = x.size(0);
int input_len = x.size(1);
int n_channel = x.size(2);
int output_len = input_len - pool_size + 1;
MTensor y({n_examples, output_len, n_channel});
const float *x_data = x.data();
float *y_data = y.mutable_data();
for (int n = 0; n < n_examples; n++) {
for (int c = 0; c < n_channel; c++) {
for (int i = 0; i < output_len; i++) {
float this_max = -FLT_MAX;
for (int r = i; r < i + pool_size; r++) {
this_max = fmax(this_max, x_data[n * (n_channel * input_len) + r * n_channel + c]);
}
y_data[n * (n_channel * output_len) + i * n_channel + c] = this_max;
}
}
}
return y;
}
/*
input shape: m, n
return shape: n, m
*/
static MTensor transpose2D(const MTensor &x)
{
int m = x.size(0);
int n = x.size(1);
MTensor y({n, m});
float *y_data = y.mutable_data();
const float *x_data = x.data();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
y_data[j * m + i] = x_data[i * n + j];
}
}
return y;
}
/*
input shape: m, n, p
return shape: p, n, m
*/
static MTensor transpose3D(const MTensor &x)
{
int m = x.size(0);
int n = x.size(1);
int p = x.size(2);
MTensor y({p, n, m});
float *y_data = y.mutable_data();
const float *x_data = x.data();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < p; k++) {
y_data[k * m * n + j * m + i] = x_data[i * n * p + j * p + k];
}
}
}
return y;
}
static void addmv(MTensor &y, const MTensor &x)
{
int m = y.size(0);
int n = y.size(1);
int p = y.size(2);
float *y_data = y.mutable_data();
const float *x_data = x.data();
for (int i = 0; i < p; i++) {
vDSP_vsadd(y_data + i, p, x_data + i, y_data + i, p, m * n);
}
}
static MTensor getDenseTensor(const float *df)
{
MTensor dense_tensor({1, DENSE_FEATURE_LEN});
if (df) {
memcpy(dense_tensor.mutable_data(), df, DENSE_FEATURE_LEN * sizeof(float));
} else {
memset(dense_tensor.mutable_data(), 0, DENSE_FEATURE_LEN * sizeof(float));
}
return dense_tensor;
}
static MTensor predictOnMTML(const std::string task, const char *texts, const std::unordered_map<std::string, MTensor> &weights, const float *df)
{
MTensor dense_tensor = getDenseTensor(df);
std::string final_layer_weight_key = task + ".weight";
std::string final_layer_bias_key = task + ".bias";
const MTensor &embed_t = weights.at("embed.weight");
const MTensor &conv0w_t = weights.at("convs.0.weight");
const MTensor &conv1w_t = weights.at("convs.1.weight");
const MTensor &conv2w_t = weights.at("convs.2.weight");
const MTensor &conv0b_t = weights.at("convs.0.bias");
const MTensor &conv1b_t = weights.at("convs.1.bias");
const MTensor &conv2b_t = weights.at("convs.2.bias");
const MTensor &fc1w_t = weights.at("fc1.weight"); // (128, 190)
const MTensor &fc1b_t = weights.at("fc1.bias"); // 128
const MTensor &fc2w_t = weights.at("fc2.weight"); // (64, 128)
const MTensor &fc2b_t = weights.at("fc2.bias"); // 64
const MTensor &final_layer_weight_t = weights.at(final_layer_weight_key); // (2, 64) or (5, 64)
const MTensor &final_layer_bias_t = weights.at(final_layer_bias_key); // 2 or 5
const MTensor &convs_0_weight = transpose3D(conv0w_t);
const MTensor &convs_1_weight = transpose3D(conv1w_t);
const MTensor &convs_2_weight = transpose3D(conv2w_t);
const MTensor &fc1_weight = transpose2D(fc1w_t);
const MTensor &fc2_weight = transpose2D(fc2w_t);
const MTensor &final_layer_weight = transpose2D(final_layer_weight_t);
// embedding
const MTensor &embed_x = embedding(texts, SEQ_LEN, embed_t);
// conv0
MTensor c0 = conv1D(embed_x, convs_0_weight); // (1, 126, 32)
addmv(c0, conv0b_t);
relu(c0);
// conv1
MTensor c1 = conv1D(c0, convs_1_weight); // (1, 124, 64)
addmv(c1, conv1b_t);
relu(c1);
c1 = maxPool1D(c1, 2); // (1, 123, 64)
// conv2
MTensor c2 = conv1D(c1, convs_2_weight); // (1, 121, 64)
addmv(c2, conv2b_t);
relu(c2);
// max pooling
MTensor ca = maxPool1D(c0, c0.size(1));
MTensor cb = maxPool1D(c1, c1.size(1));
MTensor cc = maxPool1D(c2, c2.size(1));
// concatenate
flatten(ca, 1);
flatten(cb, 1);
flatten(cc, 1);
std::vector<MTensor *> concat_tensors { &ca, &cb, &cc, &dense_tensor };
const MTensor &concat = concatenate(concat_tensors);
// dense + relu
MTensor dense1_x = dense(concat, fc1_weight, fc1b_t);
relu(dense1_x);
MTensor dense2_x = dense(dense1_x, fc2_weight, fc2b_t);
relu(dense2_x);
MTensor final_layer_dense_x = dense(dense2_x, final_layer_weight, final_layer_bias_t);
softmax(final_layer_dense_x);
return final_layer_dense_x;
}
}
#endif
@@ -0,0 +1,36 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(ModelUtility)
@interface FBSDKModelUtility : NSObject
+ (NSString *)normalizedText:(NSString *)text;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,38 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBSDKModelUtility.h"
#import <Foundation/Foundation.h>
@implementation FBSDKModelUtility : NSObject
+ (NSString *)normalizedText:(NSString *)text
{
NSMutableArray *tokens = [[text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
[tokens removeObject:@""];
return [tokens componentsJoinedByString:@" "];
}
@end
#endif
@@ -0,0 +1,134 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#include <cassert>
#include <cmath>
#include <cstring>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#import <Accelerate/Accelerate.h>
// minimal aten implementation
#define MAT_ALWAYS_INLINE inline __attribute__((always_inline))
namespace fbsdk {
static void *MAllocateMemory(size_t nbytes)
{
void *ptr = nullptr;
assert(nbytes > 0);
#ifdef __ANDROID__
ptr = memalign(64, nbytes);
#else
const int ret = posix_memalign(&ptr, 64, nbytes);
(void)ret;
assert(ret == 0);
#endif
return ptr;
}
static void MFreeMemory(void *ptr)
{
if (ptr) {
free(ptr);
}
}
class MTensor {
public:
MTensor() :
storage_(nullptr),
sizes_(),
strides_(),
capacity_(0) {};
explicit MTensor(const std::vector<int> &sizes)
{
std::vector<int> strides = std::vector<int>(sizes.size());
strides[strides.size() - 1] = 1;
for (int i = static_cast<int32_t>(strides.size()) - 2; i >= 0; --i) {
strides[i] = strides[i + 1] * sizes[i + 1];
}
strides_ = strides;
sizes_ = sizes;
capacity_ = 1;
for (int size : sizes) {
capacity_ *= size;
}
storage_ = std::shared_ptr<void>(MAllocateMemory((size_t)capacity_ * sizeof(float)), MFreeMemory);
}
MAT_ALWAYS_INLINE int count() const
{
return capacity_;
}
MAT_ALWAYS_INLINE int size(int dim) const
{
return sizes_[dim];
}
MAT_ALWAYS_INLINE const std::vector<int> &sizes() const
{
return sizes_;
}
MAT_ALWAYS_INLINE const std::vector<int> &strides() const
{
return strides_;
}
MAT_ALWAYS_INLINE const float *data() const
{
return (const float *)(storage_.get());
}
MAT_ALWAYS_INLINE float *mutable_data()
{
return static_cast<float *>(storage_.get());
}
MAT_ALWAYS_INLINE void Reshape(const std::vector<int> &sizes)
{
int count = 1;
for (int i = 0; i < sizes.size(); i++) {
count *= sizes[i];
}
if (count > capacity_) {
capacity_ = count;
storage_.reset(MAllocateMemory((size_t)capacity_ * sizeof(float)), MFreeMemory);
}
sizes_ = sizes;
}
private:
int capacity_;
std::vector<int> sizes_;
std::vector<int> strides_;
std::shared_ptr<void> storage_;
};
}
#endif
@@ -0,0 +1,32 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
NS_ASSUME_NONNULL_BEGIN
/// An internal protocol used to describe a type that can update a value
NS_SWIFT_NAME(ConversionValueUpdating)
@protocol FBSDKConversionValueUpdating
+ (void)updateConversionValue:(NSInteger)conversionValue;
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show More