diff --git a/Podfile b/Podfile index f8e73a0cc86c..90ba07faffc3 100644 --- a/Podfile +++ b/Podfile @@ -50,8 +50,7 @@ def wordpress_ui end def wordpress_kit - # Anything compatible with 8.9, starting from 8.9.1 which has a breaking change fix - pod 'WordPressKit', '~> 9.0', '>= 9.0.2' + pod 'WordPressKit', '~> 9.0', '>= 9.0.3' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: 'trunk' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' diff --git a/Podfile.lock b/Podfile.lock index daab523c6c77..35f8ccf85a0c 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -70,7 +70,7 @@ PODS: - WordPressKit (~> 9.0.0) - WordPressShared (~> 2.1-beta) - WordPressUI (~> 1.7-beta) - - WordPressKit (9.0.2): + - WordPressKit (9.0.3): - Alamofire (~> 4.8.0) - NSObject-SafeExpectations (~> 0.0.4) - UIDeviceIdentifier (~> 2.0) @@ -121,7 +121,7 @@ DEPENDENCIES: - SwiftLint (~> 0.50) - WordPress-Editor-iOS (~> 1.19.9) - WordPressAuthenticator (>= 8.0.1, ~> 8.0) - - WordPressKit (>= 9.0.2, ~> 9.0) + - WordPressKit (>= 9.0.3, ~> 9.0) - WordPressShared (~> 2.2) - WordPressUI (~> 1.15) - ZendeskSupportSDK (= 5.3.0) @@ -218,7 +218,7 @@ SPEC CHECKSUMS: WordPress-Aztec-iOS: fbebd569c61baa252b3f5058c0a2a9a6ada686bb WordPress-Editor-iOS: bda9f7f942212589b890329a0cb22547311749ef WordPressAuthenticator: fd2e1d340680faffffd9d675fc2df5ed19e26ea2 - WordPressKit: 23d0ffb43f2ccdad2debd6799e62d39790a5ffad + WordPressKit: 4d41fd70b83876ee5db4617868767b33f3ae1bc4 WordPressShared: 87f3ee89b0a3e83106106f13a8b71605fb8eb6d2 WordPressUI: a491454affda3b0fb812812e637dc5e8f8f6bd06 wpxmlrpc: 68db063041e85d186db21f674adf08d9c70627fd @@ -231,6 +231,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: d170fa8e270b2a32bef9dcdcabff5b8f1a5deced -PODFILE CHECKSUM: 9567ce349333fd257fea44c201727b4180efb961 +PODFILE CHECKSUM: d7a42312b8249374e94aecb6021ee06d137c7cfa COCOAPODS: 1.14.2 diff --git a/WordPress/Classes/Stores/RemoteFeatureFlagStore.swift b/WordPress/Classes/Stores/RemoteFeatureFlagStore.swift index f1484ca9ba19..4091b29a6e19 100644 --- a/WordPress/Classes/Stores/RemoteFeatureFlagStore.swift +++ b/WordPress/Classes/Stores/RemoteFeatureFlagStore.swift @@ -17,6 +17,19 @@ class RemoteFeatureFlagStore { return queue }() + /// The `deviceID` ensures we retain a stable set of Feature Flags between updates. If there are staged rollouts or other dynamic changes + /// happening server-side we don't want our flags to change on each fetch, so we provide an anonymous ID to manage this. + public var deviceID: String { + guard let deviceID = persistenceStore.string(forKey: Constants.DeviceIdKey) else { + DDLogInfo("🚩 Unable to find existing device ID – generating a new one") + let newID = UUID().uuidString + persistenceStore.set(newID, forKey: Constants.DeviceIdKey) + return newID + } + + return deviceID + } + init(queue: DispatchQueue = .remoteFeatureFlagStoreQueue, persistenceStore: UserPersistentRepository = UserDefaults.standard) { self.queue = queue @@ -63,19 +76,6 @@ extension RemoteFeatureFlagStore { typealias FetchCallback = () -> Void - /// The `deviceID` ensures we retain a stable set of Feature Flags between updates. If there are staged rollouts or other dynamic changes - /// happening server-side we don't want out flags to change on each fetch, so we provide an anonymous ID to manage this. - private var deviceID: String { - guard let deviceID = persistenceStore.string(forKey: Constants.DeviceIdKey) else { - DDLogInfo("🚩 Unable to find existing device ID – generating a new one") - let newID = UUID().uuidString - persistenceStore.set(newID, forKey: Constants.DeviceIdKey) - return newID - } - - return deviceID - } - /// The local cache stores feature flags between runs so that the most recently fetched set are ready to go as soon as this object is instantiated. private var cache: [String: Bool] { get { diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Models/DashboardCard.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Models/DashboardCard.swift index 1068063e1ab6..b349e248bb95 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Models/DashboardCard.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Models/DashboardCard.swift @@ -147,21 +147,8 @@ enum DashboardCard: String, CaseIterable { } } - static func shouldShowDynamicCard( - for blog: Blog, - payload: DashboardDynamicCardModel.Payload, - remoteFeatureFlagStore: RemoteFeatureFlagStore, - isJetpack: Bool = AppConfiguration.isJetpack - ) -> Bool { - let remoteFeatureFlagEnabled = { - guard let key = payload.remoteFeatureFlag else { - return true - } - return remoteFeatureFlagStore.value(for: key) ?? false - }() - return isJetpack - && RemoteDashboardCard.dynamic.supported(by: blog) - && remoteFeatureFlagEnabled + static func shouldShowDynamicCard(for blog: Blog, isJetpack: Bool = AppConfiguration.isJetpack) -> Bool { + isJetpack && RemoteDashboardCard.dynamic.supported(by: blog) } private func shouldShowRemoteCard(apiResponse: BlogDashboardRemoteEntity?) -> Bool { diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardRemoteEntity.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardRemoteEntity.swift index 864184d32edb..3adc0af744b9 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardRemoteEntity.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardRemoteEntity.swift @@ -57,7 +57,6 @@ extension BlogDashboardRemoteEntity { struct BlogDashboardDynamic: Decodable, Hashable { let id: String - let remoteFeatureFlag: String? let title: String? let featuredImage: String? let url: String? @@ -79,7 +78,6 @@ extension BlogDashboardRemoteEntity { private enum CodingKeys: String, CodingKey { case id case title - case remoteFeatureFlag = "remote_feature_flag" case featuredImage = "featured_image" case url case action diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardService.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardService.swift index a60b58666451..141056448f91 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardService.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Service/BlogDashboardService.swift @@ -7,10 +7,10 @@ final class BlogDashboardService { private let persistence: BlogDashboardPersistence private let postsParser: BlogDashboardPostsParser private let repository: UserPersistentRepository - private let remoteFeatureFlagStore: RemoteFeatureFlagStore private let isJetpack: Bool private let isDotComAvailable: Bool private let shouldShowJetpackFeatures: Bool + private let remoteFeatureFlagStore: RemoteFeatureFlagStore init( managedObjectContext: NSManagedObjectContext, @@ -42,8 +42,9 @@ final class BlogDashboardService { } let cardsToFetch: [String] = DashboardCard.RemoteDashboardCard.allCases.filter {$0.supported(by: blog)}.map { $0.rawValue } + let deviceID = remoteFeatureFlagStore.deviceID - remoteService.fetch(cards: cardsToFetch, forBlogID: dotComID, success: { [weak self] cardsDictionary in + remoteService.fetch(cards: cardsToFetch, forBlogID: dotComID, deviceId: deviceID, success: { [weak self] cardsDictionary in guard let cardsDictionary = self?.parseCardsForLocalContent(cardsDictionary, blog: blog) else { failure?([]) @@ -187,8 +188,6 @@ private extension BlogDashboardService { let model = DashboardDynamicCardModel(payload: payload, dotComID: dotComID) let shouldShow = DashboardCard.shouldShowDynamicCard( for: blog, - payload: payload, - remoteFeatureFlagStore: remoteFeatureFlagStore, isJetpack: isJetpack ) guard shouldShow, personalizationService.isEnabled(model) else { diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift index 6d9fd7a47256..6658186f53e8 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift @@ -26,7 +26,7 @@ final class CompliancePopoverCoordinator: CompliancePopoverCoordinatorProtocol { } func presentIfNeeded() { - guard FeatureFlag.compliancePopover.enabled, !defaults.didShowCompliancePopup else { + guard FeatureFlag.compliancePopover.enabled/*, !defaults.didShowCompliancePopup */else { return } complianceService.getIPCountryCode { [weak self] result in diff --git a/WordPress/WordPressTest/Dashboard/BlogDashboardServiceTests.swift b/WordPress/WordPressTest/Dashboard/BlogDashboardServiceTests.swift index fa82e6b4eea2..64257325fce6 100644 --- a/WordPress/WordPressTest/Dashboard/BlogDashboardServiceTests.swift +++ b/WordPress/WordPressTest/Dashboard/BlogDashboardServiceTests.swift @@ -12,6 +12,7 @@ class BlogDashboardServiceTests: CoreDataTestCase { private var repositoryMock: InMemoryUserDefaults! private var postsParserMock: BlogDashboardPostsParserMock! private var remoteFeatureFlagStore: RemoteFeatureFlagStoreMock! + private let featureFlags = FeatureFlagOverrideStore() private let wpComID = 123456 @@ -77,6 +78,7 @@ class BlogDashboardServiceTests: CoreDataTestCase { service.fetch(blog: blog) { _ in XCTAssertEqual(self.remoteServiceMock.didCallWithBlogID, self.wpComID) + XCTAssertEqual(self.remoteServiceMock.didCallWithDeviceId, "Test") XCTAssertEqual(self.remoteServiceMock.didRequestCards, ["todays_stats", "posts", "pages", "activity", "dynamic"]) expect.fulfill() } @@ -413,10 +415,9 @@ class BlogDashboardServiceTests: CoreDataTestCase { // MARK: - Dynamic Cards - func testCardsPresenceWhenAllCardsFeatureFlagsAreEnabled() throws { + func testCardsPresenceWhenFeatureFlagIsEnabled() throws { let expect = expectation(description: "2 dynamic cards at the top and one at the bottom should be present") remoteServiceMock.respondWith = .withMultipleDynamicCards - remoteFeatureFlagStore.enabledFeatureFlags = ["feature_flag_12345", "feature_flag_67890", "feature_flag_13579"] let blog = newTestBlog(id: wpComID, context: mainContext) @@ -430,28 +431,9 @@ class BlogDashboardServiceTests: CoreDataTestCase { waitForExpectations(timeout: 3, handler: nil) } - func testCardsPresenceWhenSomeCardsFeatureFlagsAreEnabled() throws { - let expect = expectation(description: "2 dynamic cards at the top and one at the bottom should be present") - remoteServiceMock.respondWith = .withMultipleDynamicCards - remoteFeatureFlagStore.enabledFeatureFlags = ["feature_flag_12345"] - remoteFeatureFlagStore.disabledFeatureFlag = ["feature_flag_67890"] - - let blog = newTestBlog(id: wpComID, context: mainContext) - - service.fetch(blog: blog) { cards in - let numberOfDynamicCards = cards.compactMap { $0.dynamic() }.count - XCTAssertEqual(numberOfDynamicCards, 1) - XCTAssertEqual(cards[0].dynamic()?.payload.id, "id_12345") - expect.fulfill() - } - - waitForExpectations(timeout: 3, handler: nil) - } - - func testCardsAbsenceWhenRemoteFeatureFlagIsDisabled() throws { + func testCardsAbsenceWhenFeatureFlagIsDisabled() throws { let expect = expectation(description: "No dynamic card should be present") remoteServiceMock.respondWith = .withMultipleDynamicCards - remoteFeatureFlagStore.enabledFeatureFlags = ["feature_flag_12345", "feature_flag_67890", "feature_flag_13579"] try featureFlags.override(RemoteFeatureFlag.dynamicDashboardCards, withValue: false) let blog = newTestBlog(id: wpComID, context: mainContext) @@ -468,7 +450,6 @@ class BlogDashboardServiceTests: CoreDataTestCase { func testDecodingWithDynamicCards() throws { let expect = expectation(description: "Dynamic card should be successfully decoded") remoteServiceMock.respondWith = .withOnlyOneDynamicCard - remoteFeatureFlagStore.enabledFeatureFlags = ["feature_flag_12345"] try featureFlags.override(RemoteFeatureFlag.dynamicDashboardCards, withValue: true) let blog = newTestBlog(id: wpComID, context: mainContext) @@ -479,7 +460,6 @@ class BlogDashboardServiceTests: CoreDataTestCase { let payload = card.payload let expected = BlogDashboardRemoteEntity.BlogDashboardDynamic( id: "id_12345", - remoteFeatureFlag: "feature_flag_12345", title: "Title 12345", featuredImage: "https://example.com/image12345", url: "https://example.com/url12345", @@ -546,11 +526,19 @@ class DashboardServiceRemoteMock: DashboardServiceRemote { var respondWith: Response = .withDraftAndSchedulePosts var didCallWithBlogID: Int? + var didCallWithDeviceId: String? var didRequestCards: [String]? - override func fetch(cards: [String], forBlogID blogID: Int, success: @escaping (NSDictionary) -> Void, failure: @escaping (Error) -> Void) { + override func fetch( + cards: [String], + forBlogID blogID: Int, + deviceId: String?, + success: @escaping (NSDictionary) -> Void, + failure: @escaping (Error) -> Void + ) { didCallWithBlogID = blogID didRequestCards = cards + didCallWithDeviceId = deviceId if let fileURL: URL = Bundle(for: BlogDashboardServiceTests.self).url(forResource: respondWith.rawValue, withExtension: nil), let data: Data = try? Data(contentsOf: fileURL), diff --git a/WordPress/WordPressTest/Dashboard/Dynamic Cards/BlogDashboardDynamicCardCoordinatorTests.swift b/WordPress/WordPressTest/Dashboard/Dynamic Cards/BlogDashboardDynamicCardCoordinatorTests.swift index 8895ac9aadb6..08916d578e08 100644 --- a/WordPress/WordPressTest/Dashboard/Dynamic Cards/BlogDashboardDynamicCardCoordinatorTests.swift +++ b/WordPress/WordPressTest/Dashboard/Dynamic Cards/BlogDashboardDynamicCardCoordinatorTests.swift @@ -156,7 +156,6 @@ final class BlogDashboardDynamicCardCoordinatorTests: XCTestCase { ) -> BlogDashboardDynamicCardCoordinator { let payload = DashboardDynamicCardModel.Payload( id: id, - remoteFeatureFlag: "default", title: "Domain Management", featuredImage: "https://wordpress.com", url: url, diff --git a/WordPress/WordPressTest/RemoteFeatureFlagStoreMock.swift b/WordPress/WordPressTest/RemoteFeatureFlagStoreMock.swift index 2cb63959fd14..3d85f211706e 100644 --- a/WordPress/WordPressTest/RemoteFeatureFlagStoreMock.swift +++ b/WordPress/WordPressTest/RemoteFeatureFlagStoreMock.swift @@ -14,7 +14,9 @@ class RemoteFeatureFlagStoreMock: RemoteFeatureFlagStore { var enabledFeatureFlags = Set() var disabledFeatureFlag = Set() - // MARK: - Access Remote Feature Flag Value + override var deviceID: String { + return "Test" + } override func value(for flagKey: String) -> Bool? { if enabledFeatureFlags.contains(flagKey) { diff --git a/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-multiple-dynamic-cards.json b/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-multiple-dynamic-cards.json index 51800bfc5d63..ac08a9ac021e 100644 --- a/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-multiple-dynamic-cards.json +++ b/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-multiple-dynamic-cards.json @@ -3,7 +3,6 @@ { "id": "id_12345", "title": "Title 12345", - "remote_feature_flag": "feature_flag_12345", "featured_image": "https://example.com/image12345", "url": "https://example.com/url12345", "action": "Action 12345", @@ -24,7 +23,6 @@ { "id": "id_67890", "title": "Title 67890", - "remote_feature_flag": "feature_flag_67890", "featured_image": "https://example.com/image67890", "url": "https://example.com/url67890", "action": "Action 67890", @@ -45,7 +43,6 @@ { "id": "id_13579", "title": "Title 13579", - "remote_feature_flag": "feature_flag_13579", "featured_image": "https://example.com/image13579", "url": "https://example.com/url13579", "action": "Action 13579", diff --git a/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-only-one-dynamic-card.json b/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-only-one-dynamic-card.json index 88e239b41588..bac329cfc092 100644 --- a/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-only-one-dynamic-card.json +++ b/WordPress/WordPressTest/Test Data/Dashboard/dashboard-200-with-only-one-dynamic-card.json @@ -3,7 +3,6 @@ { "id": "id_12345", "title": "Title 12345", - "remote_feature_flag": "feature_flag_12345", "featured_image": "https://example.com/image12345", "url": "https://example.com/url12345", "action": "Action 12345",