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
@@ -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 "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
#import "FBAEMAdvertiserRuleMatching.h"
#import "FBAEMAdvertiserRuleOperator.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AEMAdvertiserMultiEntryRule)
@interface FBAEMAdvertiserMultiEntryRule : NSObject<FBAEMAdvertiserRuleMatching, NSCopying, NSSecureCoding>
@property (nonatomic, readonly, assign) FBAEMAdvertiserRuleOperator operator;
@property (nonatomic, readonly) NSArray<id<FBAEMAdvertiserRuleMatching>> *rules;
- (instancetype)initWithOperator:(FBAEMAdvertiserRuleOperator)op
rules:(NSArray<id<FBAEMAdvertiserRuleMatching>> *)rules;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,101 @@
// 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 "FBAEMAdvertiserMultiEntryRule.h"
#import "FBAEMAdvertiserSingleEntryRule.h"
static NSString *const OPERATOR_KEY = @"operator";
static NSString *const RULES_KEY = @"rules";
@implementation FBAEMAdvertiserMultiEntryRule
- (instancetype)initWithOperator:(FBAEMAdvertiserRuleOperator)op
rules:(NSArray<id<FBAEMAdvertiserRuleMatching>> *)rules
{
if (self = [super init]) {
_operator = op;
_rules = rules;
}
return self;
}
#pragma mark - FBAEMAdvertiserRuleMatching
- (BOOL)isMatchedEventParameters:(nullable NSDictionary<NSString *, id> *)eventParams
{
@try {
BOOL isMatched = _operator == FBAEMAdvertiserRuleOperatorOr ? NO : YES;
for (id<FBAEMAdvertiserRuleMatching> rule in _rules) {
BOOL doesSubruleMatch = [rule isMatchedEventParameters:eventParams];
if (_operator == FBAEMAdvertiserRuleOperatorAnd) {
isMatched = isMatched & doesSubruleMatch;
}
if (_operator == FBAEMAdvertiserRuleOperatorOr) {
isMatched = isMatched | doesSubruleMatch;
}
if (_operator == FBAEMAdvertiserRuleOperatorNot) {
isMatched = isMatched & !doesSubruleMatch;
}
}
return isMatched;
} @catch (NSException *exception) {
#if DEBUG
#if FBTEST
@throw exception;
#endif
#endif
return NO;
}
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
FBAEMAdvertiserRuleOperator op = [decoder decodeIntegerForKey:OPERATOR_KEY];
NSSet *classes = [NSSet setWithArray:@[NSArray.class, FBAEMAdvertiserMultiEntryRule.class, FBAEMAdvertiserSingleEntryRule.class]];
NSArray<id<FBAEMAdvertiserRuleMatching>> *rules = [decoder decodeObjectOfClasses:classes forKey:RULES_KEY];
return [self initWithOperator:op rules:rules];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInteger:_operator forKey:OPERATOR_KEY];
[encoder encodeObject:_rules forKey:RULES_KEY];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
@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>
#import "FBAEMAdvertiserRuleMatching.h"
#import "FBAEMAdvertiserRuleProviding.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AEMAdvertiserRuleFactory)
@interface FBAEMAdvertiserRuleFactory : NSObject<FBAEMAdvertiserRuleProviding>
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,184 @@
// 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 "FBAEMAdvertiserRuleFactory.h"
#import "FBAEMAdvertiserMultiEntryRule.h"
#import "FBAEMAdvertiserSingleEntryRule.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
@implementation FBAEMAdvertiserRuleFactory
- (nullable id<FBAEMAdvertiserRuleMatching>)createRuleWithJson:(nullable NSString *)json
{
@try {
json = [FBSDKTypeUtility stringValueOrNil:json];
if (!json) {
return nil;
}
NSDictionary<NSString *, id> *rule = [FBSDKBasicUtility objectForJSONString:json error:nil];
return [self createRuleWithDict:rule];
} @catch (NSException *exception) {
NSLog(@"Fail to parse Advertiser Rules with JSON");
}
return nil;
}
- (nullable id<FBAEMAdvertiserRuleMatching>)createRuleWithDict:(NSDictionary<NSString *, id> *)dict
{
@try {
dict = [FBSDKTypeUtility dictionaryValue:dict];
if (!dict) {
return nil;
}
FBAEMAdvertiserRuleOperator op = [self getOperator:dict];
if ([self isOperatorForMultiEntryRule:op]) {
return [self createMultiEntryRuleWithDict:dict];
} else {
return [self createSingleEntryRuleWithDict:dict];
}
} @catch (NSException *exception) {
NSLog(@"Fail to parse Advertiser Rules with Dict");
}
return nil;
}
- (nullable FBAEMAdvertiserMultiEntryRule *)createMultiEntryRuleWithDict:(NSDictionary<NSString *, id> *)dict
{
dict = [FBSDKTypeUtility dictionaryValue:dict];
if (!dict) {
return nil;
}
NSString *opString = [self primaryKeyForRule:dict];
FBAEMAdvertiserRuleOperator operator = [self getOperator:dict];
if (![self isOperatorForMultiEntryRule:operator]) {
return nil;
}
NSArray<NSDictionary *> *subrules = [FBSDKTypeUtility dictionary:dict objectForKey:opString ofType:NSArray.class];
NSMutableArray<id<FBAEMAdvertiserRuleMatching>> *rules = [NSMutableArray new];
for (NSDictionary *subrule in subrules) {
id<FBAEMAdvertiserRuleMatching> entryRule = [self createRuleWithDict:subrule];
if (!entryRule) {
return nil;
}
[FBSDKTypeUtility array:rules addObject:entryRule];
}
if (!rules.count) {
return nil;
}
return [[FBAEMAdvertiserMultiEntryRule alloc] initWithOperator:operator rules:rules];
}
- (nullable FBAEMAdvertiserSingleEntryRule *)createSingleEntryRuleWithDict:(NSDictionary<NSString *, id> *)dict
{
dict = [FBSDKTypeUtility dictionaryValue:dict];
if (!dict) {
return nil;
}
NSString *paramKey = [self primaryKeyForRule:dict];
NSDictionary<NSString *, id> *rawRule = [FBSDKTypeUtility dictionary:dict objectForKey:paramKey ofType:NSDictionary.class];
NSString *encodedOperator = [self primaryKeyForRule:rawRule];
FBAEMAdvertiserRuleOperator operator = [self getOperator:rawRule];
NSString *linguisticCondition = nil;
NSNumber *numericalCondition = nil;
NSArray *arrayCondition = nil;
switch (operator) {
case Unknown:
default:
return nil;
case FBAEMAdvertiserRuleOperatorContains:
case FBAEMAdvertiserRuleOperatorNotContains:
case FBAEMAdvertiserRuleOperatorStartsWith:
case FBAEMAdvertiserRuleOperatorI_Contains:
case FBAEMAdvertiserRuleOperatorI_NotContains:
case FBAEMAdvertiserRuleOperatorI_StartsWith:
case FBAEMAdvertiserRuleOperatorRegexMatch:
case FBAEMAdvertiserRuleOperatorEqual:
case FBAEMAdvertiserRuleOperatorNotEqual:
linguisticCondition = [FBSDKTypeUtility dictionary:rawRule objectForKey:encodedOperator ofType:NSString.class]; break;
case FBAEMAdvertiserRuleOperatorLessThan:
case FBAEMAdvertiserRuleOperatorLessThanOrEqual:
case FBAEMAdvertiserRuleOperatorGreaterThan:
case FBAEMAdvertiserRuleOperatorGreaterThanOrEqual:
numericalCondition = [FBSDKTypeUtility dictionary:rawRule objectForKey:encodedOperator ofType:NSNumber.class]; break;
case FBAEMAdvertiserRuleOperatorI_IsAny:
case FBAEMAdvertiserRuleOperatorI_IsNotAny:
case FBAEMAdvertiserRuleOperatorIsAny:
case FBAEMAdvertiserRuleOperatorIsNotAny:
arrayCondition = [FBSDKTypeUtility dictionary:rawRule objectForKey:encodedOperator ofType:NSArray.class]; break;
}
if (linguisticCondition || numericalCondition != nil || arrayCondition.count > 0) {
return [[FBAEMAdvertiserSingleEntryRule alloc] initWithOperator:operator paramKey:paramKey linguisticCondition:linguisticCondition numericalCondition:numericalCondition arrayCondition:arrayCondition];
}
return nil;
}
- (nullable NSString *)primaryKeyForRule:(NSDictionary<NSString *, id> *)rule
{
NSArray<NSString *> *keys = [rule allKeys];
NSString *key = keys.firstObject;
return [FBSDKTypeUtility stringValueOrNil:key];
}
- (FBAEMAdvertiserRuleOperator)getOperator:(NSDictionary<NSString *, id> *)rule
{
NSString *key = [self primaryKeyForRule:rule];
if (!key) {
return Unknown;
}
NSArray<NSString *> *operatorKeys = @[
@"unknown",
@"and",
@"or",
@"not",
@"contains",
@"not_contains",
@"starts_with",
@"i_contains",
@"i_not_contains",
@"i_starts_with",
@"regex_match",
@"eq",
@"neq",
@"lt",
@"lte",
@"gt",
@"gte",
@"i_is_any",
@"i_is_not_any",
@"is_any",
@"is_not_any"
];
NSInteger index = [operatorKeys indexOfObject:key.lowercaseString];
return index == NSNotFound ? Unknown : index;
}
- (BOOL)isOperatorForMultiEntryRule:(FBAEMAdvertiserRuleOperator)operator
{
return operator == FBAEMAdvertiserRuleOperatorAnd
|| operator == FBAEMAdvertiserRuleOperatorOr
|| operator == FBAEMAdvertiserRuleOperatorNot;
}
@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 "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FBAEMAdvertiserRuleMatching <NSObject>
- (BOOL)isMatchedEventParameters:(nullable NSDictionary<NSString *, id> *)eventParams;
@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
typedef NS_CLOSED_ENUM(NSInteger, FBAEMAdvertiserRuleOperator)
{
Unknown = 0,
// Multi Entry Rule Operator
FBAEMAdvertiserRuleOperatorAnd,
FBAEMAdvertiserRuleOperatorOr,
FBAEMAdvertiserRuleOperatorNot,
// Single Entry Rule Operator
FBAEMAdvertiserRuleOperatorContains,
FBAEMAdvertiserRuleOperatorNotContains,
FBAEMAdvertiserRuleOperatorStartsWith,
FBAEMAdvertiserRuleOperatorI_Contains,
FBAEMAdvertiserRuleOperatorI_NotContains,
FBAEMAdvertiserRuleOperatorI_StartsWith,
FBAEMAdvertiserRuleOperatorRegexMatch,
FBAEMAdvertiserRuleOperatorEqual,
FBAEMAdvertiserRuleOperatorNotEqual,
FBAEMAdvertiserRuleOperatorLessThan,
FBAEMAdvertiserRuleOperatorLessThanOrEqual,
FBAEMAdvertiserRuleOperatorGreaterThan,
FBAEMAdvertiserRuleOperatorGreaterThanOrEqual,
FBAEMAdvertiserRuleOperatorI_IsAny,
FBAEMAdvertiserRuleOperatorI_IsNotAny,
FBAEMAdvertiserRuleOperatorIsAny,
FBAEMAdvertiserRuleOperatorIsNotAny
} NS_SWIFT_NAME(AEMAdvertiserRuleOperator);
#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
NS_ASSUME_NONNULL_BEGIN
/// Describes anything that can provide instances of `AEMAdvertiserRuleMatching`
NS_SWIFT_NAME(AEMAdvertiserRuleProviding)
@protocol FBAEMAdvertiserRuleProviding
- (nullable id<FBAEMAdvertiserRuleMatching>)createRuleWithJson:(nullable NSString *)json;
- (nullable id<FBAEMAdvertiserRuleMatching>)createRuleWithDict:(NSDictionary<NSString *, id> *)dict;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,53 @@
// 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 "FBAEMAdvertiserRuleMatching.h"
#import "FBAEMAdvertiserRuleOperator.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AEMAdvertiserSingleEntryRule)
@interface FBAEMAdvertiserSingleEntryRule : NSObject<FBAEMAdvertiserRuleMatching, NSCopying, NSSecureCoding>
@property (nonatomic, readonly, assign) FBAEMAdvertiserRuleOperator operator;
@property (nonatomic, readonly) NSString *paramKey;
@property (nullable, nonatomic, readonly) NSString *linguisticCondition;
@property (nullable, nonatomic, readonly) NSNumber *numericalCondition;
@property (nullable, nonatomic, readonly) NSArray *arrayCondition;
- (instancetype)initWithOperator:(FBAEMAdvertiserRuleOperator)op
paramKey:(NSString *)paramKey
linguisticCondition:(nullable NSString *)linguisticCondition
numericalCondition:(nullable NSNumber *)numericalCondition
arrayCondition:(nullable NSArray *)arrayCondition;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,267 @@
// 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 "FBAEMAdvertiserSingleEntryRule.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
static NSString *const OPERATOR_KEY = @"operator";
static NSString *const PARAMKEY_KEY = @"param_key";
static NSString *const STRING_VALUE_KEY = @"string_value";
static NSString *const NUMBER_VALUE_KEY = @"number_value";
static NSString *const ARRAY_VALUE_KEY = @"array_value";
static NSString *const PARAM_DELIMETER = @".";
static NSString *const ASTERISK_DELIMETER = @"[*]";
@implementation FBAEMAdvertiserSingleEntryRule
- (instancetype)initWithOperator:(FBAEMAdvertiserRuleOperator)op
paramKey:(NSString *)paramKey
linguisticCondition:(nullable NSString *)linguisticCondition
numericalCondition:(nullable NSNumber *)numericalCondition
arrayCondition:(nullable NSArray *)arrayCondition
{
if ((self = [super init])) {
_operator = op;
_paramKey = paramKey;
_linguisticCondition = linguisticCondition;
_numericalCondition = numericalCondition;
_arrayCondition = arrayCondition;
}
return self;
}
#pragma mark - FBAEMAdvertiserRuleMatching
- (BOOL)isMatchedEventParameters:(nullable NSDictionary<NSString *, id> *)eventParams
{
@try {
NSArray<NSString *> *paramPath = [_paramKey componentsSeparatedByString:PARAM_DELIMETER];
return [self isMatchedEventParameters:eventParams paramPath:paramPath];
} @catch (NSException *exception) {
#if DEBUG
#if FBTEST
@throw exception;
#endif
#endif
return NO;
}
}
- (BOOL)isMatchedEventParameters:(nullable NSDictionary<NSString *, id> *)eventParams
paramPath:(NSArray<NSString *> *)paramPath
{
eventParams = [FBSDKTypeUtility dictionaryValue:eventParams];
if (!eventParams || !paramPath.count) {
return NO;
}
NSString *param = [FBSDKTypeUtility stringValueOrNil:paramPath.firstObject];
if ([param hasSuffix:ASTERISK_DELIMETER]) {
return [self isMatchedWithAsteriskParam:param eventParameters:eventParams paramPath:paramPath];
}
// if data does not contain the key, we should return false directly.
if (!param || ![[eventParams allKeys] containsObject:param]) {
return NO;
}
// Apply operator rule if the last param is reached
if (paramPath.count == 1) {
NSString *stringValue = nil;
NSNumber *numericalValue = nil;
switch (_operator) {
case FBAEMAdvertiserRuleOperatorContains:
case FBAEMAdvertiserRuleOperatorNotContains:
case FBAEMAdvertiserRuleOperatorStartsWith:
case FBAEMAdvertiserRuleOperatorI_Contains:
case FBAEMAdvertiserRuleOperatorI_NotContains:
case FBAEMAdvertiserRuleOperatorI_StartsWith:
case FBAEMAdvertiserRuleOperatorRegexMatch:
case FBAEMAdvertiserRuleOperatorEqual:
case FBAEMAdvertiserRuleOperatorNotEqual:
case FBAEMAdvertiserRuleOperatorI_IsAny:
case FBAEMAdvertiserRuleOperatorI_IsNotAny:
case FBAEMAdvertiserRuleOperatorIsAny:
case FBAEMAdvertiserRuleOperatorIsNotAny:
stringValue = [FBSDKTypeUtility dictionary:eventParams objectForKey:param ofType:NSString.class]; break;
case FBAEMAdvertiserRuleOperatorLessThan:
case FBAEMAdvertiserRuleOperatorLessThanOrEqual:
case FBAEMAdvertiserRuleOperatorGreaterThan:
case FBAEMAdvertiserRuleOperatorGreaterThanOrEqual:
numericalValue = [FBSDKTypeUtility dictionary:eventParams objectForKey:param ofType:NSNumber.class]; break;
default: break;
}
return [self isMatchedWithStringValue:stringValue numericalValue:numericalValue];
}
NSDictionary<NSString *, id> *subParams = [FBSDKTypeUtility dictionary:eventParams objectForKey:param ofType:NSDictionary.class];
NSRange range = NSMakeRange(1, paramPath.count - 1);
NSArray *subParamPath = [paramPath subarrayWithRange:range];
return [self isMatchedEventParameters:subParams paramPath:subParamPath];
}
- (BOOL)isMatchedWithAsteriskParam:(NSString *)param
eventParameters:(NSDictionary<NSString *, id> *)eventParams
paramPath:(NSArray<NSString *> *)paramPath
{
param = [param substringToIndex:param.length - ASTERISK_DELIMETER.length];
NSArray<NSDictionary *> *items = [FBSDKTypeUtility dictionary:eventParams objectForKey:param ofType:NSArray.class];
if (!items.count || paramPath.count < 2) {
return NO;
}
BOOL isMatched = NO;
NSRange range = NSMakeRange(1, paramPath.count - 1);
NSArray *subParamPath = [paramPath subarrayWithRange:range];
for (NSDictionary *item in items) {
isMatched |= [self isMatchedEventParameters:item paramPath:subParamPath];
if (isMatched) {
break;
}
}
return isMatched;
}
- (BOOL)isMatchedWithStringValue:(nullable NSString *)stringValue
numericalValue:(nullable NSNumber *)numericalValue
{
BOOL isMatched = NO;
switch (_operator) {
case FBAEMAdvertiserRuleOperatorContains:
isMatched = stringValue && [stringValue containsString:_linguisticCondition]; break;
case FBAEMAdvertiserRuleOperatorNotContains:
isMatched = !(stringValue && [stringValue containsString:_linguisticCondition]); break;
case FBAEMAdvertiserRuleOperatorStartsWith:
isMatched = stringValue && [stringValue hasPrefix:_linguisticCondition]; break;
case FBAEMAdvertiserRuleOperatorI_Contains:
isMatched = stringValue && [stringValue.lowercaseString containsString:_linguisticCondition.lowercaseString]; break;
case FBAEMAdvertiserRuleOperatorI_NotContains:
isMatched = !(stringValue && [stringValue.lowercaseString containsString:_linguisticCondition.lowercaseString]); break;
case FBAEMAdvertiserRuleOperatorI_StartsWith:
isMatched = stringValue && [stringValue.lowercaseString hasPrefix:_linguisticCondition.lowercaseString]; break;
case FBAEMAdvertiserRuleOperatorRegexMatch:
isMatched = stringValue && [self isRegexMatch:stringValue]; break;
case FBAEMAdvertiserRuleOperatorEqual:
isMatched = stringValue && [stringValue isEqualToString:_linguisticCondition]; break;
case FBAEMAdvertiserRuleOperatorNotEqual:
isMatched = !(stringValue && [stringValue isEqualToString:_linguisticCondition]); break;
case FBAEMAdvertiserRuleOperatorI_IsAny:
isMatched = stringValue && [self isAnyOf:_arrayCondition stringValue:stringValue ignoreCase:YES]; break;
case FBAEMAdvertiserRuleOperatorI_IsNotAny:
isMatched = !(stringValue && [self isAnyOf:_arrayCondition stringValue:stringValue ignoreCase:YES]); break;
case FBAEMAdvertiserRuleOperatorIsAny:
isMatched = stringValue && [self isAnyOf:_arrayCondition stringValue:stringValue ignoreCase:NO]; break;
case FBAEMAdvertiserRuleOperatorIsNotAny:
isMatched = !(stringValue && [self isAnyOf:_arrayCondition stringValue:stringValue ignoreCase:NO]); break;
case FBAEMAdvertiserRuleOperatorLessThan:
isMatched = (numericalValue != nil) && ([numericalValue compare:_numericalCondition] == NSOrderedAscending); break;
case FBAEMAdvertiserRuleOperatorLessThanOrEqual:
isMatched = (numericalValue != nil) && ([numericalValue compare:_numericalCondition] != NSOrderedDescending); break;
case FBAEMAdvertiserRuleOperatorGreaterThan:
isMatched = (numericalValue != nil) && ([numericalValue compare:_numericalCondition] == NSOrderedDescending); break;
case FBAEMAdvertiserRuleOperatorGreaterThanOrEqual:
isMatched = (numericalValue != nil) && ([numericalValue compare:_numericalCondition] != NSOrderedAscending); break;
default: break;
}
return isMatched;
}
- (BOOL)isRegexMatch:(NSString *)stringValue
{
if (!_linguisticCondition.length) {
return NO;
}
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:_linguisticCondition options:0 error:&error];
if (!regex || error) {
return NO;
}
NSRange searchedRange = NSMakeRange(0, stringValue.length);
NSArray *matches = [regex matchesInString:stringValue options:0 range:searchedRange];
return matches.count > 0;
}
- (BOOL)isAnyOf:(NSArray<NSString *> *)arrayCondition
stringValue:(NSString *)stringValue
ignoreCase:(BOOL)ignoreCase
{
NSMutableSet<NSString *> *set = [NSMutableSet new];
for (NSString *item in arrayCondition) {
if (ignoreCase) {
[set addObject:item.lowercaseString];
} else {
[set addObject:item];
}
}
if (ignoreCase) {
stringValue = stringValue.lowercaseString;
}
return [set containsObject:stringValue];
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
FBAEMAdvertiserRuleOperator op = [decoder decodeIntegerForKey:OPERATOR_KEY];
NSString *paramKey = [decoder decodeObjectOfClass:NSString.class forKey:PARAMKEY_KEY];
NSString *linguisticCondition = [decoder decodeObjectOfClass:NSString.class forKey:STRING_VALUE_KEY];
NSNumber *numericalCondition = [decoder decodeObjectOfClass:NSNumber.class forKey:NUMBER_VALUE_KEY];
NSArray *arrayCondition = [decoder decodeObjectOfClass:NSArray.class forKey:ARRAY_VALUE_KEY];
return [self initWithOperator:op
paramKey:paramKey
linguisticCondition:linguisticCondition
numericalCondition:numericalCondition
arrayCondition:arrayCondition];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInteger:_operator forKey:OPERATOR_KEY];
[encoder encodeObject:_paramKey forKey:PARAMKEY_KEY];
[encoder encodeObject:_linguisticCondition forKey:STRING_VALUE_KEY];
[encoder encodeObject:_numericalCondition forKey:NUMBER_VALUE_KEY];
[encoder encodeObject:_arrayCondition forKey:ARRAY_VALUE_KEY];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
#if DEBUG
#if FBTEST
- (void)setOperator:(FBAEMAdvertiserRuleOperator)operator
{
_operator = operator;
}
#endif
#endif
@end
#endif
+66
View File
@@ -0,0 +1,66 @@
// 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 "FBAEMRule.h"
#import "FBAEMAdvertiserRuleMatching.h"
#import "FBAEMAdvertiserRuleProviding.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AEMConfiguration)
@interface FBAEMConfiguration : NSObject <NSCopying, NSSecureCoding>
@property (nonatomic, readonly, assign) NSInteger cutoffTime;
/** The UNIX timestamp of config's valid date and works as a unqiue identifier of the config */
@property (nonatomic, readonly, assign) NSInteger validFrom;
@property (nonatomic, readonly, copy) NSString *defaultCurrency;
@property (nonatomic, readonly, copy) NSString *configMode;
@property (nullable, nonatomic, readonly, copy) NSString *businessID;
@property (nullable, nonatomic, readonly, copy) id<FBAEMAdvertiserRuleMatching> matchingRule;
@property (nonatomic, readonly) NSArray<FBAEMRule *> *conversionValueRules;
@property (nonatomic, readonly) NSSet<NSString *> *eventSet;
@property (nonatomic, readonly) NSSet<NSString *> *currencySet;
+ (void)configureWithRuleProvider:(id<FBAEMAdvertiserRuleProviding>)ruleProvider;
- (nullable instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict;
- (BOOL)isSameValidFrom:(NSInteger)validFrom
businessID:(nullable NSString *)businessID;
- (BOOL)isSameBusinessID:(nullable NSString *)businessID;
@end
NS_ASSUME_NONNULL_END
#endif
+231
View File
@@ -0,0 +1,231 @@
// 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 "FBAEMConfiguration.h"
#import "FBAEMAdvertiserMultiEntryRule.h"
#import "FBAEMAdvertiserSingleEntryRule.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
static NSString *const DEFAULT_CURRENCY_KEY = @"default_currency";
static NSString *const CUTOFF_TIME_KEY = @"cutoff_time";
static NSString *const CONVERSION_RULES_KEY = @"conversion_value_rules";
static NSString *const VALID_FROM_KEY = @"valid_from";
static NSString *const CONFIG_MODE_KEY = @"config_mode";
static NSString *const CONFIG_BUSINESS_ID_KEY = @"advertiser_id";
static NSString *const BUSINESS_ID_KEY = @"business_id";
static NSString *const PARAM_RULE_KEY = @"param_rule";
static id<FBAEMAdvertiserRuleProviding> _ruleProvider;
@implementation FBAEMConfiguration
+ (void)configureWithRuleProvider:(id<FBAEMAdvertiserRuleProviding>)ruleProvider
{
if (self == [FBAEMConfiguration class]) {
_ruleProvider = ruleProvider;
}
}
+ (id<FBAEMAdvertiserRuleProviding>)ruleProvider
{
return _ruleProvider;
}
- (nullable instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict
{
if ((self = [super init])) {
@try {
dict = [FBSDKTypeUtility dictionaryValue:dict];
if (!dict) {
return nil;
}
NSString *defaultCurrency = [FBSDKTypeUtility dictionary:dict objectForKey:DEFAULT_CURRENCY_KEY ofType:NSString.class];
NSNumber *cutoffTime = [FBSDKTypeUtility dictionary:dict objectForKey:CUTOFF_TIME_KEY ofType:NSNumber.class];
NSNumber *validFrom = [FBSDKTypeUtility dictionary:dict objectForKey:VALID_FROM_KEY ofType:NSNumber.class];
NSString *configMode = [FBSDKTypeUtility dictionary:dict objectForKey:CONFIG_MODE_KEY ofType:NSString.class];
NSString *businessID = [FBSDKTypeUtility dictionary:dict objectForKey:CONFIG_BUSINESS_ID_KEY ofType:NSString.class];
NSString *paramRuleJson = [FBSDKTypeUtility dictionary:dict objectForKey:PARAM_RULE_KEY ofType:NSString.class];
id<FBAEMAdvertiserRuleMatching> matchingRule = [FBAEMConfiguration.ruleProvider createRuleWithJson:paramRuleJson];
NSArray<FBAEMRule *> *rules = [FBAEMConfiguration parseRules:[FBSDKTypeUtility dictionary:dict objectForKey:CONVERSION_RULES_KEY ofType:NSArray.class]];
if (!defaultCurrency || cutoffTime == nil || validFrom == nil || !configMode || 0 == rules.count) {
return nil;
}
// Advertiser Config must have param rule
if (businessID && !matchingRule) {
return nil;
}
_defaultCurrency = defaultCurrency;
_cutoffTime = cutoffTime.integerValue;
_validFrom = validFrom.integerValue;
_configMode = configMode;
_businessID = businessID;
_matchingRule = matchingRule;
_conversionValueRules = rules;
_eventSet = [FBAEMConfiguration getEventSetFromRules:_conversionValueRules];
_currencySet = [FBAEMConfiguration getCurrencySetFromRules:_conversionValueRules];
} @catch (NSException *exception) {
return nil;
}
}
return self;
}
- (instancetype)initWithDefaultCurrency:(NSString *)defaultCurrency
cutoffTime:(NSInteger)cutoffTime
validFrom:(NSInteger)validFrom
configMode:(NSString *)configMode
businessID:(nullable NSString *)businessID
matchingRule:(id<FBAEMAdvertiserRuleMatching>)matchingRule
conversionValueRules:(NSArray<FBAEMRule *> *)conversionValueRules
{
if ((self = [super init])) {
_defaultCurrency = defaultCurrency;
_cutoffTime = cutoffTime;
_validFrom = validFrom;
_configMode = configMode;
_businessID = businessID;
_matchingRule = matchingRule;
_conversionValueRules = conversionValueRules;
_eventSet = [FBAEMConfiguration getEventSetFromRules:_conversionValueRules];
_currencySet = [FBAEMConfiguration getCurrencySetFromRules:_conversionValueRules];
}
return self;
}
+ (nullable NSArray<FBAEMRule *> *)parseRules:(nullable NSArray<NSDictionary<NSString *, id> *> *)rules
{
if (0 == rules.count) {
return nil;
}
NSMutableArray<FBAEMRule *> *parsedRules = [NSMutableArray new];
for (NSDictionary<NSString *, id> *ruleEntry in rules) {
FBAEMRule *rule = [[FBAEMRule alloc] initWithJSON:ruleEntry];
if (!rule) {
return nil;
}
[FBSDKTypeUtility array:parsedRules addObject:rule];
}
// Sort the rules in descending priority order
[parsedRules sortUsingComparator:^NSComparisonResult (FBAEMRule *obj1, FBAEMRule *obj2) {
if (obj1.priority < obj2.priority) {
return NSOrderedDescending;
}
if (obj1.priority > obj2.priority) {
return NSOrderedAscending;
}
return NSOrderedSame;
}];
return [parsedRules copy];
}
+ (NSSet<NSString *> *)getEventSetFromRules:(NSArray<FBAEMRule *> *)rules
{
NSMutableSet<NSString *> *eventSet = [NSMutableSet new];
for (FBAEMRule *rule in rules) {
if (!rule) {
continue;
}
for (FBAEMEvent *event in rule.events) {
if (event.eventName) {
[eventSet addObject:event.eventName];
}
}
}
return [eventSet copy];
}
+ (NSSet<NSString *> *)getCurrencySetFromRules:(NSArray<FBAEMRule *> *)rules
{
NSMutableSet<NSString *> *currencySet = [NSMutableSet new];
for (FBAEMRule *rule in rules) {
if (!rule) {
continue;
}
for (FBAEMEvent *event in rule.events) {
for (NSString *currency in event.values) {
[currencySet addObject:[currency uppercaseString]];
}
}
}
return [currencySet copy];
}
- (BOOL)isSameValidFrom:(NSInteger)validFrom
businessID:(nullable NSString *)businessID
{
return (_validFrom == validFrom) && [self isSameBusinessID:businessID];
}
- (BOOL)isSameBusinessID:(nullable NSString *)businessID
{
return (_businessID && [_businessID isEqualToString:businessID])
|| (!_businessID && !businessID);
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
NSString *defaultCurrency = [decoder decodeObjectOfClass:NSString.class forKey:DEFAULT_CURRENCY_KEY];
NSInteger cutoffTime = [decoder decodeIntegerForKey:CUTOFF_TIME_KEY];
NSInteger validFrom = [decoder decodeIntegerForKey:VALID_FROM_KEY];
NSString *configMode = [decoder decodeObjectOfClass:NSString.class forKey:CONFIG_MODE_KEY];
NSString *businessID = [decoder decodeObjectOfClass:NSString.class forKey:BUSINESS_ID_KEY];
NSSet *matchingRuleClasses = [NSSet setWithArray:@[NSArray.class, FBAEMAdvertiserMultiEntryRule.class, FBAEMAdvertiserSingleEntryRule.class]];
id<FBAEMAdvertiserRuleMatching> matchingRule = [decoder decodeObjectOfClasses:matchingRuleClasses forKey:PARAM_RULE_KEY];
NSArray<FBAEMRule *> *rules = [decoder decodeObjectOfClasses:[NSSet setWithArray:@[NSArray.class, FBAEMRule.class, FBAEMEvent.class]] forKey:CONVERSION_RULES_KEY];
return [self initWithDefaultCurrency:defaultCurrency
cutoffTime:cutoffTime
validFrom:validFrom
configMode:configMode
businessID:businessID
matchingRule:matchingRule
conversionValueRules:rules];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:_defaultCurrency forKey:DEFAULT_CURRENCY_KEY];
[encoder encodeInteger:_cutoffTime forKey:CUTOFF_TIME_KEY];
[encoder encodeInteger:_validFrom forKey:VALID_FROM_KEY];
[encoder encodeObject:_configMode forKey:CONFIG_MODE_KEY];
[encoder encodeObject:_businessID forKey:BUSINESS_ID_KEY];
[encoder encodeObject:_matchingRule forKey:PARAM_RULE_KEY];
[encoder encodeObject:_conversionValueRules forKey:CONVERSION_RULES_KEY];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
@end
#endif
+39
View File
@@ -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>
NS_ASSUME_NONNULL_BEGIN
@interface FBAEMEvent : NSObject <NSCopying, NSSecureCoding>
@property (nonatomic, readonly, copy) NSString *eventName;
@property (nullable, nonatomic, readonly, copy) NSDictionary<NSString *, NSNumber *> *values;
- (nullable instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict;
@end
NS_ASSUME_NONNULL_END
#endif
+106
View File
@@ -0,0 +1,106 @@
// 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 "FBAEMEvent.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
static NSString *const EVENT_NAME_KEY = @"event_name";
static NSString *const VALUES_KEY = @"values";
static NSString *const CURRENCY_KEY = @"currency";
static NSString *const AMOUNT_KEY = @"amount";
@implementation FBAEMEvent
- (nullable instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict
{
if ((self = [super init])) {
dict = [FBSDKTypeUtility dictionaryValue:dict];
if (!dict) {
return nil;
}
_eventName = [FBSDKTypeUtility dictionary:dict objectForKey:EVENT_NAME_KEY ofType:NSString.class];
// Event name is a required field
if (!_eventName) {
return nil;
}
// Values is an optional field
NSArray<NSDictionary<NSString *, id> *> *valueEntries = [FBSDKTypeUtility dictionary:dict objectForKey:VALUES_KEY ofType:NSArray.class];
if (valueEntries.count > 0) {
NSMutableDictionary<NSString *, NSNumber *> *valueDict = [NSMutableDictionary new];
for (NSDictionary<NSString *, id> *valueEntry in valueEntries) {
NSDictionary<NSString *, id> *value = [FBSDKTypeUtility dictionaryValue:valueEntry];
NSString *currency = [FBSDKTypeUtility dictionary:value objectForKey:CURRENCY_KEY ofType:NSString.class];
NSNumber *amount = [FBSDKTypeUtility dictionary:value objectForKey:AMOUNT_KEY ofType:NSNumber.class];
if (!currency || amount == nil) {
return nil;
}
[FBSDKTypeUtility dictionary:valueDict setObject:amount forKey:[currency uppercaseString]];
}
_values = [valueDict copy];
}
}
return self;
}
- (instancetype)initWithEventName:(NSString *)eventName
values:(NSDictionary<NSString *, NSNumber *> *)values
{
if ((self = [super init])) {
_eventName = eventName;
_values = values;
}
return self;
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
NSString *eventName = [decoder decodeObjectOfClass:NSString.class forKey:EVENT_NAME_KEY];
NSDictionary<NSString *, NSNumber *> *values = [decoder decodeObjectOfClass:NSDictionary.class forKey:VALUES_KEY];
return [self initWithEventName:eventName values:values];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:_eventName forKey:EVENT_NAME_KEY];
if (_values) {
[encoder encodeObject:_values forKey:VALUES_KEY];
}
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
@end
#endif
+83
View File
@@ -0,0 +1,83 @@
// 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 "FBAEMConfiguration.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AEMInvocation)
@interface FBAEMInvocation : NSObject <NSCopying, NSSecureCoding>
@property (nonatomic, readonly, copy) NSString *campaignID;
@property (nonatomic, readonly, copy) NSString *ACSToken;
@property (nullable, nonatomic, readonly, copy) NSString *ACSSharedSecret;
@property (nullable, nonatomic, readonly, copy) NSString *ACSConfigID;
@property (nullable, nonatomic, readonly, copy) NSString *businessID;
@property (nonatomic, readonly, assign) BOOL isTestMode;
@property (nonatomic, readonly, assign) BOOL hasSKAN;
@property (nonatomic, readonly, copy) NSDate *timestamp;
@property (nonatomic, readonly, copy) NSString *configMode;
/** The unique identifier of the config, it's the same as config's validFrom */
@property (nonatomic, readonly, assign) NSInteger configID;
@property (nonatomic, readonly) NSMutableSet<NSString *> *recordedEvents;
@property (nonatomic, readonly) NSMutableDictionary<NSString *, NSMutableDictionary *> *recordedValues;
@property (nonatomic, readonly, assign) NSInteger conversionValue;
@property (nonatomic, readonly, assign) NSInteger priority;
@property (nullable, nonatomic, readonly) NSDate *conversionTimestamp;
@property (nonatomic, assign) BOOL isAggregated;
+ (nullable instancetype)invocationWithAppLinkData:(nullable NSDictionary<id, id> *)applinkData;
- (BOOL)attributeEvent:(NSString *)event
currency:(nullable NSString *)currency
value:(nullable NSNumber *)value
parameters:(nullable NSDictionary *)parameters
configs:(nullable NSDictionary<NSString *, NSArray<FBAEMConfiguration *> *> *)configs;
- (BOOL)updateConversionValueWithConfigs:(nullable NSDictionary<NSString *, NSArray<FBAEMConfiguration *> *> *)configs;
- (BOOL)isOutOfWindowWithConfigs:(nullable NSDictionary<NSString *, NSArray<FBAEMConfiguration *> *> *)configs;
- (nullable NSString *)getHMAC:(NSInteger)delay;
@end
NS_ASSUME_NONNULL_END
#endif
+477
View File
@@ -0,0 +1,477 @@
// 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 "FBAEMInvocation.h"
#import <CommonCrypto/CommonHMAC.h>
#import "FBCoreKitBasicsImportForAEMKit.h"
#define SEC_IN_DAY 86400
static NSString *const CAMPAIGN_ID_KEY = @"campaign_ids";
static NSString *const ACS_TOKEN_KEY = @"acs_token";
static NSString *const ACS_SHARED_SECRET_KEY = @"shared_secret";
static NSString *const ACS_CONFIG_ID_KEY = @"acs_config_id";
static NSString *const BUSINESS_ID_KEY = @"advertiser_id";
static NSString *const TEST_DEEPLINK_KEY = @"test_deeplink";
static NSString *const TIMESTAMP_KEY = @"timestamp";
static NSString *const CONFIG_MODE_KEY = @"config_mode";
static NSString *const CONFIG_ID_KEY = @"config_id";
static NSString *const RECORDED_EVENTS_KEY = @"recorded_events";
static NSString *const RECORDED_VALUES_KEY = @"recorded_values";
static NSString *const CONVERSION_VALUE_KEY = @"conversion_value";
static NSString *const PRIORITY_KEY = @"priority";
static NSString *const CONVERSION_TIMESTAMP_KEY = @"conversion_timestamp";
static NSString *const IS_AGGREGATED_KEY = @"is_aggregated";
static NSString *const HAS_SKAN_KEY = @"has_skan";
static NSString *const FB_CONTENT = @"fb_content";
typedef NSString *const FBAEMInvocationConfigMode;
FBAEMInvocationConfigMode FBAEMInvocationConfigDefaultMode = @"DEFAULT";
FBAEMInvocationConfigMode FBAEMInvocationConfigBrandMode = @"BRAND";
@implementation FBAEMInvocation
+ (nullable instancetype)invocationWithAppLinkData:(nullable NSDictionary<id, id> *)applinkData
{
@try {
applinkData = [FBSDKTypeUtility dictionaryValue:applinkData];
if (!applinkData) {
return nil;
}
NSString *campaignID = [FBSDKTypeUtility dictionary:applinkData objectForKey:CAMPAIGN_ID_KEY ofType:NSString.class];
NSString *ACSToken = [FBSDKTypeUtility dictionary:applinkData objectForKey:ACS_TOKEN_KEY ofType:NSString.class];
NSString *ACSSharedSecret = [FBSDKTypeUtility dictionary:applinkData objectForKey:ACS_SHARED_SECRET_KEY ofType:NSString.class];
NSString *ACSConfigID = [FBSDKTypeUtility dictionary:applinkData objectForKey:CONFIG_ID_KEY ofType:NSString.class];
NSString *businessID = [FBSDKTypeUtility dictionary:applinkData objectForKey:BUSINESS_ID_KEY ofType:NSString.class];
NSNumber *isTestMode = [FBSDKTypeUtility dictionary:applinkData objectForKey:TEST_DEEPLINK_KEY ofType:NSNumber.class] ?: @NO;
NSNumber *hasSKAN = [FBSDKTypeUtility dictionary:applinkData objectForKey:HAS_SKAN_KEY ofType:NSNumber.class] ?: @NO;
if (campaignID == nil || ACSToken == nil) {
return nil;
}
return [[FBAEMInvocation alloc] initWithCampaignID:campaignID
ACSToken:ACSToken
ACSSharedSecret:ACSSharedSecret
ACSConfigID:ACSConfigID
businessID:businessID
isTestMode:isTestMode.boolValue
hasSKAN:hasSKAN.boolValue];
} @catch (NSException *exception) {
return nil;
}
}
- (nullable instancetype)initWithCampaignID:(NSString *)campaignID
ACSToken:(NSString *)ACSToken
ACSSharedSecret:(nullable NSString *)ACSSharedSecret
ACSConfigID:(nullable NSString *)ACSConfigID
businessID:(nullable NSString *)businessID
isTestMode:(BOOL)isTestMode
hasSKAN:(BOOL)hasSKAN
{
return [self initWithCampaignID:campaignID
ACSToken:ACSToken
ACSSharedSecret:ACSSharedSecret
ACSConfigID:ACSConfigID
businessID:businessID
timestamp:nil
configMode:@"DEFAULT"
configID:-1
recordedEvents:nil
recordedValues:nil
conversionValue:-1
priority:-1
conversionTimestamp:nil
isAggregated:YES
isTestMode:isTestMode
hasSKAN:hasSKAN];
}
- (nullable instancetype)initWithCampaignID:(NSString *)campaignID
ACSToken:(NSString *)ACSToken
ACSSharedSecret:(nullable NSString *)ACSSharedSecret
ACSConfigID:(nullable NSString *)ACSConfigID
businessID:(nullable NSString *)businessID
timestamp:(nullable NSDate *)timestamp
configMode:(NSString *)configMode
configID:(NSInteger)configID
recordedEvents:(nullable NSMutableSet<NSString *> *)recordedEvents
recordedValues:(nullable NSMutableDictionary<NSString *, NSMutableDictionary *> *)recordedValues
conversionValue:(NSInteger)conversionValue
priority:(NSInteger)priority
conversionTimestamp:(nullable NSDate *)conversionTimestamp
isAggregated:(BOOL)isAggregated
isTestMode:(BOOL)isTestMode
hasSKAN:(BOOL)hasSKAN
{
if ((self = [super init])) {
_campaignID = campaignID;
_ACSToken = ACSToken;
_ACSSharedSecret = ACSSharedSecret;
_ACSConfigID = ACSConfigID;
_businessID = businessID;
if ([timestamp isKindOfClass:NSDate.class]) {
_timestamp = timestamp;
} else {
_timestamp = [NSDate date];
}
_configMode = configMode;
_configID = configID;
if ([recordedEvents isKindOfClass:NSMutableSet.class]) {
_recordedEvents = recordedEvents;
} else {
_recordedEvents = [NSMutableSet new];
}
if ([recordedValues isKindOfClass:NSMutableDictionary.class]) {
_recordedValues = recordedValues;
} else {
_recordedValues = [NSMutableDictionary new];
}
_conversionValue = conversionValue;
_priority = priority;
_conversionTimestamp = conversionTimestamp;
_isAggregated = isAggregated;
_isTestMode = isTestMode;
_hasSKAN = hasSKAN;
}
return self;
}
- (BOOL)attributeEvent:(NSString *)event
currency:(nullable NSString *)currency
value:(nullable NSNumber *)value
parameters:(nullable NSDictionary *)parameters
configs:(nullable NSDictionary<NSString *, NSArray<FBAEMConfiguration *> *> *)configs
{
FBAEMConfiguration *config = [self _findConfig:configs];
if ([self _isOutOfWindowWithConfig:config] || ![config.eventSet containsObject:event]) {
return NO;
}
// Check advertiser rule matching
if (config.matchingRule && ![config.matchingRule isMatchedEventParameters:[self processedParameters:parameters]]) {
return NO;
}
BOOL isAttributed = NO;
if (![_recordedEvents containsObject:event]) {
[_recordedEvents addObject:event];
isAttributed = YES;
}
// Change currency to default currency if currency is not found in currencySet
NSString *valueCurrency = [currency uppercaseString];
if (![config.currencySet containsObject:valueCurrency]) {
valueCurrency = config.defaultCurrency;
}
if (value != nil) {
NSMutableDictionary *mapping = [[FBSDKTypeUtility dictionary:_recordedValues objectForKey:event ofType:NSDictionary.class] mutableCopy] ?: [NSMutableDictionary new];
NSNumber *valueInMapping = [FBSDKTypeUtility dictionary:mapping objectForKey:valueCurrency ofType:NSNumber.class] ?: [NSNumber numberWithDouble:0];
// Overwrite values when the incoming event's value is greater than the cached one
if (value.doubleValue > valueInMapping.doubleValue) {
[FBSDKTypeUtility dictionary:mapping setObject:[NSNumber numberWithDouble:value.doubleValue] forKey:valueCurrency];
[FBSDKTypeUtility dictionary:_recordedValues setObject:mapping forKey:event];
isAttributed = YES;
}
}
return isAttributed;
}
- (BOOL)updateConversionValueWithConfigs:(nullable NSDictionary<NSString *, NSArray<FBAEMConfiguration *> *> *)configs
{
FBAEMConfiguration *config = [self _findConfig:configs];
if (!config) {
return NO;
}
// Update conversion value if a rule is matched
for (FBAEMRule *rule in config.conversionValueRules) {
if (rule.priority <= _priority) {
break;
}
if ([rule isMatchedWithRecordedEvents:_recordedEvents recordedValues:_recordedValues]) {
_conversionValue = rule.conversionValue;
_priority = rule.priority;
_conversionTimestamp = [NSDate date];
_isAggregated = NO;
return YES;
}
}
return NO;
}
- (BOOL)isOutOfWindowWithConfigs:(nullable NSDictionary<NSString *, NSArray<FBAEMConfiguration *> *> *)configs
{
FBAEMConfiguration *config = [self _findConfig:configs];
return [self _isOutOfWindowWithConfig:config];
}
- (nullable NSString *)getHMAC:(NSInteger)delay
{
if (!_ACSSharedSecret || !_ACSConfigID) {
return nil;
}
@try {
NSData *secretData = [self decodeBase64UrlSafeString:_ACSSharedSecret];
if (!secretData) {
return nil;
}
NSMutableData *hmac = [NSMutableData dataWithLength:CC_SHA512_DIGEST_LENGTH];
NSString *text = [NSString stringWithFormat:@"%@|%@|%@|%@", _campaignID, @(_conversionValue), @(delay), @"server"];
NSData *clearTextData = [text dataUsingEncoding:NSUTF8StringEncoding];
CCHmac(kCCHmacAlgSHA512, [secretData bytes], [secretData length], [clearTextData bytes], [clearTextData length], hmac.mutableBytes);
NSString *base64UrlSafeString = [hmac base64EncodedStringWithOptions:0];
base64UrlSafeString = [base64UrlSafeString stringByReplacingOccurrencesOfString:@"/"
withString:@"_"];
base64UrlSafeString = [base64UrlSafeString stringByReplacingOccurrencesOfString:@"+"
withString:@"-"];
base64UrlSafeString = [base64UrlSafeString stringByReplacingOccurrencesOfString:@"="
withString:@""];
return base64UrlSafeString;
} @catch (NSException *exception) {
return nil;
}
}
- (nullable NSData *)decodeBase64UrlSafeString:(NSString *)base64UrlSafeString
{
if (!base64UrlSafeString.length) {
return nil;
}
NSString *base64String = [base64UrlSafeString stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
base64String = [base64String stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
base64String = [base64String stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
NSString *padding = [@"" stringByPaddingToLength:(4 - base64String.length % 4) withString:@"=" startingAtIndex:0];
base64String = [base64String stringByAppendingString:padding];
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
return decodedData;
}
- (nullable NSDictionary<NSString *, id> *)processedParameters:(nullable NSDictionary<NSString *, id> *)parameters
{
if (!parameters) {
return parameters;
}
@try {
NSMutableDictionary<NSString *, id> *result = [NSMutableDictionary dictionaryWithDictionary:parameters];
NSString *content = [FBSDKTypeUtility dictionary:result objectForKey:FB_CONTENT ofType:NSString.class];
if (content) {
[FBSDKTypeUtility dictionary:result
setObject:[FBSDKTypeUtility JSONObjectWithData:[content dataUsingEncoding:NSUTF8StringEncoding]
options:0
error:nil]
forKey:FB_CONTENT];
}
return [result copy];
} @catch (NSException *exception) {
return parameters;
}
}
- (BOOL)_isOutOfWindowWithConfig:(nullable FBAEMConfiguration *)config
{
if (!config) {
return true;
}
BOOL isCutoff = [[NSDate date] timeIntervalSinceDate:_timestamp] > config.cutoffTime * SEC_IN_DAY;
BOOL isOverLastConversionWindow = _conversionTimestamp && [[NSDate date] timeIntervalSinceDate:_conversionTimestamp] > SEC_IN_DAY;
return isCutoff || isOverLastConversionWindow;
}
- (nullable FBAEMConfiguration *)_findConfig:(nullable NSDictionary<NSString *, NSArray<FBAEMConfiguration *> *> *)configs
{
NSString *configMode = _businessID ? FBAEMInvocationConfigBrandMode : FBAEMInvocationConfigDefaultMode;
NSArray<FBAEMConfiguration *> *configList = [FBSDKTypeUtility dictionary:configs objectForKey:configMode ofType:NSArray.class];
if (0 == configList.count) {
return nil;
}
if (_configID > 0) {
for (FBAEMConfiguration *config in configList) {
if ([config isSameValidFrom:_configID businessID:_businessID]) {
return config;
}
}
return nil;
} else {
FBAEMConfiguration *config = nil;
for (FBAEMConfiguration *c in [configList reverseObjectEnumerator]) {
if (c.validFrom <= _timestamp.timeIntervalSince1970 && [c isSameBusinessID:_businessID]) {
config = c;
break;
}
}
if (!config) {
return nil;
}
[self _setConfig:config];
return config;
}
}
- (void)_setConfig:(FBAEMConfiguration *)config
{
_configID = config.validFrom;
_configMode = config.configMode;
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
NSString *campaignID = [decoder decodeObjectOfClass:NSString.class forKey:CAMPAIGN_ID_KEY];
NSString *ACSToken = [decoder decodeObjectOfClass:NSString.class forKey:ACS_TOKEN_KEY];
NSString *ACSSharedSecret = [decoder decodeObjectOfClass:NSString.class forKey:ACS_SHARED_SECRET_KEY];
NSString *ACSConfigID = [decoder decodeObjectOfClass:NSString.class forKey:ACS_CONFIG_ID_KEY];
NSString *businessID = [decoder decodeObjectOfClass:NSString.class forKey:BUSINESS_ID_KEY];
NSDate *timestamp = [decoder decodeObjectOfClass:NSDate.class forKey:TIMESTAMP_KEY];
NSString *configMode = [decoder decodeObjectOfClass:NSString.class forKey:CONFIG_MODE_KEY];
NSInteger configID = [decoder decodeIntegerForKey:CONFIG_ID_KEY];
NSMutableSet<NSString *> *recordedEvents = [decoder decodeObjectOfClass:NSMutableSet.class forKey:RECORDED_EVENTS_KEY];
NSMutableDictionary<NSString *, NSMutableDictionary *> *recordedValues = [decoder decodeObjectOfClass:NSMutableDictionary.class forKey:RECORDED_VALUES_KEY];
NSInteger conversionValue = [decoder decodeIntegerForKey:CONVERSION_VALUE_KEY];
NSInteger priority = [decoder decodeIntegerForKey:PRIORITY_KEY];
NSDate *conversionTimestamp = [decoder decodeObjectOfClass:NSDate.class forKey:CONVERSION_TIMESTAMP_KEY];
BOOL isAggregated = [decoder decodeBoolForKey:IS_AGGREGATED_KEY];
BOOL hasSKAN = [decoder decodeBoolForKey:HAS_SKAN_KEY];
return [self initWithCampaignID:campaignID
ACSToken:ACSToken
ACSSharedSecret:ACSSharedSecret
ACSConfigID:ACSConfigID
businessID:businessID
timestamp:timestamp
configMode:configMode
configID:configID
recordedEvents:recordedEvents
recordedValues:recordedValues
conversionValue:conversionValue
priority:priority
conversionTimestamp:conversionTimestamp
isAggregated:isAggregated
isTestMode:NO
hasSKAN:hasSKAN];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:_campaignID forKey:CAMPAIGN_ID_KEY];
[encoder encodeObject:_ACSToken forKey:ACS_TOKEN_KEY];
[encoder encodeObject:_ACSSharedSecret forKey:ACS_SHARED_SECRET_KEY];
[encoder encodeObject:_ACSConfigID forKey:ACS_CONFIG_ID_KEY];
[encoder encodeObject:_businessID forKey:BUSINESS_ID_KEY];
[encoder encodeObject:_timestamp forKey:TIMESTAMP_KEY];
[encoder encodeObject:_configMode forKey:CONFIG_MODE_KEY];
[encoder encodeInteger:_configID forKey:CONFIG_ID_KEY];
[encoder encodeObject:_recordedEvents forKey:RECORDED_EVENTS_KEY];
[encoder encodeObject:_recordedValues forKey:RECORDED_VALUES_KEY];
[encoder encodeInteger:_conversionValue forKey:CONVERSION_VALUE_KEY];
[encoder encodeInteger:_priority forKey:PRIORITY_KEY];
[encoder encodeObject:_conversionTimestamp forKey:CONVERSION_TIMESTAMP_KEY];
[encoder encodeBool:_isAggregated forKey:IS_AGGREGATED_KEY];
[encoder encodeBool:_hasSKAN forKey:HAS_SKAN_KEY];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
#if DEBUG
#if FBTEST
- (void)setRecordedEvents:(NSMutableSet<NSString *> *)recordedEvents
{
_recordedEvents = recordedEvents;
}
- (void)setRecordedValues:(NSMutableDictionary<NSString *, NSMutableDictionary *> *)recordedValues
{
_recordedValues = recordedValues;
}
- (void)setPriority:(NSInteger)priority
{
_priority = priority;
}
- (void)setConfigID:(NSInteger)configID
{
_configID = configID;
}
- (void)setBusinessID:(NSString *_Nullable)businessID
{
_businessID = businessID;
}
- (void)setConversionTimestamp:(NSDate *_Nonnull)conversionTimestamp
{
_conversionTimestamp = conversionTimestamp;
}
- (void)setConversionValue:(NSInteger)conversionValue
{
_conversionValue = conversionValue;
}
- (void)setCampaignID:(NSString *_Nonnull)campaignID
{
_campaignID = campaignID;
}
- (void)setACSSharedSecret:(NSString *_Nullable)ACSSharedSecret
{
_ACSSharedSecret = ACSSharedSecret;
}
- (void)setACSConfigID:(NSString *_Nullable)ACSConfigID
{
_ACSConfigID = ACSConfigID;
}
- (void)reset
{
_timestamp = [NSDate date];
_configMode = @"DEFAULT";
_configID = -1;
_businessID = nil;
_recordedEvents = [NSMutableSet new];
_recordedValues = [NSMutableDictionary new];
_conversionValue = -1;
_priority = -1;
_conversionTimestamp = [NSDate date];
_isAggregated = YES;
_hasSKAN = NO;
}
#endif
#endif
@end
#endif
+20
View File
@@ -0,0 +1,20 @@
// 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.
#define FBAEMKit_VERSION_STRING @"11.2.1"
#define FBSDK_DEFAULT_GRAPH_API_VERSION @"v11.0"
+41
View File
@@ -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 "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
#import "FBAEMNetworking.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AEMNetworker)
@interface FBAEMNetworker : NSObject <FBAEMNetworking, NSURLSessionDataDelegate>
- (void)startGraphRequestWithGraphPath:(NSString *)graphPath
parameters:(NSDictionary *)parameters
tokenString:(nullable NSString *)tokenString
HTTPMethod:(nullable NSString *)method
completion:(FBGraphRequestCompletion)completion;
@end
NS_ASSUME_NONNULL_END
#endif
+156
View File
@@ -0,0 +1,156 @@
// 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 "FBAEMNetworker.h"
#import "FBAEMKitVersions.h"
#import "FBAEMRequestBody.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
#define kNewline @"\r\n"
static NSString *const kSDK = @"ios";
static NSString *const kUserAgentBase = @"FBiOSAEM";
@implementation FBAEMNetworker
static NSString *const FB_GRAPH_API_ENDPOINT = @"https://graph.facebook.com/v11.0/";
static NSString *const FB_GRAPH_API_CONTENT_TYPE = @"application/json";
NSErrorDomain const FBAEMErrorDomain = @"com.facebook.aemkit";
- (void)startGraphRequestWithGraphPath:(NSString *)graphPath
parameters:(NSDictionary *)parameters
tokenString:(nullable NSString *)tokenString
HTTPMethod:(nullable NSString *)method
completion:(FBGraphRequestCompletion)completion
{
NSURL *url = [NSURL URLWithString:[FB_GRAPH_API_ENDPOINT stringByAppendingString:graphPath]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setHTTPMethod:method];
[request setValue:[self userAgent] forHTTPHeaderField:@"User-Agent"];
[request setValue:FB_GRAPH_API_CONTENT_TYPE forHTTPHeaderField:@"Content-Type"];
[request setHTTPShouldHandleCookies:NO];
// add parameters to body
FBAEMRequestBody *body = [FBAEMRequestBody new];
NSMutableDictionary<NSString *, id> *params = [NSMutableDictionary dictionaryWithDictionary:parameters];
[FBSDKTypeUtility dictionary:params setObject:@"json" forKey:@"format"];
[FBSDKTypeUtility dictionary:params setObject:kSDK forKey:@"sdk"];
[FBSDKTypeUtility dictionary:params setObject:@"false" forKey:@"include_headers"];
[self appendAttachments:params toBody:body addFormData:[method isEqual:@"POST"]];
if ([request.HTTPMethod isEqualToString:@"POST"]) {
request.HTTPBody = body.compressedData;
[request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
} else {
request.HTTPBody = body.data;
}
FBSDKURLSession *session = [[FBSDKURLSession alloc] initWithDelegate:self delegateQueue:[NSOperationQueue currentQueue]];
[session executeURLRequest:request completionHandler:^(NSData *responseData, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSDictionary *result = [self parseJSONResponse:responseData error:&error statusCode:httpResponse.statusCode];
completion(result, error);
}];
}
- (NSDictionary *)parseJSONResponse:(NSData *)data
error:(NSError **)error
statusCode:(NSInteger)statusCode
{
NSString *responseUTF8 = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
id response = [self parseJSONOrOtherwise:responseUTF8 error:error];
NSDictionary *result;
if (responseUTF8 == nil) {
NSString *base64Data = data.length != 0 ? [data base64EncodedStringWithOptions:0] : @"";
if (base64Data != nil) {
NSLog(@"fb_response_invalid_utf8");
}
}
if (!response) {
if ((error != NULL) && (*error == nil)) {
*error = [[NSError alloc] initWithDomain:FBAEMErrorDomain code:statusCode userInfo:nil];
}
} else if ([response isKindOfClass:[NSDictionary class]]) {
result = response;
}
return result;
}
- (id)parseJSONOrOtherwise:(NSString *)unsafeString
error:(NSError **)error
{
id parsed = nil;
NSString *const utf8 = FBSDK_CAST_TO_CLASS_OR_NIL(unsafeString, NSString);
if (!(*error) && utf8) {
parsed = [FBSDKBasicUtility objectForJSONString:utf8 error:error];
}
return parsed;
}
- (void)appendAttachments:(NSDictionary *)attachments
toBody:(FBAEMRequestBody *)body
addFormData:(BOOL)addFormData
{
[FBSDKTypeUtility dictionary:attachments enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
value = [FBSDKBasicUtility convertRequestValue:value];
if ([value isKindOfClass:[NSString class]]) {
if (addFormData) {
[body appendWithKey:key formValue:(NSString *)value];
}
} else {
NSString *msg = [NSString stringWithFormat:@"Unsupported attachment:%@, skipping.", value];
NSLog(@"%@", msg);
}
}];
}
- (NSString *)userAgent
{
static NSString *agent = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
agent = [NSString stringWithFormat:@"%@.%@", kUserAgentBase, FBAEMKit_VERSION_STRING];
});
if (@available(iOS 13.0, *)) {
SEL selector = NSSelectorFromString(@"isMacCatalystApp");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if (selector && [NSProcessInfo.processInfo respondsToSelector:selector] && [NSProcessInfo.processInfo performSelector:selector]) {
#pragma clang diagnostic pop
return [NSString stringWithFormat:@"%@/%@", agent, @"macOS"];
}
}
return agent;
}
@end
#endif
+674
View File
@@ -0,0 +1,674 @@
// 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 "FBAEMReporter.h"
#include <stdlib.h>
#import "FBAEMAdvertiserRuleFactory.h"
#import "FBAEMConfiguration.h"
#import "FBAEMInvocation.h"
#import "FBAEMNetworker.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
#define FB_AEM_CONFIG_TIME_OUT 86400
typedef void (^FBAEMReporterBlock)(NSError *);
static NSString *const BUSINESS_ID_KEY = @"advertiser_id";
static NSString *const BUSINESS_IDS_KEY = @"advertiser_ids";
static NSString *const AL_APPLINK_DATA_KEY = @"al_applink_data";
static NSString *const CAMPAIGN_ID_KEY = @"campaign_id";
static NSString *const CONVERSION_DATA_KEY = @"conversion_data";
static NSString *const CONSUMPTION_HOUR_KEY = @"consumption_hour";
static NSString *const TOKEN_KEY = @"token";
static NSString *const HMAC_KEY = @"hmac";
static NSString *const CONFIG_ID_KEY = @"config_id";
static NSString *const DELAY_FLOW_KEY = @"delay_flow";
static NSString *const FBAEMConfigurationKey = @"com.facebook.sdk:FBSDKAEMConfiguration";
static NSString *const FBAEMReporterKey = @"com.facebook.sdk:FBSDKAEMReporter";
static NSString *const FBAEMReporterFileName = @"FBSDKAEMReportData.report";
static NSString *const FBAEMConfigFileName = @"FBSDKAEMReportData.config";
static NSString *const FBAEMHTTPMethodGET = @"GET";
static NSString *const FBAEMHTTPMethodPOST = @"POST";
static BOOL g_isAEMReportEnabled = NO;
static BOOL g_isLoadingConfiguration = NO;
static dispatch_queue_t g_serialQueue;
static NSString *g_reportFile;
static NSString *g_configFile;
static NSMutableDictionary<NSString *, NSMutableArray<FBAEMConfiguration *> *> *g_configs;
static NSMutableArray<FBAEMInvocation *> *g_invocations;
static NSDate *g_configRefreshTimestamp;
static NSMutableArray<FBAEMReporterBlock> *g_completionBlocks;
static _Nullable id<FBAEMNetworking> _networker = nil;
static _Nullable id<FBSKAdNetworkReporting> _reporter = nil;
static NSString *_appId;
@implementation FBAEMReporter
static char *const dispatchQueueLabel = "com.facebook.appevents.AEM.FBAEMReporter";
+ (void)configureWithNetworker:(nullable id<FBAEMNetworking>)networker
appID:(NSString *)appID
{
[self configureWithNetworker:networker appID:appID reporter:nil];
}
+ (void)configureWithNetworker:(nullable id<FBAEMNetworking>)networker
appID:(NSString *)appID
reporter:(nullable id<FBSKAdNetworkReporting>)reporter
{
if (self == [FBAEMReporter class]) {
_networker = networker;
_appId = appID;
_reporter = reporter;
}
}
+ (id<FBAEMNetworking>)networker
{
return _networker;
}
+ (id<FBSKAdNetworkReporting>)reporter
{
return _reporter;
}
+ (void)enable
{
if (@available(iOS 14.0, *)) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[FBAEMConfiguration configureWithRuleProvider:[FBAEMAdvertiserRuleFactory new]];
g_reportFile = [FBSDKBasicUtility persistenceFilePath:FBAEMReporterFileName];
g_configFile = [FBSDKBasicUtility persistenceFilePath:FBAEMConfigFileName];
g_completionBlocks = [NSMutableArray new];
if (!g_serialQueue) {
g_serialQueue = dispatch_queue_create(dispatchQueueLabel, DISPATCH_QUEUE_SERIAL);
}
[self dispatchOnQueue:g_serialQueue block:^() {
g_configs = [self _loadConfigs];
g_invocations = [self _loadReportData];
}];
[self _loadConfigurationWithBlock:^(NSError *error) {
if (error) {
return;
}
[self _sendAggregationRequest];
[self _clearCache];
}];
// If developers forget to call configureWithNetworker:appID:
// or pass nil for networker,
// we use default networker in FBAEMKit
if (!_networker) {
_networker = [FBAEMNetworker new];
}
// If developers forget to call configureWithNetworker:appID:,
// we throw warning here
if (!_appId) {
NSLog(@"App ID is not set up correctly, please call configureWithNetworker:appID: and pass correct FB app ID");
return;
}
g_isAEMReportEnabled = YES;
});
}
}
+ (void)handleURL:(NSURL *)url
{
if (!g_isAEMReportEnabled) {
return;
}
FBAEMInvocation *invocation = [self parseURL:url];
if (!invocation) {
return;
}
if (invocation.isTestMode) {
[self _sendDebuggingRequest:invocation];
return;
}
[self _appendAndSaveInvocation:invocation];
}
+ (nullable FBAEMInvocation *)parseURL:(NSURL *)url
{
if (!url) {
return nil;
}
NSDictionary<NSString *, NSString *> *params = [FBSDKBasicUtility dictionaryWithQueryString:url.query];
NSString *applinkDataString = params[AL_APPLINK_DATA_KEY];
if (!applinkDataString) {
return nil;
}
NSDictionary<id, id> *applinkData = [FBSDKTypeUtility dictionaryValue:[FBSDKBasicUtility objectForJSONString:applinkDataString error:NULL]];
if (!applinkData) {
return nil;
}
return [FBAEMInvocation invocationWithAppLinkData:applinkData];
}
+ (void)recordAndUpdateEvent:(NSString *)event
currency:(nullable NSString *)currency
value:(nullable NSNumber *)value
parameters:(nullable NSDictionary *)parameters
{
if (@available(iOS 14.0, *)) {
if (!g_isAEMReportEnabled || 0 == event.length) {
return;
}
[self _loadConfigurationWithBlock:^(NSError *error) {
if (0 == g_configs.count || 0 == g_invocations.count) {
return;
}
FBAEMInvocation *attributedInvocation = [self _attributedInvocation:g_invocations Event:event currency:currency value:value parameters:parameters configs:g_configs];
if (attributedInvocation) {
if ([attributedInvocation updateConversionValueWithConfigs:g_configs]) {
[self _sendAggregationRequest];
}
[self _saveReportData];
}
}];
}
}
+ (nullable FBAEMInvocation *)_attributedInvocation:(NSArray<FBAEMInvocation *> *)invocations
Event:(NSString *)event
currency:(nullable NSString *)currency
value:(nullable NSNumber *)value
parameters:(nullable NSDictionary *)parameters
configs:(NSDictionary<NSString *, NSMutableArray<FBAEMConfiguration *> *> *)configs
{
BOOL isGeneralInvocationVisited = NO;
FBAEMInvocation *attributedInvocation = nil;
for (FBAEMInvocation *invocation in [invocations reverseObjectEnumerator]) {
if ([self _isDoubleCounting:invocation event:event]) {
break;
}
if (!invocation.businessID && isGeneralInvocationVisited) {
continue;
}
if ([invocation attributeEvent:event currency:currency value:value parameters:parameters configs:configs]) {
attributedInvocation = invocation;
break;
}
if (!invocation.businessID) {
isGeneralInvocationVisited = YES;
}
}
return attributedInvocation;
}
+ (BOOL)_isDoubleCounting:(FBAEMInvocation *)invocation
event:(NSString *)event
{
// We consider it as double counting if following conditions meet simultaneously
// 1. The field hasSKAN is true
// 2. The conversion happens before SKAdNetwork cutoff
// 3. The event is also being reported by SKAdNetwork
return invocation.hasSKAN
&& ![_reporter shouldCutoff]
&& [_reporter isReportingEvent:event];
}
+ (void)_appendAndSaveInvocation:(FBAEMInvocation *)invocation
{
[self dispatchOnQueue:g_serialQueue block:^() {
[FBSDKTypeUtility array:g_invocations addObject:invocation];
[self _saveReportData];
}];
}
+ (void)_loadConfigurationWithBlock:(FBAEMReporterBlock)block
{
[self dispatchOnQueue:g_serialQueue block:^() {
[FBSDKTypeUtility array:g_completionBlocks addObject:block];
// Executes blocks if there is cache
if (![self _shouldRefresh]) {
for (FBAEMReporterBlock executionBlock in g_completionBlocks) {
executionBlock(nil);
}
[g_completionBlocks removeAllObjects];
return;
}
if (g_isLoadingConfiguration) {
return;
}
g_isLoadingConfiguration = YES;
[self.networker startGraphRequestWithGraphPath:[NSString stringWithFormat:@"%@/aem_conversion_configs", _appId]
parameters:[self _requestParameters]
tokenString:nil
HTTPMethod:FBAEMHTTPMethodGET
completion:^(id _Nullable result, NSError *_Nullable error) {
[self dispatchOnQueue:g_serialQueue block:^() {
if (error) {
for (FBAEMReporterBlock executionBlock in g_completionBlocks) {
executionBlock(error);
}
[g_completionBlocks removeAllObjects];
g_isLoadingConfiguration = NO;
return;
}
NSDictionary<NSString *, id> *json = [FBSDKTypeUtility dictionaryValue:result];
if (json) {
g_configRefreshTimestamp = [NSDate date];
[self _addConfigs:[FBSDKTypeUtility dictionary:json objectForKey:@"data" ofType:NSArray.class]];
for (FBAEMReporterBlock executionBlock in g_completionBlocks) {
executionBlock(nil);
}
[g_completionBlocks removeAllObjects];
} else {
NSLog(@"Received invalid AEM config");
}
g_isLoadingConfiguration = NO;
}];
}];
}];
}
+ (NSDictionary<NSString *, id> *)_requestParameters
{
NSMutableDictionary<NSString *, id> *params = [NSMutableDictionary new];
// append business ids to the request params
NSMutableArray<NSString *> *businessIDs = [NSMutableArray new];
for (FBAEMInvocation *invocation in g_invocations) {
[FBSDKTypeUtility array:businessIDs addObject:invocation.businessID];
}
NSString *businessIDsString = [FBSDKBasicUtility JSONStringForObject:businessIDs error:nil invalidObjectHandler:nil];
[FBSDKTypeUtility dictionary:params setObject:businessIDsString forKey:BUSINESS_IDS_KEY];
return [params copy];
}
+ (BOOL)_isConfigRefreshTimestampValid
{
return g_configRefreshTimestamp && [[NSDate date] timeIntervalSinceDate:g_configRefreshTimestamp] < FB_AEM_CONFIG_TIME_OUT;
}
+ (BOOL)_shouldRefresh
{
// Refresh if there exists invocation which has business ID
for (FBAEMInvocation *invocation in g_invocations) {
if (invocation.businessID) {
return YES;
}
}
// Refresh if timestamp is expired or cached config is empty
return (![self _isConfigRefreshTimestampValid]) || (0 == g_configs.count);
}
#pragma mark - Deeplink debugging methods
+ (void)_sendDebuggingRequest:(FBAEMInvocation *)invocation
{
NSMutableArray<NSDictionary *> *params = [NSMutableArray new];
[FBSDKTypeUtility array:params addObject:[self _debuggingRequestParameters:invocation]];
if (0 == params.count) {
return;
}
@try {
NSData *jsonData = [FBSDKTypeUtility dataWithJSONObject:params options:0 error:nil];
if (jsonData) {
NSString *reports = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[self.networker startGraphRequestWithGraphPath:[NSString stringWithFormat:@"%@/aem_conversions", _appId]
parameters:@{@"aem_conversions" : reports}
tokenString:nil
HTTPMethod:FBAEMHTTPMethodPOST
completion:^(id _Nullable result, NSError *_Nullable error) {
if (error) {
NSLog(@"Fail to send AEM debugging request with error: %@", error);
}
}];
}
} @catch (NSException *exception) {
NSLog(@"Fail to send AEM debugging request");
}
}
+ (NSDictionary<NSString *, id> *)_debuggingRequestParameters:(FBAEMInvocation *)invocation
{
NSMutableDictionary<NSString *, id> *conversionParams = [NSMutableDictionary new];
[FBSDKTypeUtility dictionary:conversionParams setObject:invocation.campaignID forKey:CAMPAIGN_ID_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:@(0) forKey:CONVERSION_DATA_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:@(0) forKey:CONSUMPTION_HOUR_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:invocation.ACSToken forKey:TOKEN_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:@"server" forKey:DELAY_FLOW_KEY];
return [conversionParams copy];
}
#pragma mark - Background methods
+ (NSMutableDictionary<NSString *, NSMutableArray<FBAEMConfiguration *> *> *)_loadConfigs
{
if (@available(iOS 11.0, *)) {
NSData *cachedConfig = [NSData dataWithContentsOfFile:g_configFile options:NSDataReadingMappedIfSafe error:nil];
if ([cachedConfig isKindOfClass:NSData.class]) {
NSSet *classes = [NSSet setWithArray:@[
NSMutableDictionary.class,
NSMutableArray.class,
FBAEMConfiguration.class,
FBAEMRule.class,
FBAEMEvent.class]];
NSDictionary<NSString *, NSMutableArray<FBAEMConfiguration *> *> *cache = [FBSDKTypeUtility dictionaryValue:[NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:cachedConfig error:nil]];
if (cache) {
return [cache mutableCopy];
}
}
}
return [NSMutableDictionary new];
}
+ (void)_saveConfigs
{
if (!g_configs) {
return;
}
if (@available(iOS 11.0, *)) {
NSData *cache = [NSKeyedArchiver archivedDataWithRootObject:g_configs requiringSecureCoding:NO error:nil];
if (cache && g_configFile) {
[cache writeToFile:g_configFile atomically:YES];
}
}
}
+ (void)_addConfigs:(nullable NSArray<NSDictionary *> *)configs
{
if (0 == configs.count) {
return;
}
for (NSDictionary *config in configs) {
[self _addConfig:[[FBAEMConfiguration alloc] initWithJSON:config]];
}
[self _saveConfigs];
}
+ (void)_addConfig:(nullable FBAEMConfiguration *)config
{
if (!config.configMode) {
return;
}
NSMutableArray<FBAEMConfiguration *> *configs = [FBSDKTypeUtility dictionary:g_configs objectForKey:config.configMode ofType:NSMutableArray.class];
// Remove the config in the array that has the same "validFrom" and "businessID" as the added config
NSMutableArray<FBAEMConfiguration *> *res = [NSMutableArray new];
for (FBAEMConfiguration *c in configs) {
if ([config isSameValidFrom:c.validFrom businessID:c.businessID]) {
continue;
}
[FBSDKTypeUtility array:res addObject:c];
}
[FBSDKTypeUtility array:res addObject:config];
[FBSDKTypeUtility dictionary:g_configs setObject:res forKey:config.configMode];
// Sort the configs via "validFrom"
[res sortUsingComparator:^NSComparisonResult (FBAEMConfiguration *obj1, FBAEMConfiguration *obj2) {
if (obj1.validFrom > obj2.validFrom) {
return NSOrderedDescending;
}
if (obj1.validFrom < obj2.validFrom) {
return NSOrderedAscending;
}
return NSOrderedSame;
}];
}
+ (NSMutableArray<FBAEMInvocation *> *)_loadReportData
{
if (@available(iOS 11.0, *)) {
NSData *cachedReportData = [NSData dataWithContentsOfFile:g_reportFile options:NSDataReadingMappedIfSafe error:nil];
if ([cachedReportData isKindOfClass:NSData.class]) {
NSArray<FBAEMInvocation *> *cache = [FBSDKTypeUtility arrayValue:[NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithArray:@[NSArray.class, FBAEMInvocation.class]] fromData:cachedReportData error:nil]];
if (cache) {
return [cache mutableCopy];
}
}
}
return [NSMutableArray new];
}
+ (void)_saveReportData
{
if (@available(iOS 11.0, *)) {
NSData *cache = [NSKeyedArchiver archivedDataWithRootObject:g_invocations requiringSecureCoding:NO error:nil];
if (cache && g_reportFile) {
[cache writeToFile:g_reportFile atomically:YES];
}
}
}
+ (void)_sendAggregationRequest
{
NSMutableArray<NSDictionary *> *params = [NSMutableArray new];
NSMutableArray<FBAEMInvocation *> *aggregatedInvocations = [NSMutableArray new];
for (FBAEMInvocation *invocation in g_invocations) {
if (!invocation.isAggregated) {
[FBSDKTypeUtility array:params addObject:[self _aggregationRequestParameters:invocation]];
[FBSDKTypeUtility array:aggregatedInvocations addObject:invocation];
}
}
if (0 == params.count) {
return;
}
@try {
NSData *jsonData = [FBSDKTypeUtility dataWithJSONObject:params options:0 error:nil];
if (jsonData) {
NSString *reports = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[self.networker startGraphRequestWithGraphPath:[NSString stringWithFormat:@"%@/aem_conversions", _appId]
parameters:@{@"aem_conversions" : reports}
tokenString:nil
HTTPMethod:FBAEMHTTPMethodPOST
completion:^(id _Nullable result, NSError *_Nullable error) {
if (error) {
return;
}
[self dispatchOnQueue:g_serialQueue block:^() {
for (FBAEMInvocation *invocation in aggregatedInvocations) {
invocation.isAggregated = YES;
}
[self _saveReportData];
}];
}];
}
} @catch (NSException *exception) {
NSLog(@"Fail to send AEM reports");
}
}
+ (NSDictionary<NSString *, id> *)_aggregationRequestParameters:(FBAEMInvocation *)invocation
{
NSInteger delay = 24 + arc4random_uniform(24);
NSMutableDictionary<NSString *, id> *conversionParams = [NSMutableDictionary new];
[FBSDKTypeUtility dictionary:conversionParams setObject:invocation.campaignID forKey:CAMPAIGN_ID_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:@(invocation.conversionValue) forKey:CONVERSION_DATA_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:@(delay) forKey:CONSUMPTION_HOUR_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:invocation.ACSToken forKey:TOKEN_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:@"server" forKey:DELAY_FLOW_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:invocation.ACSConfigID forKey:CONFIG_ID_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:[invocation getHMAC:delay] forKey:HMAC_KEY];
[FBSDKTypeUtility dictionary:conversionParams setObject:invocation.businessID forKey:BUSINESS_ID_KEY];
return [conversionParams copy];
}
+ (void)dispatchOnQueue:(dispatch_queue_t)queue block:(dispatch_block_t)block
{
if (block != nil) {
if (strcmp(dispatch_queue_get_label(queue), dispatchQueueLabel) == 0) {
dispatch_async(queue, block);
} else {
block();
}
}
}
+ (void)_clearCache
{
// step 1: clear aggregated invocations that are outside attribution window
[self _clearInvocations];
// step 2: clear old configs that are not used anymore and keep the most recent config
[self _clearConfigs];
}
+ (void)_clearConfigs
{
BOOL shouldSaveCache = NO;
if (g_configs.count > 0) {
NSMutableDictionary<NSString *, NSMutableArray<FBAEMConfiguration *> *> *configs = [NSMutableDictionary new];
for (NSString *key in g_configs) {
NSMutableArray<FBAEMConfiguration *> *oldConfigurations = [FBSDKTypeUtility dictionary:g_configs objectForKey:key ofType:NSMutableArray.class];
NSMutableArray<FBAEMConfiguration *> *newConfigurations = [NSMutableArray new];
// Removes the last of the old configurations and stores it so it can be
// added to the array-to-save
FBAEMConfiguration *lastConfiguration = oldConfigurations.lastObject;
[oldConfigurations removeLastObject];
for (FBAEMConfiguration *oldConfiguration in oldConfigurations) {
if (![self _isUsingConfig:oldConfiguration forInvocations:g_invocations]) {
shouldSaveCache = YES;
continue;
}
[FBSDKTypeUtility array:newConfigurations addObject:oldConfiguration];
}
[FBSDKTypeUtility array:newConfigurations addObject:lastConfiguration];
[FBSDKTypeUtility dictionary:configs setObject:newConfigurations forKey:key];
}
g_configs = configs;
}
if (shouldSaveCache) {
[self _saveConfigs];
}
}
+ (void)_clearInvocations
{
BOOL isInvocationCacheUpdated = NO;
if (g_invocations.count > 0) {
NSMutableArray<FBAEMInvocation *> *res = [NSMutableArray new];
for (FBAEMInvocation *invocation in g_invocations) {
if ([invocation isOutOfWindowWithConfigs:g_configs] && invocation.isAggregated) {
isInvocationCacheUpdated = YES;
continue;
}
[FBSDKTypeUtility array:res addObject:invocation];
}
g_invocations = res;
}
if (isInvocationCacheUpdated) {
[self _saveReportData];
}
}
+ (BOOL)_isUsingConfig:(FBAEMConfiguration *)config
forInvocations:(NSArray<FBAEMInvocation *> *)invocations
{
for (FBAEMInvocation *invocation in invocations) {
if ([config isSameValidFrom:invocation.configID businessID:invocation.businessID]) {
return YES;
}
}
return NO;
}
#pragma mark - Testability
#if DEBUG
#if FBTEST
+ (NSMutableDictionary<NSString *, NSMutableArray<FBAEMConfiguration *> *> *)configs
{
return g_configs;
}
+ (void)setConfigs:(NSMutableDictionary<NSString *, NSMutableArray<FBAEMConfiguration *> *> *)configs
{
g_configs = configs;
}
+ (void)setInvocations:(NSMutableArray<FBAEMInvocation *> *)invocations
{
g_invocations = invocations;
}
+ (NSMutableArray<FBAEMInvocation *> *)invocations
{
return g_invocations;
}
+ (void)setIsEnabled:(BOOL)enabled
{
g_isAEMReportEnabled = enabled;
}
+ (BOOL)isEnabled
{
return g_isAEMReportEnabled;
}
+ (void)setCompletionBlocks:(NSMutableArray<FBAEMReporterBlock> *)completionBlocks
{
g_completionBlocks = completionBlocks;
}
+ (void)setQueue:(nullable dispatch_queue_t)queue
{
g_serialQueue = queue;
}
+ (void)setTimestamp:(NSDate *)timestamp
{
g_configRefreshTimestamp = timestamp;
}
+ (void)setIsLoadingConfiguration:(BOOL)loading
{
g_isLoadingConfiguration = loading;
}
+ (NSString *)reportFilePath
{
return g_reportFile;
}
+ (void)setReportFilePath:(NSString *)path
{
g_reportFile = path;
}
#endif
#endif
@end
#endif
+38
View File
@@ -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(RequestBody)
@interface FBAEMRequestBody : NSObject
@property (nonatomic, retain, readonly) NSData *data;
- (void)appendWithKey:(NSString *)key
formValue:(NSString *)value;
- (NSData *)compressedData;
@end
#endif
+116
View File
@@ -0,0 +1,116 @@
// 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 "FBAEMRequestBody.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
#define kNewline @"\r\n"
typedef void (^AEMCodeBlock)(void);
@implementation FBAEMRequestBody
{
NSMutableData *_data;
NSMutableDictionary *_json;
}
- (instancetype)init
{
if ((self = [super init])) {
_data = [NSMutableData new];
_json = [NSMutableDictionary dictionary];
}
return self;
}
- (void)appendUTF8:(NSString *)utf8
{
if (!_data.length) {
NSString *headerUTF8 = [NSString stringWithFormat:@"--%@", kNewline];
NSData *headerData = [headerUTF8 dataUsingEncoding:NSUTF8StringEncoding];
[_data appendData:headerData];
}
NSData *data = [utf8 dataUsingEncoding:NSUTF8StringEncoding];
[_data appendData:data];
}
- (void)appendWithKey:(NSString *)key
formValue:(NSString *)value
{
[self _appendWithKey:key filename:nil contentType:nil contentBlock:^{
[self appendUTF8:value];
}];
if (key && value) {
[FBSDKTypeUtility dictionary:_json setObject:value forKey:key];
}
}
- (NSData *)data
{
NSData *jsonData;
if (_json.allKeys.count > 0) {
jsonData = [FBSDKTypeUtility dataWithJSONObject:_json options:0 error:nil];
} else {
jsonData = [NSData data];
}
return jsonData;
}
- (void)_appendWithKey:(NSString *)key
filename:(NSString *)filename
contentType:(NSString *)contentType
contentBlock:(AEMCodeBlock)contentBlock
{
NSMutableArray *disposition = [NSMutableArray new];
[FBSDKTypeUtility array:disposition addObject:@"Content-Disposition: form-data"];
if (key) {
[FBSDKTypeUtility array:disposition addObject:[[NSString alloc] initWithFormat:@"name=\"%@\"", key]];
}
if (filename) {
[FBSDKTypeUtility array:disposition addObject:[[NSString alloc] initWithFormat:@"filename=\"%@\"", filename]];
}
[self appendUTF8:[[NSString alloc] initWithFormat:@"%@%@", [disposition componentsJoinedByString:@"; "], kNewline]];
if (contentType) {
[self appendUTF8:[[NSString alloc] initWithFormat:@"Content-Type: %@%@", contentType, kNewline]];
}
[self appendUTF8:kNewline];
if (contentBlock != NULL) {
contentBlock();
}
[self appendUTF8:[[NSString alloc] initWithFormat:@"%@", kNewline]];
}
- (NSData *)compressedData
{
if (!self.data.length) {
return nil;
}
return [FBSDKBasicUtility gzip:self.data];
}
@end
#endif
+46
View File
@@ -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 "TargetConditionals.h"
#if !TARGET_OS_TV
#import <Foundation/Foundation.h>
#import "FBAEMEvent.h"
NS_ASSUME_NONNULL_BEGIN
@interface FBAEMRule : NSObject <NSCopying, NSSecureCoding>
@property (nonatomic) NSInteger conversionValue;
@property (nonatomic) NSInteger priority;
@property (nonatomic, copy) NSArray<FBAEMEvent *> *events;
- (nullable instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict;
- (BOOL)isMatchedWithRecordedEvents:(nullable NSSet<NSString *> *)recordedEvents
recordedValues:(nullable NSDictionary<NSString *, NSDictionary *> *)recordedValues;
@end
NS_ASSUME_NONNULL_END
#endif
+144
View File
@@ -0,0 +1,144 @@
// 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 "FBAEMRule.h"
#import "FBCoreKitBasicsImportForAEMKit.h"
static NSString *const CONVERSION_VALUE_KEY = @"conversion_value";
static NSString *const PRIORITY_KEY = @"priority";
static NSString *const EVENTS_KEY = @"events";
@implementation FBAEMRule
- (nullable instancetype)initWithJSON:(nullable NSDictionary<NSString *, id> *)dict
{
if ((self = [super init])) {
dict = [FBSDKTypeUtility dictionaryValue:dict];
if (!dict) {
return nil;
}
NSNumber *conversionValue = [FBSDKTypeUtility dictionary:dict objectForKey:CONVERSION_VALUE_KEY ofType:NSNumber.class];
NSNumber *priority = [FBSDKTypeUtility dictionary:dict objectForKey:PRIORITY_KEY ofType:NSNumber.class];
NSArray<FBAEMEvent *> *events = [FBAEMRule parseEvents:[FBSDKTypeUtility dictionary:dict objectForKey:EVENTS_KEY ofType:NSArray.class]];
if (conversionValue == nil || priority == nil || 0 == events.count) {
return nil;
}
_conversionValue = conversionValue.integerValue;
_priority = priority.integerValue;
_events = events;
}
return self;
}
- (instancetype)initWithConversionValue:(NSInteger)conversionValue
priority:(NSInteger)priority
events:(NSArray<FBAEMEvent *> *)events
{
if ((self = [super init])) {
_conversionValue = conversionValue;
_priority = priority;
_events = events;
}
return self;
}
- (BOOL)isMatchedWithRecordedEvents:(nullable NSSet<NSString *> *)recordedEvents
recordedValues:(nullable NSDictionary<NSString *, NSDictionary *> *)recordedValues
{
for (FBAEMEvent *event in self.events) {
// Check if event name matches
if (![recordedEvents containsObject:event.eventName]) {
return NO;
}
// Check if event value matches when values is not nil
if (event.values) {
NSDictionary<NSString *, NSNumber *> *recordedEventValues = [FBSDKTypeUtility dictionary:recordedValues objectForKey:event.eventName ofType:NSDictionary.class];
if (![self _isMatchedWithValues:event.values recordedEventValues:recordedEventValues]) {
return NO;
}
}
}
return YES;
}
- (BOOL)_isMatchedWithValues:(NSDictionary<NSString *, NSNumber *> *)values
recordedEventValues:(nullable NSDictionary<NSString *, NSNumber *> *)recordedEventValues
{
for (NSString *currency in values) {
NSNumber *valueInMapping = [FBSDKTypeUtility dictionary:values objectForKey:currency ofType:NSNumber.class];
NSNumber *value = [FBSDKTypeUtility dictionary:recordedEventValues objectForKey:currency ofType:NSNumber.class];
if (value.doubleValue >= valueInMapping.doubleValue) {
return YES;
}
}
return NO;
}
+ (nullable NSArray<FBAEMEvent *> *)parseEvents:(nullable NSArray<NSDictionary<NSString *, id> *> *)events
{
if (0 == events.count) {
return nil;
}
NSMutableArray<FBAEMEvent *> *parsedEvents = [NSMutableArray new];
for (NSDictionary<NSString *, id> *eventEntry in events) {
FBAEMEvent *event = [[FBAEMEvent alloc] initWithJSON:eventEntry];
if (!event) {
return nil;
}
[FBSDKTypeUtility array:parsedEvents addObject:event];
}
return [parsedEvents copy];
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
NSInteger conversionValue = [decoder decodeIntegerForKey:CONVERSION_VALUE_KEY];
NSInteger priority = [decoder decodeIntegerForKey:PRIORITY_KEY];
NSArray<FBAEMEvent *> *events = [decoder decodeObjectOfClasses:[NSSet setWithArray:@[NSArray.class, FBAEMEvent.class]] forKey:EVENTS_KEY];
return [self initWithConversionValue:conversionValue priority:priority events:events];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInteger:_conversionValue forKey:CONVERSION_VALUE_KEY];
[encoder encodeInteger:_priority forKey:PRIORITY_KEY];
[encoder encodeObject:_events forKey:EVENTS_KEY];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return self;
}
@end
#endif
@@ -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.
// TODO: This needs to be renamed once BUCK does not include CoreKit Internal Headers
#if defined FBSDK_SWIFT_PACKAGE
@import FBSDKCoreKit_Basics;
#else
#import <FBSDKCoreKit_Basics/FBSDKCoreKit_Basics.h>
#endif
+43
View File
@@ -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.
// This deserves some explanation.
// Cocoapods - we define FBAEMKit as a subspec so to import it we need to use
// the name of the module produced by the podspec which is FBSDKCoreKit. To be able
// to reference it as <FBAEMKit> we need to publish a separate pod.
// Because of the way files in the subspec are made available to the pod, we do not need
// to use the bracket import syntax.
//
// BUCK - we define FBAEMKit as a separate library so we must import it as
// <FBAEMKit>
//
// Xcodeproj - we define FBAEMKit as a distinct module with its own project
// so that we can import it as <FBAEMKit>
//
// Swift Package Manager - it can be imported with `@import FBAEMKit` which allows us to reference
// public headers without bracket import syntax.
#import "TargetConditionals.h"
#if !TARGET_OS_TV
#import "FBAEMNetworking.h"
#import "FBAEMReporter.h"
#import "FBSKAdNetworkReporting.h"
#endif
+42
View File
@@ -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>
NS_ASSUME_NONNULL_BEGIN
typedef void (^FBGraphRequestCompletion)(id _Nullable result, NSError *_Nullable error);
NS_SWIFT_NAME(AEMNetworking)
@protocol 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
+95
View File
@@ -0,0 +1,95 @@
// 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 "FBAEMNetworking.h"
#import "FBSKAdNetworkReporting.h"
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(AEMReporter)
@interface FBAEMReporter : NSObject
/**
Configure networker used for calling Facebook AEM Graph API endpoint
and Facebook App ID
This function should be called in application(_:open:options:) from ApplicationDelegate
and BEFORE [FBAEMReporter enable] function
@param networker An optional networker conforms to FBAEMNetworking which handles Graph API request
@param appID The Facebook app ID
*/
+ (void)configureWithNetworker:(nullable id<FBAEMNetworking>)networker
appID:(NSString *)appID;
/**
Configure networker used for calling Facebook AEM Graph API endpoint
and Facebook App ID
This function should be called in application(_:open:options:) from ApplicationDelegate
and BEFORE [FBAEMReporter enable] function. We will use SKAdNetwork reporter to prevent
double counting.
@param networker An optional networker conforms to FBAEMNetworking which handles Graph API request
@param appID The Facebook app ID
@param reporter The SKAdNetwork repoter
*/
+ (void)configureWithNetworker:(nullable id<FBAEMNetworking>)networker
appID:(NSString *)appID
reporter:(nullable id<FBSKAdNetworkReporting>)reporter;
/**
Enable AEM reporting
This function should be called in application(_:open:options:) from ApplicationDelegate
*/
+ (void)enable;
/**
Handle deeplink
This function should be called in application(_:open:options:) from ApplicationDelegate
*/
+ (void)handleURL:(NSURL *)url;
/**
Calculate the conversion value for the app event based on the AEM configuration
This function should be called when you log any in-app events
*/
+ (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
#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_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(SKAdNetworkReporting)
@protocol FBSKAdNetworkReporting
- (BOOL)shouldCutoff;
- (BOOL)isReportingEvent:(NSString *)event;
@end
NS_ASSUME_NONNULL_END
#endif