diff --git a/Articles/Package.swift b/Articles/Package.swift index b62dad2c0..2c765979b 100644 --- a/Articles/Package.swift +++ b/Articles/Package.swift @@ -2,22 +2,26 @@ import PackageDescription let package = Package( - name: "Articles", + name: "Articles", platforms: [.macOS(.v14), .iOS(.v17)], - products: [ - .library( - name: "Articles", + products: [ + .library( + name: "Articles", type: .dynamic, - targets: ["Articles"]), - ], - dependencies: [ + targets: ["Articles"]), + ], + dependencies: [ .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMinor(from: "1.0.0")), - ], - targets: [ - .target( - name: "Articles", + ], + targets: [ + .target( + name: "Articles", dependencies: [ "RSCore" - ]), + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") + ] + ), ] ) diff --git a/Articles/Sources/Articles/Article.swift b/Articles/Sources/Articles/Article.swift index 275085eef..8581ba427 100644 --- a/Articles/Sources/Articles/Article.swift +++ b/Articles/Sources/Articles/Article.swift @@ -53,7 +53,7 @@ public struct Article: Hashable { } public static func calculatedArticleID(feedID: String, uniqueID: String) -> String { - return databaseIDWithString("\(feedID) \(uniqueID)") + return DatabaseIDCache.shared.databaseIDWithString("\(feedID) \(uniqueID)") } // MARK: - Hashable diff --git a/Articles/Sources/Articles/Author.swift b/Articles/Sources/Articles/Author.swift index 3bedce5bd..9e88cd065 100644 --- a/Articles/Sources/Articles/Author.swift +++ b/Articles/Sources/Articles/Author.swift @@ -33,7 +33,7 @@ public struct Author: Codable, Hashable { s += url ?? "" s += avatarURL ?? "" s += emailAddress ?? "" - self.authorID = databaseIDWithString(s) + self.authorID = DatabaseIDCache.shared.databaseIDWithString(s) } } diff --git a/Articles/Sources/Articles/DatabaseID.swift b/Articles/Sources/Articles/DatabaseID.swift index 1ad269cc3..dff70912c 100644 --- a/Articles/Sources/Articles/DatabaseID.swift +++ b/Articles/Sources/Articles/DatabaseID.swift @@ -9,23 +9,30 @@ import Foundation import RSCore -// MD5 works because: -// * It’s fast -// * Collisions aren’t going to happen with feed data +class DatabaseIDCache: @unchecked Sendable { -private var databaseIDCache = [String: String]() -private var databaseIDCacheLock = NSLock() -public func databaseIDWithString(_ s: String) -> String { - databaseIDCacheLock.lock() - defer { - databaseIDCacheLock.unlock() - } - - if let identifier = databaseIDCache[s] { + static let shared = DatabaseIDCache() + + private var databaseIDCache = [String: String]() + private let databaseIDCacheLock = NSLock() + + /// Generates — or retrieves from cache — a database-suitable ID based on a String. + func databaseIDWithString(_ s: String) -> String { + + databaseIDCacheLock.lock() + defer { + databaseIDCacheLock.unlock() + } + + if let identifier = databaseIDCache[s] { + return identifier + } + + // MD5 works because: + // * It’s fast + // * Collisions aren’t going to happen with feed data + let identifier = s.md5String + databaseIDCache[s] = identifier return identifier } - - let identifier = s.md5String - databaseIDCache[s] = identifier - return identifier }