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
+17
View File
@@ -0,0 +1,17 @@
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.
+83
View File
@@ -0,0 +1,83 @@
# Facebook SDK for iOS
[![Platforms](https://img.shields.io/cocoapods/p/FBSDKCoreKit.svg)](https://cocoapods.org/pods/FBSDKCoreKit)
[![circleci](https://circleci.com/gh/facebook/facebook-ios-sdk/tree/master.svg?style=shield)](https://circleci.com/gh/facebook/facebook-ios-sdk/tree/master)
[![CocoaPods](https://img.shields.io/cocoapods/v/FBSDKCoreKit.svg)](https://cocoapods.org/pods/FBSDKCoreKit)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
This open-source library allows you to integrate Facebook into your iOS app.
Learn more about the provided samples, documentation, integrating the SDK into your app, accessing source code, and more
at https://developers.facebook.com/docs/ios
Please take a moment and [subscribe to releases](https://docs.github.com/en/enterprise/2.15/user/articles/watching-and-unwatching-repositories) so that you can be notified about new features, deprecations, and critical fixes. To see information about the latest release, consult our [changelog](CHANGELOG.md).
## TRY IT OUT
### Swift Package Manager
1. In Xcode, select File > Swift Packages > Add Package Dependency.
1. Follow the prompts using the URL for this repository
1. Select the `Facebook`-prefixed libraries you want to use
1. Check-out the tutorials available online at: <https://developers.facebook.com/docs/ios/getting-started>
1. Start coding! Visit <https://developers.facebook.com/docs/ios> for tutorials and reference documentation.
## iOS 14 CHANGES
### Data Disclosure
Due to the release of iOS 14, tracking events that your app collects and sends to Facebook may require you to disclosed these data types in the App Store Connect questionnaire. It is your responsibility to ensure this is reflected in your applications privacy policy. Visit our blogpost for information on affected Facebook SDKs, APIs, and products and the Apple App Store Privacy Details article to learn more about the data types you will need to disclose.
link to FB blogpost https://developers.facebook.com/blog/post/2020/10/22/preparing-for-apple-app-store-data-disclosure-requirements/
apple store details https://developer.apple.com/app-store/app-privacy-details/
## FEATURES
- Login - <https://developers.facebook.com/docs/facebook-login>
- Sharing - <https://developers.facebook.com/docs/sharing>
- App Links - <https://developers.facebook.com/docs/applinks>
- Graph API - <https://developers.facebook.com/docs/ios/graph>
- Analytics - <https://developers.facebook.com/docs/analytics>
## GIVE FEEDBACK
Please report bugs or issues to our designated developer support team -- <https://developers.facebook.com/support/bugs/> -- as this will help us resolve them more quickly.
You can also visit our [Facebook Developer Community Forum](https://developers.facebook.com/community/),
join the [Facebook Developers Group on Facebook](https://www.facebook.com/groups/fbdevelopers/),
ask questions on [Stack Overflow](http://facebook.stackoverflow.com),
or open an issue in this repository.
## CONTRIBUTE
Facebook welcomes contributions to our SDKs. Please see the [CONTRIBUTING](CONTRIBUTING) file.
## LICENSE
See the [LICENSE](LICENSE) file.
## SECURITY POLICY
See the [SECURITY POLICY](SECURITY.md) for more info on our bug bounty program.
## DEVELOPER TERMS
- By enabling Facebook integrations, including through this SDK, you can share information with Facebook, including
information about peoples use of your app. Facebook will use information received in accordance with our
[Data Use Policy](https://www.facebook.com/about/privacy/), including to provide you with insights about the
effectiveness of your ads and the use of your app. These integrations also enable us and our partners to serve ads on
and off Facebook.
- You may limit your sharing of information with us by updating the Insights control in the developer tool
`https://developers.facebook.com/apps/{app_id}/settings/advanced`.
- If you use a Facebook integration, including to share information with us, you agree and confirm that you have
provided appropriate and sufficiently prominent notice to and obtained the appropriate consent from your users
regarding such collection, use, and disclosure (including, at a minimum, through your privacy policy). You further
agree that you will not share information with us about children under the age of 13.
- You agree to comply with all applicable laws and regulations and also agree to our Terms
<https://www.facebook.com/policies/>, including our Platform Policies <https://developers.facebook.com/policy/> and
Advertising Guidelines, as applicable <https://www.facebook.com/ad_guidelines.php>.
By using the Facebook SDK for iOS you agree to these terms.
@@ -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