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 {
|
public protocol CloudKitZone: AnyObject {
|
||||||
|
|
||||||
@MainActor static var qualityOfService: QualityOfService { get }
|
static var qualityOfService: QualityOfService { get }
|
||||||
|
|
||||||
var zoneID: CKRecordZone.ID { get }
|
var zoneID: CKRecordZone.ID { get }
|
||||||
|
|
||||||
@MainActor var log: OSLog { get }
|
var log: OSLog { get }
|
||||||
|
|
||||||
@MainActor var container: CKContainer? { get }
|
@MainActor var container: CKContainer? { get }
|
||||||
@MainActor var database: CKDatabase? { 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.
|
// 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.
|
// .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.
|
// .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)
|
#if os(macOS) || targetEnvironment(macCatalyst)
|
||||||
return .userInitiated
|
return .userInitiated
|
||||||
#else
|
#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
|
let delayTime = DispatchTime.now() + after
|
||||||
DispatchQueue.main.asyncAfter(deadline: delayTime, execute: {
|
DispatchQueue.main.asyncAfter(deadline: delayTime, execute: {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
@ -846,7 +846,7 @@ public protocol CloudKitZone: AnyObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Modify and delete the supplied CKRecords and CKRecord.IDs
|
/// 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 {
|
guard !(recordsToSave.isEmpty && recordIDsToDelete.isEmpty) else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@ -905,7 +905,7 @@ public protocol CloudKitZone: AnyObject {
|
|||||||
var recordToSaveChunks = recordsToSave.chunked(into: 200)
|
var recordToSaveChunks = recordsToSave.chunked(into: 200)
|
||||||
var recordIDsToDeleteChunks = recordIDsToDelete.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 {
|
if !recordToSaveChunks.isEmpty {
|
||||||
let records = recordToSaveChunks.removeFirst()
|
let records = recordToSaveChunks.removeFirst()
|
||||||
|
|
||||||
|
@ -95,52 +95,45 @@ public protocol CloudKitFeedInfoDelegate {
|
|||||||
try await delete(ckQuery: ckQuery)
|
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 {
|
guard !statusUpdates.isEmpty else {
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
return
|
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
|
Task { @MainActor in
|
||||||
let compressedModifyRecords = self.compressArticleRecords(modifyRecords)
|
|
||||||
self.modify(recordsToSave: compressedModifyRecords, recordIDsToDelete: deleteRecordIDs) { result in
|
var modifyRecords = [CKRecord]()
|
||||||
switch result {
|
var newRecords = [CKRecord]()
|
||||||
case .success:
|
var deleteRecordIDs = [CKRecord.ID]()
|
||||||
let compressedNewRecords = self.compressArticleRecords(newRecords)
|
|
||||||
self.saveIfNew(compressedNewRecords) { result in
|
for statusUpdate in statusUpdates {
|
||||||
switch result {
|
switch statusUpdate.record {
|
||||||
case .success:
|
case .all:
|
||||||
completion(.success(()))
|
modifyRecords.append(self.makeStatusRecord(statusUpdate))
|
||||||
case .failure(let error):
|
modifyRecords.append(self.makeArticleRecord(statusUpdate.article!))
|
||||||
completion(.failure(error))
|
case .new:
|
||||||
}
|
newRecords.append(self.makeStatusRecord(statusUpdate))
|
||||||
}
|
newRecords.append(self.makeArticleRecord(statusUpdate.article!))
|
||||||
case .failure(let error):
|
case .delete:
|
||||||
Task { @MainActor in
|
deleteRecordIDs.append(CKRecord.ID(recordName: self.statusID(statusUpdate.articleID), zoneID: zoneID))
|
||||||
self.handleModifyArticlesError(error, statusUpdates: statusUpdates, completion: completion)
|
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
|
return record
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated func compressArticleRecords(_ records: [CKRecord]) -> [CKRecord] {
|
func compressedArticleRecords(_ records: [CKRecord]) async -> [CKRecord] {
|
||||||
var result = [CKRecord]()
|
|
||||||
|
await withCheckedContinuation { continuation in
|
||||||
for record in records {
|
self._compressedArticleRecords(records) { records in
|
||||||
|
continuation.resume(returning: 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)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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…
x
Reference in New Issue
Block a user