Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CF-106] Fetch AdServices Token #1519

Merged
merged 45 commits into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
f3131f9
Add new configuration option automaticAdServicesAttributionTokenColle…
Apr 20, 2022
d322c7c
Add postAdServicesTokenIfNeeded, call in same places as old iAD calls
Apr 20, 2022
dbcdc4c
Fetch and post ad services token
Apr 20, 2022
c557ba5
Fix incorrect available check
Apr 20, 2022
882a936
Pull discount string from attribution strings to StoreKitStrings
Apr 20, 2022
21aa633
Merge branch 'main' into ad-services
Apr 20, 2022
cc93cdb
fetching unit tests
Apr 20, 2022
9706097
formatting/renaming
Apr 20, 2022
c98031a
Remove code for fetching
Apr 20, 2022
03357e7
Use canImport
Apr 20, 2022
bcd58a2
Update availability checks, move to Deprecations.swift
Apr 21, 2022
5c957b7
Merge branch 'ad-services' of github.com:RevenueCat/purchases-ios int…
Apr 21, 2022
4c6332c
Remove some todos, fix formatting
Apr 21, 2022
4a952c4
fix lint
Apr 22, 2022
3811408
fix mockattributionfetcher
Apr 25, 2022
e02c0d2
API testers
Apr 25, 2022
81f2628
Merge branch 'ad-services-sdk' into ad-services
Apr 25, 2022
47ecfff
move rest of deprecations to deprecations.swift
Apr 25, 2022
d51c37f
Add comment about OS availability
Apr 25, 2022
f2014f8
Revert "move rest of deprecations to deprecations.swift"
Apr 25, 2022
8923389
Add explicit integer values for attribution networks
Apr 25, 2022
e5dea3a
Fix deprecation warning ...
Apr 25, 2022
10a073b
update fastlane
Apr 25, 2022
352de9e
Revert "Fix deprecation warning ..."
Apr 26, 2022
bae8e3c
skipping setting properties on de-init
Apr 26, 2022
d4a552a
Update docc and AttributionNetwork api tester
Apr 26, 2022
c181dc7
Remove iad code
Apr 26, 2022
f29aedb
deprecate attributionnetwork enum value
Apr 26, 2022
d6d3dc7
remove superfluous lint disable
Apr 27, 2022
67b3e7a
remove iad-associated unit tests
Apr 27, 2022
4d800ac
Merge branch 'ad-services-sdk' into ad-services
Apr 27, 2022
2b21373
fix more unit tests
Apr 27, 2022
61c28a3
Fix bad merges
Apr 27, 2022
26b02f7
remove PostAttributionDataOperation and AfficheClientProxy
Apr 27, 2022
a11742a
Make adServicesToken a var
Apr 28, 2022
6a7697b
use do catch, more details in error log
Apr 28, 2022
60db73b
formatting
Apr 28, 2022
1df28a0
Update Sources/Purchasing/Purchases.swift
Apr 28, 2022
3e3d383
linking optional
Apr 28, 2022
cc98cbe
directly return
Apr 28, 2022
839bc8d
`CustomerInfo`: moved deprecated property to `Deprecations` (#1549)
NachoSoto Apr 28, 2022
2c16386
make error not optional
Apr 28, 2022
42bbabc
Merge branch 'main' into ad-services
Apr 28, 2022
3de0061
Add AdServices.framework to APITesters
joshdholtz Apr 29, 2022
d9f757b
Don't link `AdServices.framework` on `watchOS` & `tvOS` (#1554)
NachoSoto Apr 29, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.573.0)
aws-sdk-core (3.130.0)
aws-partitions (1.579.0)
aws-sdk-core (3.130.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.55.0)
aws-sdk-kms (1.56.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.113.0)
aws-sdk-s3 (1.113.1)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
aws-sigv4 (1.5.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
Expand Down Expand Up @@ -116,7 +116,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.205.1)
fastlane (2.205.2)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
Expand Down Expand Up @@ -160,7 +160,7 @@ GEM
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.18.0)
google-apis-androidpublisher_v3 (0.19.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.2)
addressable (~> 2.5, >= 2.5.1)
Expand All @@ -175,23 +175,23 @@ GEM
google-apis-core (>= 0.4, < 2.a)
google-apis-playcustomapp_v1 (0.7.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.12.0)
google-apis-storage_v1 (0.13.0)
google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.36.1)
google-cloud-storage (1.36.2)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.1.2)
googleauth (1.1.3)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
Expand Down Expand Up @@ -234,7 +234,7 @@ GEM
optparse (0.1.1)
os (1.1.4)
plist (3.6.0)
public_suffix (4.0.6)
public_suffix (4.0.7)
rake (13.0.6)
redcarpet (3.5.1)
representable (3.1.1)
Expand Down
26 changes: 9 additions & 17 deletions RevenueCat.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

62 changes: 0 additions & 62 deletions Sources/Attribution/AfficheClientProxy.swift

This file was deleted.

42 changes: 18 additions & 24 deletions Sources/Attribution/AttributionFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@ import UIKit
import WatchKit
#endif

#if canImport(AdServices)
import AdServices
#endif

enum AttributionFetcherError: Error {

case identifierForAdvertiserUnavailableForPlatform
case identifierForAdvertiserFrameworksUnavailable
case adServicesNotAvailable
case adServicesTokenFetchError

}

Expand Down Expand Up @@ -67,35 +73,23 @@ class AttributionFetcher {
return nil
}

func afficheClientAttributionDetails(completion: @escaping ([String: NSObject]?, Error?) -> Void) {
// Should match available platforms in
// https://developer.apple.com/documentation/iad/adclient?language=swift
#if os(iOS)
guard let afficheClientProxy = attributionFactory.afficheClientProxy() else {
Logger.warn(Strings.attribution.search_ads_attribution_cancelled_missing_ad_framework)
completion(nil, AttributionFetcherError.identifierForAdvertiserFrameworksUnavailable)
return
// should match OS availability in https://developer.apple.com/documentation/ad_services
@available(iOS 14.3, macOS 11.1, macCatalyst 14.3, *)
var adServicesToken: String? {
#if canImport(AdServices)
do {
return try AAAttribution.attributionToken()
} catch {
let message = Strings.attribution.adservices_token_fetch_failed(error: error)
Logger.appleWarning(message)
return nil
}
afficheClientProxy.requestAttributionDetails(completion)
#else
completion(nil, AttributionFetcherError.identifierForAdvertiserUnavailableForPlatform)
Logger.warn(Strings.attribution.adservices_not_supported)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

return nil
#endif
}

var isAuthorizedToPostSearchAds: Bool {
// Should match platforms that require permissions detailed in
// https://developer.apple.com/app-store/user-privacy-and-data-use/
if !appTrackingTransparencyRequired {
return true
}

if #available(iOS 14.0.0, tvOS 14.0.0, *) {
return isAuthorizedToPostSearchAdsInATTRequiredOS
}

return true
}

var authorizationStatus: FakeTrackingManagerAuthorizationStatus {
// should match OS availability here: https://rev.cat/app-tracking-transparency
guard #available(iOS 14.0.0, tvOS 14.0.0, macOS 11.0.0, *) else {
Expand Down
20 changes: 13 additions & 7 deletions Sources/Attribution/AttributionNetwork.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,43 @@ import Foundation
/**
Apple's search ads
*/
case appleSearchAds
@available(*, deprecated, message: "use adServices")
case appleSearchAds = 0

/**
Adjust https://www.adjust.com/
*/
case adjust
case adjust = 1

/**
AppsFlyer https://www.appsflyer.com/
*/
case appsFlyer
case appsFlyer = 2

/**
Branch https://www.branch.io/
*/
case branch
case branch = 3

/**
Tenjin https://www.tenjin.io/
*/
case tenjin
case tenjin = 4

/**
Facebook https://developers.facebook.com/
*/
case facebook
case facebook = 5

/**
mParticle https://www.mparticle.com/
*/
case mParticle
case mParticle = 6

/**
AdServices token
*/
case adServices = 7

}

Expand Down
57 changes: 12 additions & 45 deletions Sources/Attribution/AttributionPoster.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class AttributionPoster {
self.subscriberAttributesManager = subscriberAttributesManager
}

// swiftlint:disable:next function_body_length
func post(attributionData data: [String: Any],
fromNetwork network: AttributionNetwork,
networkUserId: String?) {
Expand Down Expand Up @@ -88,46 +87,27 @@ class AttributionPoster {
}

if !newData.isEmpty {
if network == .appleSearchAds {
postSearchAds(newData: newData,
network: network,
appUserID: currentAppUserID,
newDictToCache: newDictToCache)
} else {
postSubscriberAttributes(newData: newData,
network: network,
appUserID: currentAppUserID,
newDictToCache: newDictToCache)
}
postSubscriberAttributes(newData: newData,
network: network,
appUserID: currentAppUserID,
newDictToCache: newDictToCache)
}
}

func postAppleSearchAdsAttributionIfNeeded() {
guard attributionFetcher.isAuthorizedToPostSearchAds else {
// should match OS availability in https://developer.apple.com/documentation/ad_services
@available(iOS 14.3, macOS 11.1, macCatalyst 14.3, *)
func postAdServicesTokenIfNeeded() {
let latestTokenSent = latestNetworkIdAndAdvertisingIdentifierSent(network: .adServices)
guard latestTokenSent == nil else {
return
}

let latestIdsSent = latestNetworkIdAndAdvertisingIdentifierSent(network: .appleSearchAds)
guard latestIdsSent == nil else {
guard let attributionToken = attributionFetcher.adServicesToken else {
return
}

attributionFetcher.afficheClientAttributionDetails { attributionDetails, error in
guard let attributionDetails = attributionDetails,
error == nil else {
return
}

let attributionDetailsValues = Array(attributionDetails.values)
let firstAttributionDict = attributionDetailsValues.first as? [String: NSObject]

guard let hasIad = firstAttributionDict?["iad-attribution"] as? NSNumber,
hasIad.boolValue == true else {
return
}

self.post(attributionData: attributionDetails, fromNetwork: .appleSearchAds, networkUserId: nil)
}
Logger.debug("Logging attribution token for now to avoid lint warning: \(attributionToken)")
// post
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe leave this as a TODO? it's okay if lint fails since we're not merging this in main yet. That way we ensure it doesn't slip through the cracks.

}

func postPostponedAttributionDataIfNeeded() {
Expand Down Expand Up @@ -162,19 +142,6 @@ class AttributionPoster {
return cachedDict[networkID]
}

private func postSearchAds(newData: [String: Any],
network: AttributionNetwork,
appUserID: String,
newDictToCache: [String: String]) {
backend.post(attributionData: newData, network: network, appUserID: appUserID) { error in
guard error == nil else {
return
}

self.deviceCache.set(latestNetworkAndAdvertisingIdsSent: newDictToCache, appUserID: appUserID)
}
}

private func postSubscriberAttributes(newData: [String: Any],
network: AttributionNetwork,
appUserID: String,
Expand Down
4 changes: 0 additions & 4 deletions Sources/Attribution/AttributionTypeFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ import Foundation

class AttributionTypeFactory {

func afficheClientProxy() -> AfficheClientProxy? {
return AfficheClientProxy.afficheClientClass == nil ? nil : AfficheClientProxy()
}

func atFollowingProxy() -> TrackingManagerProxy? {
return TrackingManagerProxy.trackingClass == nil ? nil : TrackingManagerProxy()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Most features require configuring the SDK before using it.
- ``Purchases/finishTransactions``
- ``Purchases/invalidateCustomerInfoCache()``
- ``Purchases/forceUniversalAppStore``
- ``Purchases/automaticAppleSearchAdsAttributionCollection``
- ``Purchases/automaticAdServicesAttributionTokenCollection``
- ``Purchases/proxyURL``
- ``Purchases/verboseLogs``
- ``Purchases/verboseLogHandler``
Expand Down
6 changes: 0 additions & 6 deletions Sources/Identity/CustomerInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ import Foundation
return mostRecentDate
}

/// Returns all product IDs of the non-subscription purchases a user has made.
@available(*, deprecated, message: "use nonSubscriptionTransactions")
@objc public var nonConsumablePurchases: Set<String> {
Set(self.nonSubscriptionTransactions.map { $0.productIdentifier })
}

/**
* Returns all the non-subscription purchases a user has made.
* The purchases are ordered by purchase date in ascending order.
Expand Down
Loading