diff --git a/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift b/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift index 095ddcdea..6947e9c6d 100644 --- a/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift +++ b/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift @@ -25,6 +25,8 @@ final class CloudKitArticlesZone: CloudKitZone { weak var database: CKDatabase? var delegate: CloudKitZoneDelegate? = nil + var compressionQueue = DispatchQueue(label: "Articles Zone Compression Queue") + struct CloudKitArticle { static let recordType = "Article" struct Fields { @@ -33,7 +35,9 @@ final class CloudKitArticlesZone: CloudKitZone { static let uniqueID = "uniqueID" static let title = "title" static let contentHTML = "contentHTML" + static let contentHTMLData = "contentHTMLData" static let contentText = "contentText" + static let contentTextData = "contentTextData" static let url = "url" static let externalURL = "externalURL" static let summary = "summary" @@ -96,7 +100,11 @@ final class CloudKitArticlesZone: CloudKitZone { records.append(makeArticleRecord(saveArticle)) } - save(records, completion: completion) + compressionQueue.async { + self.compressArticleRecords(records) { compressedRecords in + self.save(compressedRecords, completion: completion) + } + } } func deleteArticles(_ webFeedExternalID: String, completion: @escaping ((Result) -> Void)) { @@ -130,22 +138,29 @@ final class CloudKitArticlesZone: CloudKitZone { deleteRecordIDs.append(CKRecord.ID(recordName: self.articleID(statusUpdate.articleID), zoneID: zoneID)) } } - - self.modify(recordsToSave: modifyRecords, recordIDsToDelete: deleteRecordIDs) { result in - switch result { - case .success: - self.saveIfNew(newRecords) { result in - switch result { - case .success: - completion(.success(())) - case .failure(let error): - completion(.failure(error)) + + compressionQueue.async { + self.compressArticleRecords(modifyRecords) { compressedModifyRecords in + self.compressArticleRecords(newRecords) { compressedNewRecords in + self.modify(recordsToSave: compressedModifyRecords, recordIDsToDelete: deleteRecordIDs) { result in + switch result { + case .success: + self.saveIfNew(compressedNewRecords) { result in + switch result { + case .success: + completion(.success(())) + case .failure(let error): + completion(.failure(error)) + } + } + case .failure(let error): + self.handleModifyArticlesError(error, statusUpdates: statusUpdates, completion: completion) + } } } - case .failure(let error): - self.handleModifyArticlesError(error, statusUpdates: statusUpdates, completion: completion) } } + } } @@ -237,5 +252,37 @@ private extension CloudKitArticlesZone { return record } + func compressArticleRecords(_ records: [CKRecord], completion: ([CKRecord]) -> Void ) { + 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 + 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 + record[CloudKitArticle.Fields.contentText] = nil + } + } + + } else { + + result.append(record) + + } + } + + completion(result) + } } diff --git a/Account/Sources/Account/CloudKit/CloudKitArticlesZoneDelegate.swift b/Account/Sources/Account/CloudKit/CloudKitArticlesZoneDelegate.swift index c3801c1ec..2f20b4204 100644 --- a/Account/Sources/Account/CloudKit/CloudKitArticlesZoneDelegate.swift +++ b/Account/Sources/Account/CloudKit/CloudKitArticlesZoneDelegate.swift @@ -23,6 +23,7 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate { weak var account: Account? var database: SyncDatabase weak var articlesZone: CloudKitArticlesZone? + var compressionQueue = DispatchQueue(label: "Articles Zone Delegate Compression Queue") init(account: Account, database: SyncDatabase, articlesZone: CloudKitArticlesZone) { self.account = account @@ -134,7 +135,7 @@ private extension CloudKitArticlesZoneDelegate { } group.enter() - DispatchQueue.global(qos: .utility).async { + compressionQueue.async { let parsedItems = records.compactMap { self.makeParsedItem($0) } let webFeedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) } @@ -199,6 +200,20 @@ private extension CloudKitArticlesZoneDelegate { return nil } + var contentHTML = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentHTML] as? String + if let contentHTMLData = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentHTMLData] as? NSData { + if let decompressedContentHTMLData = try? contentHTMLData.decompressed(using: .lzfse) { + contentHTML = String(data: decompressedContentHTMLData as Data, encoding: .utf8) + } + } + + var contentText = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentText] as? String + if let contentTextData = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentTextData] as? NSData { + if let decompressedContentTextData = try? contentTextData.decompressed(using: .lzfse) { + contentText = String(data: decompressedContentTextData as Data, encoding: .utf8) + } + } + let parsedItem = ParsedItem(syncServiceID: nil, uniqueID: uniqueID, feedURL: webFeedURL, @@ -206,8 +221,8 @@ private extension CloudKitArticlesZoneDelegate { externalURL: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.externalURL] as? String, title: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.title] as? String, language: nil, - contentHTML: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentHTML] as? String, - contentText: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.contentText] as? String, + contentHTML: contentHTML, + contentText: contentText, summary: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.summary] as? String, imageURL: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.imageURL] as? String, bannerImageURL: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.imageURL] as? String,