Skip to content

Commit

Permalink
Merge branch 'main' into sam/truncate-os-version
Browse files Browse the repository at this point in the history
# By Alessandro Boron (3) and others
# Via GitHub
* main:
  BSK - Add feature flag for SKAN API (#3356)
  Remove checking for negative attribution case  (#3355)
  C.S.S Bump (Via BSK) (#3346)
  Update Onboarding gradients (#3350)
  Alessandro/onboarding copy and private search options (#3349)
  Onboarding Intro - Add choose address bar position (#3340)
  Fix PrivacyDashboard appearance on entering foreground (#3345)
  Improve Data Store ID managing (#3335)

# Conflicts:
#	DuckDuckGo.xcodeproj/project.pbxproj
#	DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • Loading branch information
samsymons committed Sep 12, 2024
2 parents 3c38691 + 5798fce commit 66cb90c
Show file tree
Hide file tree
Showing 43 changed files with 1,480 additions and 195 deletions.
2 changes: 1 addition & 1 deletion Core/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ extension FeatureFlag: FeatureFlagSourceProviding {
case .newTabPageSections:
return .remoteDevelopment(.feature(.newTabPageImprovements))
case .duckPlayer:
return .remoteReleasable(.feature(.duckPlayer))
return .remoteReleasable(.subfeature(DuckPlayerSubfeature.enableDuckPlayer))
case .sslCertificatesBypass:
return .remoteReleasable(.subfeature(SslCertificatesSubfeature.allowBypass))
case .syncPromotionBookmarks:
Expand Down
82 changes: 82 additions & 0 deletions Core/NSAttributedStringExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,86 @@ extension NSAttributedString {
newString.setAttributes([.font: newFont], range: string.fullRange)
return newString
}

/// Creates a new `NSAttributedString` initialized with the characters and attributes of the current attributed string plus the specified font.
///
/// - Parameter font: The `UIFont` to apply to the text in the `NSAttributedString`.
/// - Returns: A new `NSAttributedString`initialized with characters and attributes of the current attributed string plus the specified font.
public func withFont(_ font: UIFont) -> NSAttributedString {
with(attribute: .font, value: font)
}

/// Creates a new `NSAttributedString` initialized with the characters and attributes of the current attributed string plus the specified text color
///
/// - Parameter color: The color to apply to the text
/// - Returns: A new `NSAttributedString` initialized with characters and attributes of the current attributed string plus the text color
public func withTextColor(_ color: UIColor) -> NSAttributedString {
with(attribute: .foregroundColor, value: color)
}

/// Creates a new `NSAttributedString` initialized with the characters and attributes of the current attributed string plus the specified attribute
///
/// - Parameters:
/// - key: The attribute key to apply. This should be one of the keys defined in `NSAttributedString.Key`.
/// - value: The value associated with the attribute key. This can be any object compatible with the attribute.
/// - range: An optional `NSRange` specifying the range within the `NSAttributedString` to apply the attribute.
/// If `nil`, the attribute is applied to the entire `NSAttributedString`.
/// - Returns: A new `NSAttributedString` with the specified attribute applied.
public func with(attribute key: NSAttributedString.Key, value: Any, in range: NSRange? = nil) -> NSAttributedString {
with(attributes: [key: value], in: range)
}

/// Creates a new `NSAttributedString` initialized with the characters and attributes of the current attributed string plus the specified attributes
///
/// - Parameters:
/// - attributes: A dictionary of attributes to apply, where the keys are of type `NSAttributedString.Key` and the values
/// are objects compatible with the attributes (e.g., `UIFont`, `UIColor`).
/// - range: An optional `NSRange` specifying the range within the `NSAttributedString` to apply the attributes.
/// If `nil`, the attributes are applied to the entire `NSAttributedString`.
/// - Returns: A new `NSAttributedString` with the specified attributes applied.
public func with(attributes: [NSAttributedString.Key: Any], in range: NSRange? = nil) -> NSAttributedString {
let mutableString = NSMutableAttributedString(attributedString: self)
mutableString.addAttributes(attributes, range: range ?? string.nsRange)
return mutableString
}
}

/// Concatenates two `NSAttributedString` instances, returning a new `NSAttributedString`.
///
/// - Parameters:
/// - lhs: The left-hand side `NSAttributedString` to which the `rhs` `NSAttributedString` will be appended.
/// - rhs: The `NSAttributedString` to append to the `lhs` `NSAttributedString`.
/// - Returns: A new `NSAttributedString` that is the result of concatenating `lhs` and `rhs`.
public func + (lhs: NSAttributedString, rhs: NSAttributedString) -> NSAttributedString {
let mutable = NSMutableAttributedString(attributedString: lhs)
mutable.append(rhs)
return mutable
}

/// Concatenates an `NSAttributedString` with a `String`, returning a new `NSAttributedString`.
///
/// - Parameters:
/// - lhs: The left-hand side `NSAttributedString` to which the `String` will be appended.
/// - rhs: The `String` to append to the `lhs` `NSAttributedString`.
/// - Returns: A new `NSAttributedString` which is the result of concatenating `lhs` with `rhs`.
public func + (lhs: NSAttributedString, rhs: String) -> NSAttributedString {
lhs + NSAttributedString(string: rhs)
}

/// Concatenates a `String` with an `NSAttributedString`, returning a new `NSAttributedString`.
///
/// - Parameters:
/// - lhs: The `String` to prepend to the `rhs` `NSAttributedString`.
/// - rhs: The right-hand side `NSAttributedString` that will be appended to the `lhs` `String`.
/// - Returns: A new `NSAttributedString` which is the result of concatenating `lhs` with `rhs`.
public func + (lhs: String, rhs: NSAttributedString) -> NSAttributedString {
NSAttributedString(string: lhs) + rhs
}

private extension String {

var nsRange: NSRange {
NSRange(startIndex..., in: self)
}

}
2 changes: 0 additions & 2 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,6 @@ extension Pixel {

// MARK: Apple Ad Attribution
case appleAdAttribution
case appleAdAttributionNotAttributed

// MARK: Secure Vault
case secureVaultL1KeyMigration
Expand Down Expand Up @@ -1446,7 +1445,6 @@ extension Pixel.Event {

// MARK: - Apple Ad Attribution
case .appleAdAttribution: return "m_apple-ad-attribution"
case .appleAdAttributionNotAttributed: return "m_apple-ad-attribution_not-attributed"

// MARK: - User behavior
case .userBehaviorReloadTwiceWithin12Seconds: return "m_reload-twice-within-12-seconds"
Expand Down
2 changes: 0 additions & 2 deletions Core/UserDefaultsPropertyWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ public struct UserDefaultsWrapper<T> {
case addressBarPosition = "com.duckduckgo.ios.addressbarposition"
case showFullURLAddress = "com.duckduckgo.ios.showfullurladdress"

case webContainerId = "com.duckduckgo.ios.webcontainer.id"

case bookmarksLastGoodVersion = "com.duckduckgo.ios.bookmarksLastGoodVersion"
case bookmarksMigrationVersion = "com.duckduckgo.ios.bookmarksMigrationVersion"

Expand Down
36 changes: 19 additions & 17 deletions Core/WKWebViewConfigurationExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
//

import WebKit
import Persistence

extension WKWebViewConfiguration {

@MainActor
public static func persistent(idManager: DataStoreIdManager = .shared) -> WKWebViewConfiguration {
public static func persistent(idManager: DataStoreIdManaging = DataStoreIdManager.shared) -> WKWebViewConfiguration {
let config = configuration(persistsData: true)

// Only use a container if there's an id which will be allocated next time the fire button is used.
if #available(iOS 17, *), let containerId = idManager.id {
if #available(iOS 17, *), let containerId = idManager.currentId {
config.websiteDataStore = WKWebsiteDataStore(forIdentifier: containerId)
}
return config
Expand Down Expand Up @@ -58,32 +59,33 @@ extension WKWebViewConfiguration {

public protocol DataStoreIdManaging {

var id: UUID? { get }
var hasId: Bool { get }
func allocateNewContainerId()
var currentId: UUID? { get }

func invalidateCurrentIdAndAllocateNew()
}

public class DataStoreIdManager: DataStoreIdManaging {

public static let shared = DataStoreIdManager()
enum Constants: String {
case currentWebContainerId = "com.duckduckgo.ios.webcontainer.id"
}

@UserDefaultsWrapper(key: .webContainerId, defaultValue: nil)
private var containerId: String?
public static let shared = DataStoreIdManager()

public var id: UUID? {
if let containerId {
return UUID(uuidString: containerId)
}
return nil
private let store: KeyValueStoring
init(store: KeyValueStoring = UserDefaults.app) {
self.store = store
}

public var hasId: Bool {
return containerId != nil
public var currentId: UUID? {
guard let uuidString = store.object(forKey: Constants.currentWebContainerId.rawValue) as? String else {
return nil
}
return UUID(uuidString: uuidString)
}

public func allocateNewContainerId() {
self.containerId = UUID().uuidString
public func invalidateCurrentIdAndAllocateNew() {
store.set(UUID().uuidString, forKey: Constants.currentWebContainerId.rawValue)
}

}
27 changes: 17 additions & 10 deletions Core/WebCacheManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import os.log

extension WKWebsiteDataStore {

public static func current(dataStoreIdManager: DataStoreIdManager = .shared) -> WKWebsiteDataStore {
if #available(iOS 17, *), let id = dataStoreIdManager.id {
public static func current(dataStoreIdManager: DataStoreIdManaging = DataStoreIdManager.shared) -> WKWebsiteDataStore {
if #available(iOS 17, *), let id = dataStoreIdManager.currentId {
return WKWebsiteDataStore(forIdentifier: id)
} else {
return WKWebsiteDataStore.default()
Expand Down Expand Up @@ -82,17 +82,13 @@ public class WebCacheManager {
dataStoreIdManager: DataStoreIdManaging = DataStoreIdManager.shared) async {

var cookiesToUpdate = [HTTPCookie]()
if #available(iOS 17, *), dataStoreIdManager.hasId {
if #available(iOS 17, *) {
cookiesToUpdate += await containerBasedClearing(storeIdManager: dataStoreIdManager) ?? []
}

// Perform legacy clearing to migrate to new container
cookiesToUpdate += await legacyDataClearing() ?? []

if #available(iOS 17, *) {
dataStoreIdManager.allocateNewContainerId()
}

cookieStorage.updateCookies(cookiesToUpdate, keepingPreservedLogins: logins)
}

Expand All @@ -118,13 +114,24 @@ extension WebCacheManager {

@available(iOS 17, *)
private func containerBasedClearing(storeIdManager: DataStoreIdManaging) async -> [HTTPCookie]? {
guard let containerId = storeIdManager.id else { return [] }
guard let containerId = storeIdManager.currentId else {
storeIdManager.invalidateCurrentIdAndAllocateNew()
return []
}
storeIdManager.invalidateCurrentIdAndAllocateNew()

var dataStore: WKWebsiteDataStore? = WKWebsiteDataStore(forIdentifier: containerId)
let cookies = await dataStore?.httpCookieStore.allCookies()
dataStore = nil

let uuids = await WKWebsiteDataStore.allDataStoreIdentifiers
let previousLeftOversCount = max(0, uuids.count - 1) // -1 because there should be a current store
var uuids = await WKWebsiteDataStore.allDataStoreIdentifiers
if let newContainerID = storeIdManager.currentId,
let newIdIndex = uuids.firstIndex(of: newContainerID) {
assertionFailure("Attempted to cleanup current Data Store")
uuids.remove(at: newIdIndex)
}

let previousLeftOversCount = max(0, uuids.count - 1) // -1 because one store is expected to be cleared
for uuid in uuids {
try? await WKWebsiteDataStore.remove(forIdentifier: uuid)
}
Expand Down
Loading

0 comments on commit 66cb90c

Please sign in to comment.