mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2024-12-22 23:58:36 +01:00
Fix some concurrency warnings.
This commit is contained in:
parent
261c3136d2
commit
6d9d1762aa
@ -36,11 +36,11 @@ public typealias CloudKitRecordKey = (recordType: CKRecord.RecordType, recordID:
|
||||
|
||||
public protocol CloudKitZone: AnyObject {
|
||||
|
||||
@MainActor static var qualityOfService: QualityOfService { get }
|
||||
static var qualityOfService: QualityOfService { get }
|
||||
|
||||
var zoneID: CKRecordZone.ID { get }
|
||||
|
||||
@MainActor var log: OSLog { get }
|
||||
var log: OSLog { get }
|
||||
|
||||
@MainActor var container: CKContainer? { get }
|
||||
@MainActor var database: CKDatabase? { get }
|
||||
@ -64,7 +64,7 @@ public protocol CloudKitZone: AnyObject {
|
||||
// My observation has been that QoS is treated differently for CloudKit operations on macOS vs iOS.
|
||||
// .userInitiated is too aggressive on iOS and can lead the UI slowing down and appearing to block.
|
||||
// .default (or lower) on macOS will sometimes hang for extended periods of time and appear to hang.
|
||||
static var qualityOfService: QualityOfService {
|
||||
nonisolated static var qualityOfService: QualityOfService {
|
||||
#if os(macOS) || targetEnvironment(macCatalyst)
|
||||
return .userInitiated
|
||||
#else
|
||||
@ -121,7 +121,7 @@ public protocol CloudKitZone: AnyObject {
|
||||
}
|
||||
}
|
||||
|
||||
func retryIfPossible(after: Double, block: @escaping @MainActor () -> ()) {
|
||||
nonisolated func retryIfPossible(after: Double, block: @escaping @Sendable @MainActor () -> ()) {
|
||||
let delayTime = DispatchTime.now() + after
|
||||
DispatchQueue.main.asyncAfter(deadline: delayTime, execute: {
|
||||
Task { @MainActor in
|
||||
@ -846,7 +846,7 @@ public protocol CloudKitZone: AnyObject {
|
||||
}
|
||||
|
||||
/// Modify and delete the supplied CKRecords and CKRecord.IDs
|
||||
func modify(recordsToSave: [CKRecord], recordIDsToDelete: [CKRecord.ID], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
func modify(recordsToSave: [CKRecord], recordIDsToDelete: [CKRecord.ID], completion: @escaping @Sendable (Result<Void, Error>) -> Void) {
|
||||
|
||||
guard !(recordsToSave.isEmpty && recordIDsToDelete.isEmpty) else {
|
||||
DispatchQueue.main.async {
|
||||
@ -905,7 +905,7 @@ public protocol CloudKitZone: AnyObject {
|
||||
var recordToSaveChunks = recordsToSave.chunked(into: 200)
|
||||
var recordIDsToDeleteChunks = recordIDsToDelete.chunked(into: 200)
|
||||
|
||||
@MainActor func saveChunks(completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
func saveChunks(completion: @escaping @Sendable (Result<Void, Error>) -> Void) {
|
||||
if !recordToSaveChunks.isEmpty {
|
||||
let records = recordToSaveChunks.removeFirst()
|
||||
|
||||
|
@ -95,52 +95,45 @@ public protocol CloudKitFeedInfoDelegate {
|
||||
try await delete(ckQuery: ckQuery)
|
||||
}
|
||||
|
||||
@MainActor public func modifyArticles(_ statusUpdates: [CloudKitArticleStatusUpdate], completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
public func modifyArticles(_ statusUpdates: [CloudKitArticleStatusUpdate], completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
guard !statusUpdates.isEmpty else {
|
||||
completion(.success(()))
|
||||
return
|
||||
}
|
||||
|
||||
var modifyRecords = [CKRecord]()
|
||||
var newRecords = [CKRecord]()
|
||||
var deleteRecordIDs = [CKRecord.ID]()
|
||||
|
||||
for statusUpdate in statusUpdates {
|
||||
switch statusUpdate.record {
|
||||
case .all:
|
||||
modifyRecords.append(self.makeStatusRecord(statusUpdate))
|
||||
modifyRecords.append(self.makeArticleRecord(statusUpdate.article!))
|
||||
case .new:
|
||||
newRecords.append(self.makeStatusRecord(statusUpdate))
|
||||
newRecords.append(self.makeArticleRecord(statusUpdate.article!))
|
||||
case .delete:
|
||||
deleteRecordIDs.append(CKRecord.ID(recordName: self.statusID(statusUpdate.articleID), zoneID: zoneID))
|
||||
case .statusOnly:
|
||||
modifyRecords.append(self.makeStatusRecord(statusUpdate))
|
||||
deleteRecordIDs.append(CKRecord.ID(recordName: self.articleID(statusUpdate.articleID), zoneID: zoneID))
|
||||
}
|
||||
}
|
||||
|
||||
compressionQueue.async { [newRecords] in
|
||||
let compressedModifyRecords = self.compressArticleRecords(modifyRecords)
|
||||
self.modify(recordsToSave: compressedModifyRecords, recordIDsToDelete: deleteRecordIDs) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
let compressedNewRecords = self.compressArticleRecords(newRecords)
|
||||
self.saveIfNew(compressedNewRecords) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
Task { @MainActor in
|
||||
self.handleModifyArticlesError(error, statusUpdates: statusUpdates, completion: completion)
|
||||
}
|
||||
Task { @MainActor in
|
||||
|
||||
var modifyRecords = [CKRecord]()
|
||||
var newRecords = [CKRecord]()
|
||||
var deleteRecordIDs = [CKRecord.ID]()
|
||||
|
||||
for statusUpdate in statusUpdates {
|
||||
switch statusUpdate.record {
|
||||
case .all:
|
||||
modifyRecords.append(self.makeStatusRecord(statusUpdate))
|
||||
modifyRecords.append(self.makeArticleRecord(statusUpdate.article!))
|
||||
case .new:
|
||||
newRecords.append(self.makeStatusRecord(statusUpdate))
|
||||
newRecords.append(self.makeArticleRecord(statusUpdate.article!))
|
||||
case .delete:
|
||||
deleteRecordIDs.append(CKRecord.ID(recordName: self.statusID(statusUpdate.articleID), zoneID: zoneID))
|
||||
case .statusOnly:
|
||||
modifyRecords.append(self.makeStatusRecord(statusUpdate))
|
||||
deleteRecordIDs.append(CKRecord.ID(recordName: self.articleID(statusUpdate.articleID), zoneID: zoneID))
|
||||
}
|
||||
}
|
||||
|
||||
let compressedModifyRecords = await compressedArticleRecords(modifyRecords)
|
||||
|
||||
do {
|
||||
try await self.modify(recordsToSave: compressedModifyRecords, recordIDsToDelete: deleteRecordIDs)
|
||||
|
||||
let compressedNewRecords = await compressedArticleRecords(newRecords)
|
||||
try await self.saveIfNew(compressedNewRecords)
|
||||
|
||||
} catch {
|
||||
self.handleModifyArticlesError(error, statusUpdates: statusUpdates, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,34 +229,47 @@ private extension CloudKitArticlesZone {
|
||||
return record
|
||||
}
|
||||
|
||||
nonisolated func compressArticleRecords(_ records: [CKRecord]) -> [CKRecord] {
|
||||
var result = [CKRecord]()
|
||||
|
||||
for record in records {
|
||||
|
||||
if record.recordType == CloudKitArticle.recordType {
|
||||
|
||||
if let contentHTML = record[CloudKitArticle.Fields.contentHTML] as? String {
|
||||
let data = Data(contentHTML.utf8) as NSData
|
||||
if let compressedData = try? data.compressed(using: .lzfse) {
|
||||
record[CloudKitArticle.Fields.contentHTMLData] = compressedData as Data
|
||||
record[CloudKitArticle.Fields.contentHTML] = nil
|
||||
}
|
||||
}
|
||||
|
||||
if let contentText = record[CloudKitArticle.Fields.contentText] as? String {
|
||||
let data = Data(contentText.utf8) as NSData
|
||||
if let compressedData = try? data.compressed(using: .lzfse) {
|
||||
record[CloudKitArticle.Fields.contentTextData] = compressedData as Data
|
||||
record[CloudKitArticle.Fields.contentText] = nil
|
||||
}
|
||||
}
|
||||
|
||||
func compressedArticleRecords(_ records: [CKRecord]) async -> [CKRecord] {
|
||||
|
||||
await withCheckedContinuation { continuation in
|
||||
self._compressedArticleRecords(records) { records in
|
||||
continuation.resume(returning: records)
|
||||
}
|
||||
|
||||
result.append(record)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func _compressedArticleRecords(_ records: [CKRecord], completion: @escaping @Sendable ([CKRecord]) -> Void) {
|
||||
|
||||
compressionQueue.async {
|
||||
|
||||
var result = [CKRecord]()
|
||||
|
||||
for record in records {
|
||||
|
||||
if record.recordType == CloudKitArticle.recordType {
|
||||
|
||||
if let contentHTML = record[CloudKitArticle.Fields.contentHTML] as? String {
|
||||
let data = Data(contentHTML.utf8) as NSData
|
||||
if let compressedData = try? data.compressed(using: .lzfse) {
|
||||
record[CloudKitArticle.Fields.contentHTMLData] = compressedData as Data
|
||||
record[CloudKitArticle.Fields.contentHTML] = nil
|
||||
}
|
||||
}
|
||||
|
||||
if let contentText = record[CloudKitArticle.Fields.contentText] as? String {
|
||||
let data = Data(contentText.utf8) as NSData
|
||||
if let compressedData = try? data.compressed(using: .lzfse) {
|
||||
record[CloudKitArticle.Fields.contentTextData] = compressedData as Data
|
||||
record[CloudKitArticle.Fields.contentText] = nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
result.append(record)
|
||||
}
|
||||
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user