Skip to content

Commit

Permalink
[MBL-870] Fix GraphSchema with an Update to AppTrackingTransparency (#…
Browse files Browse the repository at this point in the history
…1832)

* updated app track transparency to be useful after multiple foreground/background transitions

Also sets up our AppTrackingTransparency for use with `triggerThirdPartyEvents` which is going to be a replacement for `triggerCapiEvents` (once we get the go ahead from Leigh that all the bugs are fixed)

* fixed existing tests to pass under new appTrackingTransparency class

* formatting

* added new test cases
  • Loading branch information
msadoon committed Jun 29, 2023
1 parent 2c5ef89 commit e629b80
Show file tree
Hide file tree
Showing 28 changed files with 3,035 additions and 786 deletions.
8 changes: 8 additions & 0 deletions Kickstarter-iOS/AppDelegate.swift
Expand Up @@ -295,6 +295,14 @@ internal final class AppDelegate: UIResponder, UIApplicationDelegate {
return self.viewModel.outputs.applicationDidFinishLaunchingReturnValue
}

func applicationDidBecomeActive(_: UIApplication) {
self.viewModel.inputs.applicationActive(state: true)
}

func applicationWillResignActive(_: UIApplication) {
self.viewModel.inputs.applicationActive(state: false)
}

func applicationWillEnterForeground(_: UIApplication) {
self.viewModel.inputs.applicationWillEnterForeground()
}
Expand Down
32 changes: 23 additions & 9 deletions Kickstarter-iOS/AppDelegateViewModel.swift
Expand Up @@ -33,6 +33,9 @@ public protocol AppDelegateViewModelInputs {
/// Call when the application enters background.
func applicationDidEnterBackground()

/// Call when the application becomes active `UIApplicationStateActive` and `UIApplicationStateInactive`
func applicationActive(state: Bool)

/// Call when the aplication receives memory warning from the system.
func applicationDidReceiveMemoryWarning()

Expand Down Expand Up @@ -786,18 +789,24 @@ public final class AppDelegateViewModel: AppDelegateViewModelType, AppDelegateVi
.skipNil()
.map { _ in .displayInAppMessageNow }

self.requestATTrackingAuthorizationStatus = self.applicationLaunchOptionsProperty.signal
.skipNil()
self.requestATTrackingAuthorizationStatus = Signal
.combineLatest(
self.applicationDidFinishLaunchingReturnValueProperty.signal.ignoreValues(),
self.applicationActiveProperty.signal
)
.map(second)
.skipRepeats()
.ksr_delay(.seconds(1), on: AppEnvironment.current.scheduler)
.filter(isTrue)
.map { _ in
guard featureConsentManagementDialogEnabled() else { return }

let appTrackingTransparency = AppTrackingTransparency()

let authorizationStatus = appTrackingTransparency.authorizationStatus()
let advertisingIdentifier = appTrackingTransparency.advertisingIdentifier(authorizationStatus)
guard let _ = AppEnvironment.current.appTrackingTransparency.advertisingIdentifier else {
if AppEnvironment.current.appTrackingTransparency.shouldRequestAuthorizationStatus(),
featureConsentManagementDialogEnabled() {
AppEnvironment.current.appTrackingTransparency.requestAndSetAuthorizationStatus()
}

AppEnvironment.updateAdvertisingIdentifer(advertisingIdentifier)
return
}
}
}

Expand Down Expand Up @@ -826,6 +835,11 @@ public final class AppDelegateViewModel: AppDelegateViewModelType, AppDelegateVi
self.applicationWillEnterForegroundProperty.value = ()
}

fileprivate let applicationActiveProperty = MutableProperty<Bool>(false)
public func applicationActive(state: Bool) {
self.applicationActiveProperty.value = state
}

fileprivate let applicationDidEnterBackgroundProperty = MutableProperty(())
public func applicationDidEnterBackground() {
self.applicationDidEnterBackgroundProperty.value = ()
Expand Down
112 changes: 107 additions & 5 deletions Kickstarter-iOS/AppDelegateViewModelTests.swift
Expand Up @@ -2420,14 +2420,116 @@ final class AppDelegateViewModelTests: TestCase {
}
}

func testRequestATTrackingAuthorizationStatus_CalledOnceOnDidFinishLaunching() {
self.requestATTrackingAuthorizationStatus.assertValueCount(0)
func testRequestATTrackingAuthorizationStatus_WhenAppBecomesActive_WhenAdvertisingIdentifierNil_WhenConsentManagementFeatureFlagOn_WhenShouldRequestAuthorizationStatusTrue_RequestAllowed_ShowsConsentDialogAndUpdatesAdId() {
let mockRemoteConfigClient = MockRemoteConfigClient()
|> \.features .~ [
RemoteConfigFeature.consentManagementDialogEnabled.rawValue: true
]
let appTrackingTransparency = MockAppTrackingTransparency()
appTrackingTransparency.requestAndSetAuthorizationStatusFlag = true
appTrackingTransparency.shouldRequestAuthStatus = true

self.vm.inputs.applicationDidFinishLaunching(application: UIApplication.shared, launchOptions: nil)
withEnvironment(
appTrackingTransparency: appTrackingTransparency,
remoteConfigClient: mockRemoteConfigClient
) {
self.requestATTrackingAuthorizationStatus.assertValueCount(0)

XCTAssertNil(appTrackingTransparency.advertisingIdentifier)

self.vm.inputs.applicationActive(state: false)
self.vm.inputs.applicationDidFinishLaunching(application: UIApplication.shared, launchOptions: nil)
self.vm.inputs.applicationActive(state: true)

self.scheduler.advance(by: .seconds(1))

XCTAssertEqual(appTrackingTransparency.advertisingIdentifier, "advertisingIdentifier")
self.requestATTrackingAuthorizationStatus.assertValueCount(1)
}
}

func testRequestATTrackingAuthorizationStatus_WhenAppBecomesActive_WhenAdvertisingIdentifierNil_WhenConsentManagementFeatureFlagOn_WhenShouldRequestAuthorizationStatusFalse_RequestAllowed_DoesNotShowConsentDialogAndDoesNotUpdateAdId() {
let mockRemoteConfigClient = MockRemoteConfigClient()
|> \.features .~ [
RemoteConfigFeature.consentManagementDialogEnabled.rawValue: true
]
let appTrackingTransparency = MockAppTrackingTransparency()
appTrackingTransparency.requestAndSetAuthorizationStatusFlag = true
appTrackingTransparency.shouldRequestAuthStatus = false

withEnvironment(
appTrackingTransparency: appTrackingTransparency,
remoteConfigClient: mockRemoteConfigClient
) {
self.requestATTrackingAuthorizationStatus.assertValueCount(0)

XCTAssertNil(appTrackingTransparency.advertisingIdentifier)

self.vm.inputs.applicationActive(state: false)
self.vm.inputs.applicationDidFinishLaunching(application: UIApplication.shared, launchOptions: nil)
self.vm.inputs.applicationActive(state: true)

self.scheduler.advance(by: .seconds(1))

XCTAssertNil(appTrackingTransparency.advertisingIdentifier)
self.requestATTrackingAuthorizationStatus.assertValueCount(1)
}
}

func testRequestATTrackingAuthorizationStatus_WhenAppBecomesActive_WhenAdvertisingIdentifierNil_WhenConsentManagementFeatureFlagOn_WhenShouldRequestAuthorizationStatusTrue_RequestDenied_DoesNotShowConsentDialogAndDoesNotUpdateAdId() {
let mockRemoteConfigClient = MockRemoteConfigClient()
|> \.features .~ [
RemoteConfigFeature.consentManagementDialogEnabled.rawValue: true
]
let appTrackingTransparency = MockAppTrackingTransparency()
appTrackingTransparency.requestAndSetAuthorizationStatusFlag = false
appTrackingTransparency.shouldRequestAuthStatus = true

withEnvironment(
appTrackingTransparency: appTrackingTransparency,
remoteConfigClient: mockRemoteConfigClient
) {
self.requestATTrackingAuthorizationStatus.assertValueCount(0)

XCTAssertNil(appTrackingTransparency.advertisingIdentifier)

self.vm.inputs.applicationActive(state: false)
self.vm.inputs.applicationDidFinishLaunching(application: UIApplication.shared, launchOptions: nil)
self.vm.inputs.applicationActive(state: true)

self.scheduler.advance(by: .seconds(1))

XCTAssertNil(appTrackingTransparency.advertisingIdentifier)
self.requestATTrackingAuthorizationStatus.assertValueCount(1)
}
}

func testRequestATTrackingAuthorizationStatus_WhenAppBecomesActive_WhenAdvertisingIdentifierNil_WhenConsentManagementFeatureFlagOff_WhenShouldRequestAuthorizationStatusTrue_RequestAllowed_DoesNotShowConsentDialogAndDoesNotUpdateAdId() {
let mockRemoteConfigClient = MockRemoteConfigClient()
|> \.features .~ [
RemoteConfigFeature.consentManagementDialogEnabled.rawValue: false
]
let appTrackingTransparency = MockAppTrackingTransparency()
appTrackingTransparency.requestAndSetAuthorizationStatusFlag = true
appTrackingTransparency.shouldRequestAuthStatus = true

self.scheduler.advance(by: .seconds(1))
withEnvironment(
appTrackingTransparency: appTrackingTransparency,
remoteConfigClient: mockRemoteConfigClient
) {
self.requestATTrackingAuthorizationStatus.assertValueCount(0)

self.requestATTrackingAuthorizationStatus.assertValueCount(1)
XCTAssertNil(appTrackingTransparency.advertisingIdentifier)

self.vm.inputs.applicationActive(state: false)
self.vm.inputs.applicationDidFinishLaunching(application: UIApplication.shared, launchOptions: nil)
self.vm.inputs.applicationActive(state: true)

self.scheduler.advance(by: .seconds(1))

XCTAssertNil(appTrackingTransparency.advertisingIdentifier)
self.requestATTrackingAuthorizationStatus.assertValueCount(1)
}
}

func testPresentViewController_BrazeInAppNotificationDeeplink_ProjectCommentThread_Success() {
Expand Down
10 changes: 3 additions & 7 deletions Kickstarter.xcodeproj/project.pbxproj
Expand Up @@ -483,7 +483,6 @@
606C45F529FACD78001BA067 /* RemoteConfigFeature+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606C45F429FACD78001BA067 /* RemoteConfigFeature+Helpers.swift */; };
606C45F829FACD9F001BA067 /* RemoteConfigFeature+HelpersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606C45F629FACD94001BA067 /* RemoteConfigFeature+HelpersTests.swift */; };
606C45FA29FACE17001BA067 /* MockRemoteConfigClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606C45F929FACE17001BA067 /* MockRemoteConfigClient.swift */; };
606F214429799A1200BA5CDF /* ATTrackingAuthorizationStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606F214229799A0000BA5CDF /* ATTrackingAuthorizationStatus.swift */; };
606F215C299D3FA900BA5CDF /* GraphAPI.TriggerCapiEventInput+TriggerCapiEventInputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606F215A299D3EF800BA5CDF /* GraphAPI.TriggerCapiEventInput+TriggerCapiEventInputTests.swift */; };
606F215E299D414800BA5CDF /* GraphAPI.TriggerCapiEventInput+TriggerCapiEventInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606F215D299D414800BA5CDF /* GraphAPI.TriggerCapiEventInput+TriggerCapiEventInput.swift */; };
606F2166299D456D00BA5CDF /* TriggerCapiEvent.graphql in Sources */ = {isa = PBXBuildFile; fileRef = 606F2165299D456D00BA5CDF /* TriggerCapiEvent.graphql */; };
Expand Down Expand Up @@ -2066,7 +2065,6 @@
606C45F429FACD78001BA067 /* RemoteConfigFeature+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteConfigFeature+Helpers.swift"; sourceTree = "<group>"; };
606C45F629FACD94001BA067 /* RemoteConfigFeature+HelpersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteConfigFeature+HelpersTests.swift"; sourceTree = "<group>"; };
606C45F929FACE17001BA067 /* MockRemoteConfigClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRemoteConfigClient.swift; sourceTree = "<group>"; };
606F214229799A0000BA5CDF /* ATTrackingAuthorizationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ATTrackingAuthorizationStatus.swift; sourceTree = "<group>"; };
606F215A299D3EF800BA5CDF /* GraphAPI.TriggerCapiEventInput+TriggerCapiEventInputTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GraphAPI.TriggerCapiEventInput+TriggerCapiEventInputTests.swift"; sourceTree = "<group>"; };
606F215D299D414800BA5CDF /* GraphAPI.TriggerCapiEventInput+TriggerCapiEventInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GraphAPI.TriggerCapiEventInput+TriggerCapiEventInput.swift"; sourceTree = "<group>"; };
606F2165299D456D00BA5CDF /* TriggerCapiEvent.graphql */ = {isa = PBXFileReference; lastKnownFileType = text; path = TriggerCapiEvent.graphql; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5919,7 +5917,6 @@
children = (
8A788F25263C80C500A89DAE /* Vendor */,
6018626529A9194600EA2842 /* AppTrackingTransparency.swift */,
606F214229799A0000BA5CDF /* ATTrackingAuthorizationStatus.swift */,
6018626729A91B8C00EA2842 /* FacebookCAPIEventName.swift */,
8A23203025B0ECF700B940C3 /* IdentifyingTrackingClient.swift */,
8A213CE8239EAEA400BBB4C7 /* KSRAnalytics.swift */,
Expand Down Expand Up @@ -7675,7 +7672,6 @@
8A6C58932475E5950098D5A2 /* UIRefreshControl+StartRefreshing.swift in Sources */,
D7A37CCF1E2FF93D00EA066D /* SearchEmptyStateCellViewModel.swift in Sources */,
80D73AF61D50F1A60099231F /* Navigation.swift in Sources */,
606F214429799A1200BA5CDF /* ATTrackingAuthorizationStatus.swift in Sources */,
473DE012273C502F0033331D /* ProjectRisksCellViewModel.swift in Sources */,
A755115F1C8642C3005355CF /* Format.swift in Sources */,
598D96CB1D42AE85003F3F66 /* ActivitySampleProjectCellViewModel.swift in Sources */,
Expand Down Expand Up @@ -9201,8 +9197,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = "app-icon-beta";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Kickstarter-iOS/Beta.entitlements";
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5DAN4UM3NC;
ENABLE_MODULE_VERIFIER = NO;
Expand All @@ -9219,7 +9215,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.kickstarter.kickstarter.beta;
PRODUCT_MODULE_NAME = Kickstarter_iOS;
PRODUCT_NAME = KickBeta;
PROVISIONING_PROFILE_SPECIFIER = "match InHouse com.kickstarter.kickstarter.beta";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos;
VALIDATE_WORKSPACE = NO;
};
Expand Down
Expand Up @@ -5,17 +5,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "a5f16ba68913840ee5df91b8dc06f5cc063579de",
"version" : "1.2021110200.0"
"revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c",
"version" : "1.2022062300.0"
}
},
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
"version" : "5.6.2"
"revision" : "bc268c28fb170f494de9e9927c371b8342979ece",
"version" : "5.7.1"
}
},
{
Expand Down Expand Up @@ -50,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Appboy/appboy-segment-ios",
"state" : {
"revision" : "08a061113e8a7e835e6bc9c0e1629d94e63aa29e",
"version" : "4.6.0"
"revision" : "dc659b70f2b432f8c70fda869b27eac2acb688fe",
"version" : "4.6.1"
}
},
{
Expand Down Expand Up @@ -86,26 +86,26 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "4961c0b7eb5d794e47a58dbfdd258b4beca4263a",
"version" : "10.9.0"
"revision" : "e700a8f40c87c31cab7984875fcc1225d96b25bf",
"version" : "10.11.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "9209b95a2593985569918e5e5ee2bf4ef8ff3640",
"version" : "10.9.0"
"revision" : "62e3a0c09a75e2637f5300d46f05a59313f1c286",
"version" : "10.11.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "5056b15c5acbb90cd214fe4d6138bdf5a740e5a8",
"version" : "9.2.0"
"revision" : "7874c1b48cbffd086ce8a052c4be873a78613775",
"version" : "9.2.3"
}
},
{
Expand All @@ -122,17 +122,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "df37f6af8a273bc687e3166843ed86007de57d78",
"version" : "1.44.0"
"revision" : "f1b366129d1125be7db83247e003fc333104b569",
"version" : "1.50.2"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "d4289da23e978f37c344ea6a386e5546e2466294",
"version" : "2.1.0"
"revision" : "d415594121c9e8a4f9d79cecee0965cf35e74dbd",
"version" : "3.1.1"
}
},
{
Expand Down Expand Up @@ -167,8 +167,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/onevcat/Kingfisher",
"state" : {
"revision" : "af4be924ad984cf4d16f4ae4df424e79a443d435",
"version" : "7.6.2"
"revision" : "3f6992b5cd3143e83b02300ea59c400d4cf0747a",
"version" : "7.8.0"
}
},
{
Expand Down Expand Up @@ -212,8 +212,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "3e4e743631e86c8c70dbc6efdc7beaa6e90fd3bb",
"version" : "2.1.1"
"revision" : "ec957ccddbcc710ccc64c9dcbd4c7006fcf8b73a",
"version" : "2.2.0"
}
},
{
Expand All @@ -239,8 +239,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/SDWebImage/SDWebImage.git",
"state" : {
"revision" : "6c6b951845a520fa7e8356e28adb5339c0f008d3",
"version" : "5.15.0"
"revision" : "c51ba84499268ea3020e6aee9e229c0f56b9d924",
"version" : "5.16.0"
}
},
{
Expand Down Expand Up @@ -293,8 +293,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "b8230909dedc640294d7324d37f4c91ad3dcf177",
"version" : "1.20.1"
"revision" : "f25867a208f459d3c5a06935dceb9083b11cd539",
"version" : "1.22.0"
}
},
{
Expand All @@ -311,8 +311,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/scinfu/SwiftSoup",
"state" : {
"revision" : "6778575285177365cbad3e5b8a72f2a20583cfec",
"version" : "2.4.3"
"revision" : "0e96a20ffd37a515c5c963952d4335c89bed50a6",
"version" : "2.6.0"
}
}
],
Expand Down

0 comments on commit e629b80

Please sign in to comment.