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

Remote Messaging Framework for macOS #876

Merged
merged 40 commits into from
Jul 9, 2024
Merged

Remote Messaging Framework for macOS #876

merged 40 commits into from
Jul 9, 2024

Conversation

ayoy
Copy link
Contributor

@ayoy ayoy commented Jul 3, 2024

Please review the release process for BrowserServicesKit here.

Required:

Task/Issue URL: https://app.asana.com/0/72649045549333/1202913520695928/f
iOS PR: duckduckgo/iOS#3031
macOS PR: duckduckgo/macos-browser#2937
What kind of version bump will this require?: Major

Optional:

CC: @amddg44 @samsymons

Description:
This change adds support for RMF on macOS.

  • Remaining RMF functionality previously present in iOS codebase was moved to BSK
    (most importantly RemoteMessagingProcessing protocol).
  • remoteMessaging feature flag was added.
  • RemoteMessagingConfigMatcherProviding protocol was added – it's implemented by client apps
    to create and return the config matcher filled with current values of matching attributes.
  • Support for platform-specific user attribute matchers was added. CommonUserAttributeMatcher was added
    to handle attributes that are the supported on both iOS and macOS. Mobile and Desktop user attribute matcher
    structs were added to support platform-specific attributes (currently it's only isWidgetInstalled for iOS).
  • RemoteMessagingRequest was replaced with RemoteMessagingConfigFetcher that uses Configuration Store
    and supports Etags.
  • All RMF tests (and mocks) were moved out of BrowserServicesKit(Tests) into RemoteMessagingTestsUtils
    and RemoteMessagingTests (that also mandated creating EmailTestsUtils).
  • RemoteMessagingStore was updated to keep a reference to the database and create contexts on demand,
    to ensure it always gets the freshest data from the persistent store.

Steps to test this PR:
See iOS PR for steps to test.

OS Testing:

  • iOS 14
  • iOS 15
  • iOS 16
  • macOS 10.15
  • macOS 11
  • macOS 12

Internal references:

Software Engineering Expectations
Technical Design Template

@ayoy ayoy self-assigned this Jul 3, 2024
Package.swift Outdated
@@ -166,6 +166,12 @@ let package = Package(
.define("DEBUG", .when(configuration: .debug))
]
),
.target(
name: "EmailTestsUtils",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

EmailTestUtils contains EmailManager related mocks that are moved over from BrowserServicesKit so as to avoid making RemoteMessagingTests dependent on BrowserServicesKit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

EDIT: I renamed it as BrowserServicesKitTestsUtils to indicate that these are mocks of classes from BrowserServicesKit module. Also moved there StatisticsStore, Variant and VariantManager mocks.

@@ -272,6 +279,13 @@ let package = Package(
.define("DEBUG", .when(configuration: .debug))
]
),
.target(
name: "RemoteMessagingTestsUtils",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

RemoteMessagingTestsUtils contains mocks for RemoteMessaging related protocols

@@ -400,7 +414,7 @@ let package = Package(
name: "BrowserServicesKitTests",
dependencies: [
"BrowserServicesKit",
"RemoteMessaging", // Move tests later (lots of test dependencies in BSK)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

RemoteMessaging has its own tests target now.

@@ -184,6 +184,31 @@ public struct BookmarkUtils {
return result.compactMap { $0[#keyPath(BookmarkEntity.title)] as? String }
}

public static func numberOfBookmarks(in context: NSManagedObjectContext) -> Int {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These 2 functions were used exclusively for RMF in the iOS repo. Now they're in BookmarkUtils and can be reused by macOS.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The 2 files in EmailTestUtils are moved 1:1 from EmailManagerTests.swift in BrowserServicesKit


}

public struct CommonUserAttributeMatcher: AttributeMatching {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Common matcher defines matching mechanism for all attributes that are supported by both iOS and macOS.

@@ -27,7 +27,7 @@ private enum RuleAttributes {
static let since = "since"
}

protocol MatchingAttribute {
public protocol MatchingAttribute {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

AttributeMatching was made public. Since it uses MatchingAttribute, this one needs to be made public as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has been replaced by RemoteMessagingConfigFetcher that uses ConfigurationFetcher/Store.

let context: NSManagedObjectContext
let database: CoreDataDatabase
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change makes the functions in RemoteMessagingStore create context as needed, ensuring that they have the fresh data all the time. We use a similar approach in Sync Bookmarks provider.

@@ -19,12 +19,17 @@
import Foundation
import RemoteMessaging

class MockRemoteMessagePercentileStore: RemoteMessagingPercentileStoring {
public class MockRemoteMessagePercentileStore: RemoteMessagingPercentileStoring {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mocks moved to a dedicated mocks target (RemoteMessagingTestsUtils in this case) need to be made public so that the module could be imported without @testable

@ayoy ayoy marked this pull request as ready for review July 4, 2024 07:19
@ayoy ayoy requested a review from jaceklyp July 4, 2024 07:19
@ayoy ayoy assigned jaceklyp and unassigned ayoy Jul 4, 2024
Copy link
Contributor

@jaceklyp jaceklyp left a comment

Choose a reason for hiding this comment

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

looks solid :)

return
}
do {
let statusResponse = try await configurationFetcher.fetchRemoteMessagingConfig()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this might be some old naming, where this func used to return status code or something? But now it is config indeed, isn't it?

I would actually suggest a couple of helper func here to make this functionality here more readable, e.g:
private func fetchRemoteMessagingConfig() async throws -> RemoteMessagingConfig
private func processRemoteMessagingConfig(_ config: RemoteMessagingConfig, using store: RemoteMessagingStoring) async throws -> ProcessedResult

where first func just implements: try await configurationFetcher.fetchRemoteMessagingConfig()
second: pretty much old the rest

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Yeah, I agree it's a bit messy. This function used to be called fetchRemoteMessages. Now I've renamed it but failed to notice that there are two fetchRemoteMessagingConfig functions being called here, from 2 different protocols 🤦 and they do 2 different things, first one fetches the config from the server, and the second one fetches config metadata from the local database. 😬

Let me try to make it right :)

Comment on lines 92 to 94
if let processorResult = processor.process(jsonRemoteMessagingConfig: statusResponse, currentConfig: config) {
store.saveProcessedResult(processorResult)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

perhaps this should be guard that throws some processing failed error? otherwise we're not getting any debug info what happened

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method calls a bunch of Store private methods inside, each of which can throw an error that is handled by sending a pixel (via EventMapping) and printing a console log. So I think it's acceptable. I agree that it's not ideal though.

Also, this function's error is silenced at the call site (with try? – again probably acceptable in this specific case because it effectively silences only network and JSON parsing errors). I may propose an update to this piece of code but it has been a status quo for past 2 years so I'd rather handle it separately.

Copy link
Contributor

Choose a reason for hiding this comment

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

don't bother, makes sense to me

@ayoy ayoy merged commit bfabf45 into main Jul 9, 2024
7 checks passed
@ayoy ayoy deleted the dominik/rmf-macos branch July 9, 2024 06:21
samsymons added a commit that referenced this pull request Jul 19, 2024
* main:
  Remove unused VPN session utilities (#898)
  Add new deprecated Mac remote message attribute. (#903)
  Resetting all state for the VPN will cancel the tunnel and stop the monitors (#900)
  Add support for skipping sending usage pixels for remote messages (#902)
  Bump Tests/BrowserServicesKitTests/Resources/privacy-reference-tests (#896)
  Removes the listen port from the wireguard client (#901)
  Be explicit when performing developer redirects (#884)
  C-S-S cross origin fixes
  Update C-S-S version (#892)
  Add a debug menu action to reset Remote Messages on macOS (#891)
  Add desktop specific RMF attributes (#883)
  Upload exception message to Sentry (#856)
  Add locale to broken site report (#889)
  Add new subfeature for duckplayer (#885)
  Swiftlint refactoring (#882)
  Remote Messaging Framework for macOS (#876)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants